欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity3D-UGUI-Image制作异步加载场景进度条

程序员文章站 2022-05-30 08:17:02
...

先看一下我的成果

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;
}

写到这里,终于可以实现一个进度条的加载了,下面是我做进度条的步骤和代码,希望能给你们一些参考。挂在脚本实现操作如图所示:

Unity3D-UGUI-Image制作异步加载场景进度条

image组件progress,image Type:Filled , Fill Method : Horizontal

Unity3D-UGUI-Image制作异步加载场景进度条

代码如下:

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;
    }

}
相关标签: Image制作进度条