我们再次重构Game场景,引入层级来对Game场景中的对象进行管理。

修改SceneRoot预制体层级,添加MapMgr节点:

  • SceneRoot
    • Main Camera
    • MapMgr
      • map_0
      • map_1

将MapMgr拉取进Resources——Prefab——Game中。并把SceneRoot预制体中的MapMgr节点删掉,仅保留Main Camera。

拉取一个小飞机到场景,命名为Player,并将Prefab保存到Resources——Prefab——Game中。

Pathes中添加这些Prefab的路径

1
2
public const string PREFAB_MAP_MGR = PREFAB_GAME_FOLDER + "MapMgr";
public const string PREFAB_PLAYER = PREFAB_GAME_FOLDER + "Player";

Enums中添加GameLayer枚举,这里设置成负数,方便在后面指定各个层级的z轴大小

1
2
3
4
5
6
public enum GameLayer
{
BACKGROUND = 0,
PLANE = -1,
EFFECT = -2,
}

SceneRoot

在Scripts——Logic文件夹中新建SceneRoot,这个脚本负责当前场景所有逻辑脚本的初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;

public class SceneRoot : MonoBehaviour
{
public void Init()
{
var gameLayerMgr = gameObject.AddComponent<GameLayerMgr>();
gameLayerMgr.Init();

Transform camera = transform.Find("Main Camera");
camera.AddCo<CameraMove>();

var mapGo = LoadMgr.Instance.LoadPrefabAndInstantiate(Pathes.PREFAB_MAP_MGR);
var mapmgr = mapGo.AddComponent<MapMgr>();
mapmgr.Init(camera);
}
}

修改SceneConfig

添加了SceneRoot后,修改SceneConfig的场景初始化逻辑

1
2
3
4
5
6
7
8
9
10
11
private void AddLoaded()
{
SceneMgr.Instance.AddSceneLoaded(SceneName.Game, callback => PoolMgr.Instance.Init(callback));
SceneMgr.Instance.AddSceneLoaded(SceneName.Game, callback =>
{
var go = LoadMgr.Instance.LoadPrefabAndInstantiate(Pathes.PREFAB_SCENEROOT);
var root = go.AddComponent<SceneRoot>();
root.Init();
callback?.Invoke();
});
}

修改MapMgr

把初始化相机的逻辑转移到SceneRoot中,并且让它继承BGView(下面讲到)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;

public class MapMgr : BGView
{
public void Init(Transform camera)
{
Transform map1 = transform.Find("map_1");
Transform map0 = transform.Find("map_0");
float offset = Mathf.Abs(map1.position.y - map0.position.y);
var item0 = map0.AddCo<MapItem>();
item0.Init(offset, camera);
var item1 = map1.AddCo<MapItem>();
item1.Init(offset, camera);

}
}

GameLayerMgr

在Scripts——Logic文件夹中新建GameLayerMgr,这个对象使用自己的单例,这个单例只在Game场景中起作用,离开Game场景后会被销毁。这个单例的创建和Init方法关系密切,这和一般的Mono单例写法不同。

负责生成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
41
42
43
using System;
using System.Collections.Generic;
using UnityEngine;

public class GameLayerMgr : MonoBehaviour
{
private static GameLayerMgr instance;
public static GameLayerMgr Instance
{
get
{
if (SceneMgr.Instance.IsScene(SceneName.Game))
{
return instance;
}
else
Debug.LogError("GameSceneMgr只能在Scene场景下运行");
return null;
}
}
private Dictionary<GameLayer, Transform> _layerGoDic;
public void Init()
{
instance = this;
_layerGoDic = new Dictionary<GameLayer, Transform>();
foreach (GameLayer layer in Enum.GetValues(typeof(GameLayer)))
{
GameObject layerGo = new(layer.ToString());
layerGo.transform.position = Vector3.forward * (int)layer;
layerGo.transform.SetParent(transform);
_layerGoDic.Add(layer,layerGo.transform);
}
}
public void SetLayerParent(IGameView gameView)
{
if (_layerGoDic.TryGetValue(gameView.GameLayer, out Transform parent))
{
gameView.Self.SetParent(parent);//直接调用SetParent方法不会改变物体原本的世界坐标,所以Player的Prefab的z轴不能为0
}
else
Debug.LogError("当前层级并没有初始化进字典中:" + gameView.GameLayer.ToString());
}
}

GameView

Game场景中的层级不同于UI,所有的UI基本的开闭逻辑都是相似的,而Game场景中的对象却是五花八门,所以需要新的接口进行管理。

在Scripts——Logic文件夹下新建View文件夹,在其中新建IGameView

IGameView

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

public interface IGameView : IInit
{
GameLayer GameLayer { get; }
Transform Self { get; }
}
public abstract class GameViewBase : MonoBehaviour, IGameView
{
public abstract GameLayer GameLayer { get; }

public Transform Self => transform;

public virtual void Init()
{
}
private void OnEnable()
{
if(GameLayerMgr.Instance != null)
{
GameLayerMgr.Instance.SetLayerParent(this);
}
Init();
}
}

public class BGView : GameViewBase
{
public override GameLayer GameLayer => GameLayer.BACKGROUND;
}
public class PlaneView : GameViewBase
{
public override GameLayer GameLayer => GameLayer.PLANE;
}
public class EffectView : GameViewBase
{
public override GameLayer GameLayer => GameLayer.EFFECT;
}