修改场景加载逻辑

我们修改SceneMgr,让它能够抛出场景加载和卸载的事件。

我们每当打开一个新场景,配合当前场景配置文件来动态加载这个场景需要的各种脚本。我们还需要给出加载进度。

进度设置规则:加载场景——设为1,新场景每个需要加载的脚本都设为1,它们的总数为每个需要加载的场景的总进度。加载场景要设为1是因为不设值的话根本不知道场景的加载情况。

修改SceneName

修改EnumSceneName

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()//将所有需要加载的场景初始加载值设为1
{
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;//把逻辑从LoadingController转移过来
_async = null;
}
public bool IsScene(SceneName name)
{
return GameStateModel.Instance.CurrentScene == name;
}
private void ResetData()
{
CurInitNum = 0;
InitItemTotalNum();
}
//...
}

注意AddSceneLoadedAddSceneUnLoaded的写法,我们的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);
}

调用链

  1. LifeCycleMgrSceneConfig初始化,SceneConfig初始化时把PoolMgr的初始化方法注册进SceneMgrSceneLoaded回调中。
  2. SceneMgr加载完场景后,根据场景名称的不同,执行不同场景注册进来的脚本初始化方法。
  3. PoolMgr.Init(Action callback)执行时,会调用async PoolMgr.Init()这个异步方法,异步方法执行到最后,会将PoolMgr.Init(Action callback)传进来的callback执行,而这个callback正是SceneMgr.LoadCallback方法,SceneMgr记录的CurInitNum加1
  4. 在Loading界面,根据SceneMgr提供的InitTotalNumCurInitNum来显示加载进度。

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

修改LoadingControllerUpdateFunHide方法。

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);//跳转到Game UI
}
}
public override void Hide()
{
base.Hide();
if(GameStateModel.Instance.TargetScene != SceneName.NULL )
{//删除掉GameStateModel.CurScene的赋值逻辑。
GameStateModel.Instance.TargetScene = SceneName.NULL;
}
LifeCycleMgr.Instance.Remove(LifeName.UPDATE, this);

}

这里指定了跳转到Game UI,Game UI在下一节完成