赞
踩
在start函数中执行IEnumerator协程函数.
在协程函数中异步加载场景 AsyncOperation op=SceneManager.LoadSceneAsync(),并设置op.allowSceneActivation = false使场景加载完毕后不立即显示,此时op.progress最多加载到90%,等op.allowSceneActivation = true后op.progress再加载后10%.
问题是:op=SceneManager.LoadSceneAsync()这句会导致Loaing界面的卡顿(打印消息和特效粒子都卡住很久才显示).
求助:我希望能避免这个卡顿(毕竟异步和协程就是为了减少卡顿的,但它还是卡),所以我怀疑可能是我没用好协程和异步,导致异步没有生效.有没有知道原理的大神,求大神解说以下,或者推荐一下比较有用的文章.(还是说异步只能把卡顿转移到某一个画面,要想完全不显示卡顿,只能用静态界面.)
资料:
1.我查看了一些文章,有些说异步LoadSceneAsync()并非真正的后台载入,它在每一帧载入一些游戏资源,并给出一个progress值,所以在载入的时候还是会造成游戏卡顿.基于此原理,我觉得我的异步可能并没有生效,因为它在一开始就卡顿很久.(而后面进度条显示由于写了延时器所以都比较流畅.)
2.资源会在start函数开始时加载,如果没有分包,它的加载流量是虎头蛇尾的形式,它会在第一帧就能加载多少就加载多少,多到导致卡顿.(这就是我这个问题的真正原因).
3.关于协程IEnumerator,并不是异步,即使使用了协程,其实还是单线程(因为Unity基本上都是单线程,若出现多线程会导致Unity很多原生功能出现线程不安全的问题),只不过把中间帧抽出来做另一个任务,所以实质是还是同步.
4.我还发现在异步加载时,Unity编辑器暂停也不管用,后台会继续执行异步内容(Loaing界面是暂停的),异步加载完后还会自动转场景.(异步一旦开始是不是就无法停止了)
5.降低异步加载优先级,使Application.backgroundLoadingPriority = ThreadPriority.Low,尽量不影响帧率.异步加载包括:资源.LoadAsync/AssetBundle.LoadAssetAsync/AssetBundle.LoadAllAssetAsync, 场景sceneManager.LoadSceneAsync.
6.Unity官方专家给的解释:
在 Unity 中实例化操作(Instantiate),以及资源的初始化(如 Texture.AwakeFromLoad/Shader.Parse)等都不是异步的,会导致主线程的卡顿。Application.LoadLevelAsync 同样会在 Load 操作完成后自动进行资源的初始化和物件的实例化。在这个过程里就无法保持当前的帧率了。
因此,能否提供一下具体的 profiler 截图,看加载中卡在了什么地方,如果第二帧就加载完,很可能是在第一帧里就已经完成了 load 操作,而第二帧里是在做实例化等卡住主线程的操作。
我现在没办法解决这个问题,所以一开始就把进度先从0随机显示某一个位置,并结合几秒延迟创建异步加载场景,播放一会儿粒子效果,假装是加载过程中随机的卡顿,而不是创建异步的卡顿.(但我明白这治标不治本)
顺便说一下我是怎么测出"op=SceneManager.LoadSceneAsync()"这句会导致卡顿的,因为我把它放到哪里哪里就卡.
- using UnityEngine;
- using System.Collections;
- using UnityEngine.UI;
- using UnityEngine.SceneManagement;
- public class Loading : MonoBehaviour
- {
- public Slider m_Slider;
- public Text m_Text;
- private ParticleSystem particle;
- private AsyncOperation op =null;
- private int startShowProgess=5;
- void Awake()
- {
- particle=GameObject.Find("Particle").GetComponent<ParticleSystem>();
- particle.Play();
- }
- void Start()
- {
- SetLoadingPercentage(startShowProgess);
- StartCoroutine(loadScene()); //该协程会导致加载卡顿
- }
- string GetLoadName()
- {
- string loadName="";
- GameObject LevelRecordCreater=GameObject.Find("LevelRecordCreater");
- if (LevelRecordCreater)
- {
-
- int nowlevel=LevelRecordCreater.GetComponent<levelUpdata>().level;
- if (nowlevel == 1)
- {
- loadName = "level1";
- }
- if (nowlevel == 2)
- {
- loadName = "level2";
- }
- if (nowlevel == 3)
- {
- loadName = "level3";
- }
- }
- return loadName;
- }
- IEnumerator loadScene()
- {
- yield return new WaitForEndOfFrame(); //加上这么一句就可以先显示加载画面然后再进行加载
- int displayProgress = startShowProgess;
- int toProgress = (int)(Random.value*88); //假随机卡在某个数
- //假进度前88%
- while (displayProgress < toProgress)
- {
- SetLoadingPercentage(++displayProgress);
- yield return new WaitForEndOfFrame();
- }
- yield return new WaitForSeconds(3); //把卡顿推后,假装是进度中的,防止一开始玩家就跑了<过半理论>
- op = SceneManager.LoadSceneAsync(GetLoadName()); //这个创建还是会导致卡顿
- op.allowSceneActivation = false;
- //真进度后88%~90%
- while (op.progress < 0.9f) //op.allowSceneActivation = true至前,op.progress最大值为0.9
- {
- toProgress = (int)op.progress * 100;
- while (displayProgress < toProgress)
- {
- SetLoadingPercentage(++displayProgress);
- yield return new WaitForEndOfFrame();
- }
- }
- //假进度后90%~100%
- toProgress = 100;
- while (displayProgress < toProgress)
- {
- SetLoadingPercentage(++displayProgress);
- yield return new WaitForEndOfFrame();
- }
- op.allowSceneActivation = true;
- }
- void SetLoadingPercentage(int sliderProgress)
- {
- m_Slider.value = sliderProgress * 0.01f;
- m_Text.text = "加载资源 "+sliderProgress.ToString() + "%";
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。