Unity3D-UGUI-Image制作异步加载场景进度条
先看一下我的成果
问题
我们通常在玩游戏的时候,会看到进度条加载,为什么要这样做那?
仔细分析一下我们就会明白,其实是主场景包含的资源较多,直接导致加载场景的时间较长,影响了玩家的体验。对于这样的情况,是很容易引起玩家反感的,为了避免这种情形的发生,添加一个加载Loading场景,然后再通过Loading场景来加载主场景,是势在必行的。因为Loading场景包含的资源较少,所以加载速度快。在加载主场景的时候一般会在Loading界面中显示一个进度条来告知玩家当前加载的进度。在Unity中可以通过调用Application.LoadLevelAsync函数来异步加载游戏场景,通过查询AsyncOperation.progress的值来得到场景加载的进度。
//单击加载按钮
public void LoadButton() {
//通过协同加载程序
StartCoroutine(StartLoading(1));
}
//定义协同
private IEnumerator StartLoading(int scene) {
//异步加载场景
AsyncOperation op = Application.LoadLevelAsync(scene);
//是否操作完成
while(!op.isDone) {
SetLoadingPercentage(op.progress * 100);
yield return new WaitForEndOfFrame();
}
}
//设置显示进度
private void SetLoadingPercentage(int DisplayProgress)
{
progressImg.fillAmount = DisplayProgress * 0.01f;
proText.text = DisplayProgress.ToString() + "%";
}
到这里是不是就已经可以实现了那,我只能说基本功能都实现,但是效果不会是理想的。
因为,上面的代码的结果是,进度条并没有连续的显示加载的进度,数字也不是连续出现的,在没有显示到100%就情况下,就直接切换场景了。为什么会是这样那?通过查找资料原因,我们可以发现,其原因在于Application.LoadLevelAsync并不是真正的后台加载,它在每一帧加载一些游戏资源,并给出一个progress值,所以在加载的时候还是会造成游戏卡顿,AsyncOperation.progress的值也不够精确。当场景加载完毕后就直接自动切换场景了,上述代码中的while循环体内的代码是不会被调用的,这就直接导致进度条不会显示100%。
那么怎么来解决这个问题那?
其实这个问题可以用Unity提供的手动切换场景的方法来解决,把AsyncOperation.allowSceneActivation设为false就可以禁止Unity加载完毕后自动切换场景,修改后的代码如下:
private IEnumerator StartLoading(int scene) {
AsyncOperation op = Application.LoadLevelAsync(scene);
op.allowSceneActivation = false;
while(!op.isDone) {
SetLoadingPercentage(op.progress * 100);
yield return new WaitForEndOfFrame();
}
op.allowSceneActivation = true;
}
到这里是不是就可以实现想要的结果了。
遗憾的是,执行的结果是进度条最后会一直停留在90%上,场景不会切换。通过打印log发现AsyncOperation.isDone一直为false,AsyncOperation.progress的值增加到0.9后就保持不变了,也就是说场景永远不会被加载完毕。为了解决这个问题,可以当AsyncOperation.progress到达0.9后,就直接把进度条的数值改为为100%,然后设置AsyncOperation.allowSceneActivationAsyncOperation.allowSceneActivation为ture,让Unity继续加载未完成的场景。修改后的代码如下:
private IEnumerator StartLoading(int scene) {
AsyncOperation op = Application.LoadLevelAsync(scene);
op.allowSceneActivation = false;
while(op.progress < 0.9f) {
SetLoadingPercentage(op.progress * 100);
yield return new WaitForEndOfFrame();
}
SetLoadingPercentage(100);
yield return new WaitForEndOfFrame();
op.allowSceneActivation = true;
}
运行上面的代码,我们就会发现,虽然我们解决了进度条到达不了100%的问题,但是进度条进度的显示数字并不连续,和自己理想的状态还是有些不一致,所以看上去不够自然和美观。
为了看上去像是在连续加载,可以每一次更新进度条的时候插入过渡数值。这里我采用的策略是当获得AsyncOperation.progress的值后,不立即更新进度条的数值,而是每一帧在原有的数值上加1,这样就会产生数字连续的动画效果了。
private IEnumerator StartLoading(int scene) {
int displayProgress = 0;
int toProgress = 0;
AsyncOperation op = Application.LoadLevelAsync(scene);
op.allowSceneActivation = false;
while(op.progress < 0.9f) {
toProgress = (int)op.progress * 100;
while(displayProgress < toProgress) {
++displayProgress;
SetLoadingPercentage(displayProgress);
yield return new WaitForEndOfFrame();
}
}
toProgress = 100;
while(displayProgress < toProgress){
++displayProgress;
SetLoadingPercentage(displayProgress);
yield return new WaitForEndOfFrame();
}
op.allowSceneActivation = true;
}
写到这里,终于可以实现一个进度条的加载了,下面是我做进度条的步骤和代码,希望能给你们一些参考。挂在脚本实现操作如图所示:
image组件progress,image Type:Filled , Fill Method : Horizontal
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class LoginManage : MonoBehaviour {
public static LoginManage instance;
//加载界面
public GameObject loadPanel;
//滑动进度条
public Image progressImg;
//进度显示百分比
public Text proText;
// Use this for initialization
void Start () {
instance = this;
loadPanel.SetActive(false);//一开始禁用加载层。
}
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.Escape))
{
ExitGamePressed();
}
}
/// <summary>
/// 登录按钮点击事件
/// </summary>
public void NewGamePressed()
{
StartCoroutine(StartLoading("Main"));//调用加载场景的协程
loadPanel.SetActive(true);//启用加载界面
}
/// <summary>
/// EXIT按钮点击事件
/// </summary>
/// <returns></returns>
public void ExitGamePressed()
{
//退出整个游戏
Application.Quit();
}
private void SetLoadingPercentage(int DisplayProgress) //设置显示进度
{
progressImg.fillAmount = DisplayProgress * 0.01f;
proText.text = DisplayProgress.ToString() + "%";
}
/// <summary>
/// 加载场景协程
/// </summary>
private IEnumerator StartLoading(string sceneName)
{
int displayProgress = 0;
int toProgress = 0;
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName); //异步对象
op.allowSceneActivation = false;
while (op.progress < 0.9f)
{
toProgress = (int)op.progress * 100;
while (displayProgress < toProgress)
{
++displayProgress;
SetLoadingPercentage(displayProgress);
yield return new WaitForEndOfFrame();
}
}
toProgress = 100;
while (displayProgress < toProgress)
{
++displayProgress;
SetLoadingPercentage(displayProgress);
yield return new WaitForEndOfFrame();
}
op.allowSceneActivation = true;
}
}