UFL8——场景切换模块

前置知识点

  1. 场景切换 相关知识点 (Unity四部曲之Unity入门)
  2. 协同程序 相关知识点 (Unity四部曲之Unity基础)
  3. 委托 相关知识点(C#四部曲之C#进阶)

为什么要制作场景切换模块

在游戏开发中很多时候可能需要进行场景切换,只要存在场景切换,我们往往需要在 切换场景时切换场景结束后 进行一些操作

比如:

  1. 切换场景中
    更新Loading界面进度条
  2. 切换场景后
    隐藏Loading界面,动态创建场景中关卡信息:如角色、怪物、场景物件等等

因此为了避免有太多关于切换场景的冗余代码,并且为了使用更方便
我们可以将切换场景的逻辑统一整理在一个场景切换管理器中

实现场景切换模块的主要思路

  1. 制作 SceneMgr​ 单例模式管理器

    1
    2
    3
    4
    public class SceneMgr : BaseManager<SceneMgr>
    {
    private SceneMgr() { }
    }
  2. 实现同步加载场景 的公共方法

    1
    2
    3
    4
    5
    6
    7
    8
    //同步切换场景的方法
    public void LoadScene(string name, UnityAction callBack = null)
    {
    //切换场景
    SceneManager.LoadScene(name);
    //调用回调
    callBack?.Invoke();
    }
  3. 实现异步加载场景 的公共方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //异步切换场景的方法
    public void LoadSceneAsync(string name, UnityAction callBack = null)
    {
    MonoManager.Instance.StartCoroutine(ReallyLoadSceneAsync(name, callBack));
    }

    private IEnumerator ReallyLoadSceneAsync(string sceneName, UnityAction callBack)
    {
    AsyncOperation ao = SceneManager.LoadSceneAsync(sceneName);
    yield return ao;
    callBack?.Invoke();
    }
  4. 实现外部获取异步加载场景进度

    在异步加载场景的时候,每帧通过事件中心来将加载进度发送出去(需要为此声明一个事件名)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /// <summary>
    /// 事件类型枚举
    /// </summary>
    public enum E_EventType
    {
    /// <summary>
    /// 场景切换时,进度的变化的获取
    /// </summary>
    E_SceneLoadChange,
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //异步切换场景的方法
    public void LoadSceneAsync(string name, UnityAction callBack = null)
    {
    MonoManager.Instance.StartCoroutine(ReallyLoadSceneAsync(name, callBack));
    }

    private IEnumerator ReallyLoadSceneAsync(string sceneName, UnityAction callBack)
    {
    AsyncOperation ao = SceneManager.LoadSceneAsync(sceneName);
    //不停在协同程序中每帧检测是否加载结束,如果加载结束就不会进入这个每帧循环内
    while (!ao.isDone)
    {
    //可以在这里利用事件执行,每一帧将进度发送给想要得到的地方
    EventCenter.Instance.EventTrigger(E_EventType.E_SceneLoadChange, ao.progress);
    yield return 0;
    }
    //在场景加载的最后一帧发送1出去,避免加载结束导致1没有被同步发送出去
    EventCenter.Instance.EventTrigger(E_EventType.E_SceneLoadChange, 1);
    callBack?.Invoke();
    }

使用示例

假设点击了UI上的Start Game按钮时,场景就会切换,同时控制台会输出加载进度(面板不会隐藏)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class BeginPanel : BasePanel
{
private void Start()
{
EventCenter.Instance.AddEventListener<float>(E_EventType.E_SceneLoadChange, ChangeScene);
}

protected override void ClickButton(string btnName)
{
switch (btnName)
{
case "btnBegin":
print("开始按钮被点击");
SceneMgr.Instance.LoadSceneAsync("Test", () =>
{
print("场景加载结束");
});
break;
case "btnSetting":
print("设置按钮被点击");
break;
case "btnQuit":
print("退出按钮被点击");
break;
}
}

private void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<float>(E_EventType.E_SceneLoadChange, ChangeScene);
}

private void ChangeScene(float value)
{
print("切换场景的进度:" + value);
}
}

输出:image

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

/// <summary>
/// 场景切换管理器 主要用于切换场景
/// </summary>
public class SceneMgr : BaseManager<SceneMgr>
{
private SceneMgr() { }

//同步切换场景的方法
public void LoadScene(string name, UnityAction callBack = null)
{
//切换场景
SceneManager.LoadScene(name);
//调用回调
callBack?.Invoke();
}

//异步切换场景的方法
public void LoadSceneAsync(string name, UnityAction callBack = null)
{
MonoManager.Instance.StartCoroutine(ReallyLoadSceneAsync(name, callBack));
}

private IEnumerator ReallyLoadSceneAsync(string sceneName, UnityAction callBack)
{
AsyncOperation ao = SceneManager.LoadSceneAsync(sceneName);
//不停在协同程序中每帧检测是否加载结束,如果加载结束就不会进入这个每帧循环内
while (!ao.isDone)
{
//可以在这里利用事件执行,每一帧将进度发送给想要得到的地方
EventCenter.Instance.EventTrigger(E_EventType.E_SceneLoadChange, ao.progress);
yield return 0;
}
//在场景加载的最后一帧发送1出去,避免加载结束导致1没有被同步发送出去
EventCenter.Instance.EventTrigger(E_EventType.E_SceneLoadChange, 1);
callBack?.Invoke();
}
}