在之前的PlaneSpritesModel中,我们让它继承了IInit接口,单独继承这个接口就表示此脚本的生命周期由生命周期管理器管理。

LifeCycleMgr

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System;
using System.Collections;
using System.Collections.Generic;

public class LifeCycleMgr : LazyMonoSing<LifeCycleMgr>, IInit
{
private enum LifeName
{
INIT,
UPDATE
}

private readonly ArrayList _objects;
private static readonly Dictionary<LifeName,ILifeCycle> _lifeCycles = new ()
{
{ LifeName.INIT, new LifeCycle<IInit>() },
{ LifeName.UPDATE, new LifeCycle<IUpdate>() }
};
private readonly Dictionary<LifeName, Action> _lifeCyclesFuncs = new()
{
{ LifeName.INIT, ()=>
{
_lifeCycles[LifeName.INIT].Execute((IInit o) =>
{
o.Init();
});
}
},
{ LifeName.UPDATE, ()=>
{
_lifeCycles[LifeName.UPDATE].Execute((IUpdate o) =>
{
o.UpdateFun();
});
}
}
};

void IInit.Init()
{
Add();
InitObject();
_lifeCyclesFuncs[LifeName.INIT].Invoke();
}
private void Add()
{
_objects.Add(PlaneSpritesModel.Instance);
}
private void InitObject()
{
foreach (var obj in _objects)
{
foreach(var life in _lifeCycles)
{
if(life.Value.Add(obj)) break;//TODO: 这里会跳过实现了多个接口的对象
}
}
}
private void Update()
{
_lifeCyclesFuncs[LifeName.UPDATE].Invoke();
}
}
public interface ILifeCycle
{
bool Add(object obj);
void Remove(object obj);
void Execute<T>(Action<T> action);
}

public class LifeCycle<T> : ILifeCycle
{
private readonly HashSet<object> _objects = new();
private readonly HashSet<object> _objToRemove = new();

public bool Add(object obj)
{
if(obj is T)
{
_objects.Add(obj);
return true;
}
return false;
}
public void Remove(object obj)
{
_objToRemove.Add(obj);
}

public void Execute<T1>(Action<T1> action)
{
foreach (var o in _objects)
{
if(o == null)
_objToRemove.Add(o);
else
{
if (o is T1 t1)
action.Invoke(t1);
}
}
foreach (var o in _objToRemove)
{
_objects.Remove(o);
}
_objToRemove.Clear();
}
}

这里显式实现了IInit接口,防止外部调用初始化方法。

使用LifeCycle<T>泛型类来作为所有生命周期接口的“容器”,这个类可以理解成一个“配置类”。

使用ILifeCycle接口来抽象化所有生命周期接口和生命周期执行方法,这也是尽量修改配置不修改逻辑的思想:

LifeCycleMgrInitObject方法只需要调用统一的接口方法Add就可以实现自动判断生命周期接口类型并缓存好。

LifeCycleMgrInit方法和Update方法只需要调用提前配置好的_lifeCyclesFuncs字典里引用的方法即可。

ILifeCycle之中声明了Execute<T>(Action<T> action)方法,这是一个带有回调的方法,每一个LifeCycle配置类都会执行这个回调方法,回调方法是一个泛型方法,我们把生命周期接口作为泛型参数,在传入的回调方法中把生命周期方法执行掉。这个传入的回调方法是在_lifeCyclesFuncs字典里传入的。

这种思想要体会。

LifeCycleConfig

在Config文件夹内新建LifeCycleConfig文件,把LifeCycleMgr里面配置部分转移到这里

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

public class LifeCycleConfig
{

public static readonly Dictionary<LifeName, ILifeCycle> LifeCycles = new()
{
{ LifeName.INIT, new LifeCycle<IInit>() },
{ LifeName.UPDATE, new LifeCycle<IUpdate>() }
};
public static readonly Dictionary<LifeName, Action> LifeCyclesFuncs = new()
{
{ LifeName.INIT, ()=>
{
LifeCycles[LifeName.INIT].Execute((IInit o) =>
{
o.Init();
});
}
},
{ LifeName.UPDATE, ()=>
{
LifeCycles[LifeName.UPDATE].Execute((IUpdate o) =>
{
o.UpdateFun();
});
}
}
};
}
public enum LifeName
{
INIT,
UPDATE
}

LifeCycleAddConfig

在Config文件夹内新建LifeCycleAddConfig文件,把LifeCycleMgr里面Add方法内需要添加各个Model的部分转移到这里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections;

public class LifeCycleAddConfig : IInit
{
public ArrayList Objects { get; private set; }
public void Init()
{
Objects = new ArrayList();
Add();
}
private void Add()
{
Objects.Add(PlaneSpritesModel.Instance);
}
}

