补充关卡加载部分的逻辑,在这里的逻辑设计中,每次加载Scene都需要主动打开Loading界面。由Loading界面驱动场景加载逻辑。

Loading界面

Loading界面的Prefab包括一个背景和一个Slider和一个Progress(Text)。我们使用Slider作为进度条,因为其中的Fill图片可以使用Sliced模式。这样我们可以使用九宫格切割过(一般是个圆形图片,只切两边)的更好看的图片当作加载进度条了。

Pathes中的Prefab路径

1
public const string LOADING_VIEW = PREFAB_FOLDER + "LoadingView";

添加Key

DataKeys里面添加存档中已经通过的关卡部分的Key

1
public const string LEVEL_PASSED = "levelPassed";

SceneMgr

Enums中添加Scene名的枚举

1
2
3
4
5
6
public enum SceneName
{
NULL,
StartScene,
Game
}

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
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneMgr : NormalSingleton<SceneMgr>
{
private AsyncOperation _async;
public void LoadSceneAsync(SceneName name)
{
CoroutineMgr.Instance.ExecuteOnce(LoadAsync(name.ToString()));
}
private IEnumerator LoadAsync(string name)
{
_async = SceneManager.LoadSceneAsync(name);
_async.allowSceneActivation = false;
yield return _async;
}
public float Process()
{
if(_async == null)
return 0f;
if(_async.progress >= 0.9f)
return 1f;
else
return _async.progress;
}
public void SceneActivation()
{
if(_async == null) return;
_async.allowSceneActivation = true;
_async = null;
}
}

LifeCycleMgr

当我们的脚本有需要Unity提供的生命周期的地方,就需要集中在LifeCycleMgr当中管理。

Unity的生命周期函数在底层是基于反射进行调用的。所以我们要集中处理。在LifeCycleMgr中添加AddRemoveRemoveAll方法,用于在后期添加生命周期逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LifeCycleMgr : LazyMonoSing<LifeCycleMgr>, IInit
{
//...
public void Add(LifeName lifeName, object obj)
{
LifeCycleConfig.LifeCycles[lifeName].Add(obj);
}
public void Remove(LifeName lifeName, object obj)
{
LifeCycleConfig.LifeCycles[lifeName].Remove(obj);
}
public void RemoveAll(object obj)
{
foreach (var life in LifeCycleConfig.LifeCycles)
{
life.Value.Remove(obj);
}
}
//...
}

RemoveAll(object obj)方法用于将当前对象所有的用到Unity生命周期的地方全部移除。

Loading界面需要在LifeCycleMgrUpdate每帧更新,及时读取加载进度。

LoadingView

修改UIUtil文件,在UIUtilData中添加GetComponentAddComponent的快捷方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class UIUtilData
{
//...
public T GetCo<T>() where T : UnityEngine.Component
{
if (Go != null)
return Go.GetComponent<T>();
else
{
Debug.LogError("当前Go为空");
return null;
}
}
public void AddCo<T>() where T : UnityEngine.Component
{
if (Go != null)
Go.AddComponent<T>();
else
{
Debug.LogError("当前Go为空");
}
}
}

LoadingView

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
using UnityEngine.UI;

[BindPrefab(Pathes.LOADING_VIEW, Consts.BIND_PREFAB_PRIORITY_VIEW)]
public class LoadingView : ViewBase
{
private Slider _slider;
protected override void InitChild()
{
_slider = UIUtil.Get("Slider").GetCo<Slider>();
}
public override void Show()
{
base.Show();
LifeCycleMgr.Instance.Add(LifeName.UPDATE, this);
}
public override void UpdateFun()
{
base.UpdateFun();
UpdateProgress();
UpdateSlider();
}
private void UpdateProgress()
{
float progress = SceneMgr.Instance.Process();
progress *= 100;
UIUtil.Get("Progress").SetText(string.Format("{0}%", progress));
}

private void UpdateSlider()
{
_slider.value = SceneMgr.Instance.Process();
}
public override void Hide()
{
base.Hide();
LifeCycleMgr.Instance.Remove(LifeName.UPDATE, this);
}
private void OnDestroy()//Destroy的生命周期没有统一管理,这里自行调用
{
Hide();
}
}

LoadingController

GameStateModel中添加CurrentSceneTargetScene属性

1
2
public SceneName CurrentScene {  get; set; }
public SceneName TargetScene { get; set; }

LoadingController

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
[BindPrefab(Pathes.LOADING_VIEW, Consts.BIND_PREFAB_PRIORITY_CONTROLLER)]
public class LoadingController : ControllerBase
{
protected override void InitChild()
{
}
public override void Show()
{
base.Show();
if(GameStateModel.Instance.TargetScene != GameStateModel.Instance.CurrentScene &&
GameStateModel.Instance.TargetScene != SceneName.NULL)
{
SceneMgr.Instance.LoadSceneAsync(GameStateModel.Instance.TargetScene);
LifeCycleMgr.Instance.Add(LifeName.UPDATE, this);
}
}
public override void UpdateFun()
{
base.UpdateFun();
if(SceneMgr.Instance.Process() == 1f)
{
SceneMgr.Instance.SceneActivation();
}
}
public override void Hide()
{
base.Hide();
if(GameStateModel.Instance.TargetScene != SceneName.NULL )
{
GameStateModel.Instance.CurrentScene = GameStateModel.Instance.TargetScene;
GameStateModel.Instance.TargetScene = SceneName.NULL;
}
LifeCycleMgr.Instance.Remove(LifeName.UPDATE, this);

}
private void OnDestroy()//Destroy的生命周期没有统一管理,这里自行调用
{
Hide();
}
}

启用加载界面

LevelItemController

修改LevelItemController,添加选关进入的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LevelItemController : ControllerBase
{
private int _id;
protected override void InitChild()
{
_id = transform.GetSiblingIndex();
transform.ButtonAction("Enter", () =>
{
GameStateModel.Instance.TargetScene = SceneName.Game;//这里的Game是个空场景。
UIMgr.Instance.Show(Pathes.LOADING_VIEW);
});
transform.ButtonAction("Mask", () =>
{
UIMgr.Instance.ShowDialog("当前关卡未开放");
});
}

}

注意在Build Setting里面添加场景。

GameRoot

修改GameRoot,初始化GameStateModel的开始场景

1
2
3
4
5
private void Start()
{
GameStateModel.Instance.CurrentScene = SceneName.StartScene;
//...
}

注意事项

到目前为止的设计中,只有CoroutineMgrLifeCycleMgr使用过DontDestroyOnLoad这个API。在切换场景后,所有的UI都不会保留