美文网首页unity3D技术分享
1、协程和回调的组合应用——进度条

1、协程和回调的组合应用——进度条

作者: GameObjectLgy | 来源:发表于2020-07-04 14:50 被阅读0次
    • 1、异步加载场景,显示滚动条
    • 2、战斗计算,传递数据出去
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Events;
    using UnityEngine.SceneManagement;
    using UnityEngine.UI;
    
    public class TestExample : MonoBehaviour
    {
        private float progress;
        public Text progressText;
        public Slider slider;
    
        void Start()
        {
            //LoadAsync<GameObject>("MyCube", LoadComplete1, Loading);
            LoadSceneAsyn("Scenes/MountainGroup", LoadComplete2, Loading);
            //StartCoroutine(CalculateCombatValue(GetResult));
        }
    
        
        void Update()
        {
            
        }
    
        private void LoadComplete1(GameObject obj)
        {
            Debug.Log("加载的资源名称:" + obj.name);
        }
    
        private void LoadComplete2()
        {
            Debug.Log("场景加载完成!" );
        }
    
        private void Loading(int p)
        {
            progressText.text = p.ToString();
            float temp = p / 100f;
            Debug.Log("temp = " + temp);
            slider.value = temp;
        }
    
        private void GetResult(int health)
        {
            Debug.Log("结果:" + health);
        }
    
    
        //------------------------场景加载-------------------------------------
        //说明:这里故意第一个用UnityAction,第二个用c#带Action,这两者是参数返回值方面和效率
        //有一些差别的,后者效率会更高,尽量使用Action
    
        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="name">资源名字</param>
        /// <param name="callback">加载完的回调,一个返回参数</param>
        /// <param name="callback2">加载中回调,一个返回参数,用来显示进度条</param>
        public void LoadAsync<T>(string name, UnityAction<T> callback, Action<float> callback2) where T : UnityEngine.Object
        {
            //开启异步加载的协程
            StartCoroutine(ReallyLoadAsync<T>(name, callback, callback2));        
        }
    
        private IEnumerator ReallyLoadAsync<T>(string name, UnityAction<T> callback, Action<float> callback2) where T : UnityEngine.Object
        {
            ResourceRequest r = Resources.LoadAsync<T>(name);
            if (!r.isDone)
            {
                //这里本来是应该调用r.progress到0.9的时候会自动停止更新r.progress,导致进度条到90就停了
                //需要r.allowSceneActivation = true;才能等于100.
                if (r.progress < 0.9f)
                {
                    callback2(r.progress);
                    yield return r;
                }
                callback2(1);
                //yield return new WaitForEndOfFrame();
                r.allowSceneActivation = true;
            }       
            yield return r;
    
            if (r.asset is GameObject)
            {
                //实例化一下再传给方法
                callback(GameObject.Instantiate(r.asset) as T);
            }
            else
            {
                Debug.Log(222);
                //直接传给方法
                callback(r.asset as T);
            }
        }
    
    
        //------------------------场景加载-------------------------------------
        //引入displayProgress和toProgress,目的是为了让进度条的更加平滑
        public void LoadSceneAsyn(string name, UnityAction func, Action<int> callback2)
        {       
            StartCoroutine(ReallyLoadSceneAsyn(name, func, callback2));
        }
        private IEnumerator ReallyLoadSceneAsyn(string name, UnityAction func, Action<int> callback2)
        {   
            int displayProgress = 0;
            int toProgress = 0;
            AsyncOperation ao = SceneManager.LoadSceneAsync(name);
            ao.allowSceneActivation = false;
            while (ao.progress < 0.9f)
            {
                //toProgress = (int)ao.progress * 100;           
                toProgress = 90;           
                while(displayProgress <= toProgress)
                {
                    Debug.Log("------------------toProgress = " + toProgress + "-------------displayProgress = " + displayProgress);
                    ++displayProgress;
                    callback2(displayProgress);
                    //这里故意延时0.01秒是为了放慢加载速度,更好地观察代码效果,实际应用不能这么写
                    yield return new WaitForSeconds(0.02f);
    
                    //使用下面的任一句都可以
                    //yield return new WaitForEndOfFrame();
                    //yield return ao.progress;
                }
            }
    
            toProgress = 100;
            while(displayProgress <toProgress)
            {
                ++displayProgress;
                callback2(displayProgress);
    
                yield return new WaitForSeconds(0.02f);
                //yield return new WaitForEndOfFrame();
                //yield return ao;
            }
    
            //加载完成后执行func
            func();
            ao.allowSceneActivation = true;
        }
    
    
        //------------------------应用2:战斗计算-------------------------------------
        //目前有一个应用场景是这样的,游戏中有一项费时的数据计算,
        //如果直接执行计算,再执行后面的逻辑就会造成游戏卡顿,如何解决?
        //可以把计算放在一个协程中去计算,
        //协程没有返回值,同时协程还不能传递引用,
        //可是现在需要把计算中的中间值以及最终结果(如中间值是角色血条的变化)传递出去,怎么办呢?
        //不能传递引用,可以传递数组地址(取巧),或者采用全局变量的方式。
        //这两种办法是可以解决,取巧看起来不直接,全局增加耦合,有没有更好的办法呢?
        //这时候就可以采用回调(也可以叫委托)的方式了,通过带参数的回调,把协程里的数值传递出去。
        public IEnumerator CalculateCombatValue(Action<int> callback)
        {
            int result;
            //模拟计算耗时的计算过程
            yield return new WaitForSeconds(3f);
            result = 10;
            //3秒后得到计算结果
            callback(result);
            yield return null;
        }
    }
    

    相关文章

      网友评论

        本文标题:1、协程和回调的组合应用——进度条

        本文链接:https://www.haomeiwen.com/subject/kxeuqktx.html