拆分后的LifeCycleMgr

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
89
using System;
using System.Collections.Generic;

public class LifeCycleMgr : LazyMonoSing<LifeCycleMgr>, IInit
{

void IInit.Init()
{
LifeCycleAddConfig lifeCycleAddConfig = new();
lifeCycleAddConfig.Init();
InitObject(lifeCycleAddConfig);
LifeCycleConfig.LifeCyclesFuncs[LifeName.INIT].Invoke();
}

private void InitObject(LifeCycleAddConfig config)
{
foreach (var obj in config.Objects)
{
foreach(var life in LifeCycleConfig.LifeCycles)
{
if(life.Value.Add(obj)) break;//TODO: 这里会跳过实现了多个接口的对象
}
}
}
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);
}
}
private void Update()
{
LifeCycleConfig.LifeCyclesFuncs[LifeName.UPDATE].Invoke();
}
}
public interface ILifeCycle
{
bool Add(object obj);
void Remove(object obj);
void Execute<T>(Action<T> action);
}

public class LifeCycle<T> : ILifeCycle
{
private readonly HashSet<object> _objects = new();
private readonly HashSet<object> _objToRemove = new();

public bool Add(object obj)
{
if(obj is T)
{
_objects.Add(obj);
return true;
}
return false;
}
public void Remove(object obj)
{
_objToRemove.Add(obj);
}

public void Execute<T1>(Action<T1> action)
{
foreach (var o in _objects)
{
if(o == null)
_objToRemove.Add(o);
else
{
if (o is T1 t1)
action.Invoke(t1);
}
}
foreach (var o in _objToRemove)
{
_objects.Remove(o);
}
_objToRemove.Clear();
}
}

尽量让每个类都只有单一的职责

通过以上的拆分,让生命周期函数更加灵活可配置。这种思想就是把需要配置的变量拆分成各个单独的类。一些明确需要长期存在的变量在单独的类中声明成静态的(上面的LifeCycleConfig的字典),一些临时的功能性的变量在单独的类中作为普通变量即可(上面LifeCycleAddConfig的Objects变量)。

修改各个Manager

有了LifeCycleMgr之后,我们把其他Manager的生命周期管理放到LifeCycleMgr

ConfigMgr

让它继承IInit接口

1
public class ConfigMgr : NormalSingleton<ConfigMgr>, IInit

InitCustomAttributes

让它继承IInit接口

1
public class InitCustomAttributes : IInit

修改GameRoot

GameRoot初始化的逻辑进行转移

1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;

public class GameRoot : MonoBehaviour
{
public GameObject canvas;
private void Start()
{
IInit lifeCycle = LifeCycleMgr.Instance;
lifeCycle.Init();
UIMgr.Instance.Show(Path.START_VIEW);
}
}

将转移的逻辑放到LifeCycleAddConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;

public class LifeCycleAddConfig : IInit
{
public ArrayList Objects { get; private set; }
public void Init()
{
Objects = new ArrayList();
Add();
}
private void Add()
{
//因为我们的配置会刷进存档里,如果再次修改配置的值,存档并不会更新,所以每次启动时清空一次
//DataMgr.Instance.ClearAll();

//优先级0
Objects.Add(ConfigMgr.Instance);
Objects.Add(new InitCustomAttributes());
//优先级1
Objects.Add(PlaneSpritesModel.Instance);
//优先级2
}
}

如果想要单独指定每个模块初始化的优先级,可以这样修改(伪代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections;
using System.Collections.Generic;

public class LifeCycleAddConfig : IInit
{
public Dictionary<int,ArrayList> Objects { get; private set; }
public void Init()
{
//...
}
private void Add(int priority)
{
//...
}
}

字典的key值为优先级,Add方法将优先级作为参数,添加到相应的字典,然后再在LifeCycleMgr中根据不同的优先级进行初始化,此处略

修改LifeCycle<T>

在后期的开发中,我们发现使用HashSet<object>在遍历时无法再对集合进行增减操作,这导致对象池中的对象管理总是报错。所以我们还是使用List代替HashSet。

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
public class LifeCycle<T> : ILifeCycle
{
private readonly List<object> _objects = new(128);

public bool Add(object obj)
{
if(obj is T)
{
if (_objects.Contains(obj)) return false;
else
{
_objects.Add(obj);
return true;
}
}
return false;
}
public void Remove(object obj)
{
_objects.Remove(obj);
}

public void Execute<T1>(Action<T1> action)
{
for (int i = 0; i < _objects.Count; i++)
{
if (_objects[i] is T1 t1) action.Invoke(t1);
}
}
}