修改场景加载逻辑
我们修改SceneMgr
,让它能够抛出场景加载和卸载的事件。
我们每当打开一个新场景,配合当前场景配置文件来动态加载这个场景需要的各种脚本。我们还需要给出加载进度。
进度设置规则:加载场景——设为1,新场景每个需要加载的脚本都设为1,它们的总数为每个需要加载的场景的总进度。加载场景要设为1是因为不设值的话根本不知道场景的加载情况。
修改SceneName
修改Enum
的SceneName
1 2 3 4 5 6 7
| public enum SceneName { NULL, StartScene, Game, COUNT }
|
修改SceneMgr
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| public class SceneMgr : NormalSingleton<SceneMgr> { private readonly Dictionary<SceneName, System.Action<System.Action>> _loadedDic = new(); private readonly Dictionary<SceneName, System.Action> _unloadedDic = new(); private readonly Dictionary<SceneName, int> _initItemTotalNum = new(); public int CurInitNum { get; private set; } public int InitTotalNum => _initItemTotalNum[GameStateModel.Instance.TargetScene]; public SceneMgr() { SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; InitItemTotalNum(); } private void InitItemTotalNum() { for (SceneName i = SceneName.StartScene; i < SceneName.COUNT; i++) { _initItemTotalNum[i] = 1; } } public void LoadSceneAsync(SceneName name) { ResetData(); CoroutineMgr.Instance.ExecuteOnce(LoadAsync(name.ToString())); } private void OnSceneLoaded(Scene scene, LoadSceneMode loadSceneMode) { SceneName curScene = System.Enum.Parse<SceneName>(scene.name); if( _loadedDic.TryGetValue(curScene, out System.Action<System.Action> action) ) action?.Invoke(LoadCallback); } private void LoadCallback() { CurInitNum++; } private void OnSceneUnloaded(Scene scene) { SceneName curScene = System.Enum.Parse<SceneName>(scene.name); if (_unloadedDic.TryGetValue(curScene, out System.Action action)) action?.Invoke(); } public void AddSceneLoaded(SceneName name, System.Action<System.Action> action) { _initItemTotalNum[name] += 1; if (_loadedDic.ContainsKey(name)) _loadedDic[name] += action; else _loadedDic.Add(name, action); } public void AddSceneUnLoaded(SceneName name, System.Action action) { if (_unloadedDic.ContainsKey(name)) _unloadedDic[name] += action; else _unloadedDic.Add(name, action);
} public float Process() { float ratio = CurInitNum / (float) InitTotalNum; if (_async != null && _async.progress >= 0.9f) { SceneActivation(); } return ratio; } public void SceneActivation() { CurInitNum++; if(_async == null) return; _async.allowSceneActivation = true; GameStateModel.Instance.CurrentScene = GameStateModel.Instance.TargetScene; _async = null; } public bool IsScene(SceneName name) { return GameStateModel.Instance.CurrentScene == name; } private void ResetData() { CurInitNum = 0; InitItemTotalNum(); } }
|
注意AddSceneLoaded
和AddSceneUnLoaded
的写法,我们的Dictionary的Value值为System.Action
,必须保证有头一个Action
再进行多播。
注意这里的LoadCallback()
方法,它作为_loadedDic
的Vaule,也就是Action<Action>
的参数,是可以被多次调用的,所以可以执行 CurInitNum++;
等下看到具体执行的地方,就明白其中的逻辑了。
修改Process
方法,使用自己计算的ratio作为进度。
SceneConfig
在Config文件夹中新建SceneConfig
,在其中指定每个Scene需要初始化的脚本
1 2 3 4 5 6 7 8 9 10 11
| public class SceneConfig : NormalSingleton<SceneConfig>, IInit { public void Init() { AddLoaded(); } private void AddLoaded() { SceneMgr.Instance.AddSceneLoaded(SceneName.Game, callback => PoolMgr.Instance.Init(callback)); } }
|
添加到LifeCycleAddConfig
1 2 3 4 5
| private void Add() { Objects.Add(SceneConfig.Instance); }
|
调用链
LifeCycleMgr
将SceneConfig
初始化,SceneConfig
初始化时把PoolMgr
的初始化方法注册进SceneMgr
的SceneLoaded
回调中。
SceneMgr
加载完场景后,根据场景名称的不同,执行不同场景注册进来的脚本初始化方法。
- 在
PoolMgr.Init(Action callback)
执行时,会调用async PoolMgr.Init()
这个异步方法,异步方法执行到最后,会将PoolMgr.Init(Action callback)
传进来的callback执行,而这个callback正是SceneMgr.LoadCallback
方法,SceneMgr
记录的CurInitNum
加1
- 在Loading界面,根据
SceneMgr
提供的InitTotalNum
和CurInitNum
来显示加载进度。
Loading界面修改
Loading
界面在一开始的设计中,进入Game
场景后就直接销毁了,但由于我们添加了新的加载进度数据,这要求Loading
界面不能马上销毁。
修改GameRoot
由于Loading界面需要长期存在,我们修改GameRoot
,让它不会被销毁,并且让GameRoot
挂载在Canvas上,删除掉之前的GameRoot物体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| using UnityEngine;
public class GameRoot : MonoBehaviour { private void Start() { if (FindObjectsOfType<GameRoot>().Length > 1) { Destroy(gameObject); return; }
GameStateModel.Instance.CurrentScene = SceneName.StartScene; IInit lifeCycle = LifeCycleMgr.Instance; lifeCycle.Init(); UIMgr.Instance.Show(Pathes.START_VIEW);
DontDestroyOnLoad(gameObject); } }
|
LoadingController
修改LoadingController
的UpdateFun
和Hide
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public override void UpdateFun() { base.UpdateFun(); if(SceneMgr.Instance.Process() == 1f) { UIMgr.Instance.Show(Pathes.GAMEUI_VIEW); } } public override void Hide() { base.Hide(); if(GameStateModel.Instance.TargetScene != SceneName.NULL ) { GameStateModel.Instance.TargetScene = SceneName.NULL; } LifeCycleMgr.Instance.Remove(LifeName.UPDATE, this);
}
|
这里指定了跳转到Game UI,Game UI在下一节完成