Unity-IOC-Event

此类库为IOC库的拓展,旨在实现一个自动注册事件的事件管理器.不再需要像传统项目中自己手动RegisterUnregister事件.

添加引用

通过Package Manager添加如下依赖:

Package Description
https://github.com/kakashiio/Unity-Reflection.git#1.0.0 Reflection
https://github.com/kakashiio/Unity-IOC.git#1.0.0 IOC
https://github.com/kakashiio/Unity-IOC-Event.git#1.0.0 IOC-Event

示例1:全局事件基础

假设我们希望实现如下游戏流程:

  1. IOCContainer初始化完成时,触发EventInit事件
  2. GameFlowController监听所有事件并做出响应
    1. 当收到EventInit事件时
      1. 打印[GameFlow] OnInit
      2. 触发EventFinishInit事件
    2. 当收到EventFinishInit事件时
      1. 打印[GameFlow] OnFinishInit
      2. 触发EventLoadingMain,此时进度为0
      3. 进行场景加载
    3. 当收到EventLoadingMain事件时
      1. 打印[GameFlow] OnLoadingMain 进度值
    4. 当收到EventLoadedScene事件时
      1. 打印[GameFlow] OnEnterScene scene=当前激活的场景

创建一个IOCContainer

1
2
3
4
5
6
7
8
ITypeContainer typeContainer = new TypeContainerCollection(new List<ITypeContainer>
{
new TypeContainer(Assembly.GetExecutingAssembly()),
new TypeContainer(typeof(IOCComponent).Assembly),
new TypeContainer(typeof(EventManager).Assembly)
});

new IOCContainerBuilder(typeContainer).Build();

将上述代码添加到你应用程序启动的位置.

先定义所需的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class EventInit : EventArg {}

class EventFinishInit : EventArg {}

class EventLoadingMain : EventArg
{
public float Progress;

public EventLoadingMain(float progress)
{
Progress = progress;
}
}

class EventLoadedScene : EventArg
{
public Scene Scene;

public EventLoadedScene(Scene scene)
{
Scene = scene;
}
}

定义Game以响应IOCContainer的生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[IOCComponent]
class Game : IInstanceLifeCycle
{
[Autowired]
private EventManager _EventManager;

public void BeforePropertiesOrFieldsSet()
{
}

public void AfterPropertiesOrFieldsSet()
{
}

public void AfterAllInstanceInit()
{
_EventManager.FireEvent<EventInit>();
}
}

定义GameFlowController响应事件

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
[IOCComponent]
class GameFlowController
{
[Autowired]
private AssetLoader _AssetLoader;
[Autowired]
private EventManager _EventManager;

[Event]
public void OnInit(EventInit eventInit)
{
Debug.LogError("[GameFlow] OnInit");
_EventManager.FireEvent<EventFinishInit>();
}

[Event]
public void OnFinishInit(EventFinishInit eventFinishInit)
{
Debug.LogError("[GameFlow] OnFinishInit");
_EventManager.FireEvent(new EventLoadingMain(0));
_AssetLoader.LoadScene("ScenePath", (scene) =>
{
_EventManager.FireEvent(new EventLoadingMain(100));
});
}

[Event]
public void OnLoadingMain(EventLoadingMain eventLoadingMain)
{
Debug.LogError($"[GameFlow] OnLoadingMain {eventLoadingMain.Progress}");
}

[Event]
public void OnEnterScene(EventLoadedScene eventLoadedScene)
{
Debug.LogError($"[GameFlow] OnEnterScene scene={eventLoadedScene.Scene}");
}
}

定义AssetLoader模拟资源加载

1
2
3
4
5
6
7
8
9
10
11
12
13
[IOCComponent]
class AssetLoader
{
[Autowired]
private EventManager _EventManager;

public void LoadScene(string scenePath, Action<Scene> onLoadedScene)
{
var scene = SceneManager.GetActiveScene();
onLoadedScene(scene);
_EventManager.FireEvent(new EventLoadedScene(scene));
}
}

所有工作完成

运行场景会发现控制台中按顺序出现如下输出:

1
2
3
4
5
[GameFlow] OnInit
[GameFlow] OnFinishInit
[GameFlow] OnLoadingMain 0
[GameFlow] OnLoadingMain 100
[GameFlow] OnEnterScene scene=UnityEngine.SceneManagement.Scene

很神奇,对吧!IOCContainerEventManager帮助你将IOCContainer中所有带有Event attribute的方法注册到EventManager中,因此当调用EventManagerFireEvent时,对应的方法将会被自动调用.

该例子适合于类似全局事件注册,比如网络消息注册等,此类消息的特点为只要整个游戏还在运行中,那么这类消息被永久监听,不会从监听中移除.

那么对于类似UI界面的消息需求,有的时候只需要在打开UI时监听,在关闭UI时移除监听.该EventManager也是支持的.

示例2:局部事件基础

假设我们希望实现如下游戏流程:

IOCContainer初始化完成时:

  1. 通过UIManager打开BagUI
  2. 触发EventBagItemDataChange事件,将ID1的物件的Count更新为100
  3. 触发EventBagItemDataChange事件,将ID1的物件的Count更新为200
  4. 触发EventBagDeleteItem事件,将ID1的物件移除
  5. 通过UIManager关闭BagUI
  6. 触发EventBagItemDataChange事件,将ID1的物件的Count更新为300
  7. 触发EventBagDeleteItem事件,将ID2的物件移除

注意观察BagUI能否收到第6步和第7的事件

创建一个IOCContainer

1
2
3
4
5
6
7
8
ITypeContainer typeContainer = new TypeContainerCollection(new List<ITypeContainer>
{
new TypeContainer(Assembly.GetExecutingAssembly()),
new TypeContainer(typeof(IOCComponent).Assembly),
new TypeContainer(typeof(EventManager).Assembly)
});

new IOCContainerBuilder(typeContainer).Build();

将上述代码添加到你应用程序启动的位置.

定义事件

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
class EventBagItemDataChange : EventArg
{
public int ID;
public int Count;

public EventBagItemDataChange(int id, int count)
{
ID = id;
Count = count;
}

public override string ToString()
{
return $"ID={ID} Count={Count}";
}
}

class EventBagDeleteItem : EventArg
{
public int ID;

public EventBagDeleteItem(int id)
{
ID = id;
}

public override string ToString()
{
return $"ID={ID}";
}
}

定义背包BagUI

1
2
3
4
5
6
7
8
9
10
11
12
13
class BagUI
{
[Event]
public void OnBagDataChange(EventBagItemDataChange eventBagItemDataChange)
{
Debug.LogError($"[OnBagDataChange] {eventBagItemDataChange}");
}
[Event]
public void OnEventBagDeleteItem(EventBagDeleteItem eventBagDeleteItem)
{
Debug.LogError($"[OnEventBagDeleteItem] {eventBagDeleteItem}");
}
}

该UI为了演示对事件的监听,目前没有任何逻辑,仅仅在收到事件时进行打印.

定义一个用于辅助测试的UIManager

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
[IOCComponent]
class UIManager
{
[Autowired]
private EventManager _EventManager;

private IIOCContainer _IOCContainer;

private Dictionary<Type, object> _UIs = new Dictionary<Type, object>();

public void Open<T>(Action<T> onOpened) where T : new()
{
var uiType = typeof(T);
if (_UIs.ContainsKey(uiType))
{
onOpened((T) _UIs[uiType]);
return;
}
var t = new T();
_EventManager.Register(t);
_UIs.Add(uiType, t);
onOpened.Invoke(t);
}

public void Close<T>()
{
var uiType = typeof(T);
if (!_UIs.ContainsKey(uiType))
{
return;
}

var ui = _UIs[uiType];
_UIs.Remove(uiType);
_EventManager.Unregister(ui);
}
}

可以看到每次Open时,我们将创建的UI对象通过Register方法注册到EventManager中,而每次Close时,我们将创建的UI对象注册的所有事件通过Unregister方法从EventManager中移除.可以看到不需要自己将BagUI中的对象一个个地进行注册和反注册.EventManager.Register(object)方法将object中所有带有Event attribute的方法注册到事件管理器中.

定义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
[IOCComponent]
class Game : IInstanceLifeCycle
{
[Autowired]
private UIManager _UIManager;
[Autowired]
private EventManager _EventManager;

public void BeforePropertiesOrFieldsSet()
{
}

public void AfterPropertiesOrFieldsSet()
{
}

public void AfterAllInstanceInit()
{
_UIManager.Open<BagUI>((ui) => { });
_EventManager.FireEvent(new EventBagItemDataChange(1, 100));
_EventManager.FireEvent(new EventBagItemDataChange(1, 200));
_EventManager.FireEvent(new EventBagDeleteItem(1));
_UIManager.Close<BagUI>();
_EventManager.FireEvent(new EventBagItemDataChange(1, 300));
_EventManager.FireEvent(new EventBagDeleteItem(2));
}
}

所有工作完成

运行场景会发现控制台中按顺序出现如下输出:

1
2
3
[OnBagDataChange] ID=1 Count=100
[OnBagDataChange] ID=1 Count=200
[OnEventBagDeleteItem] ID=1

可以看到当调用_UIManager.Close<BagUI>()后,BagUI没有触发另外两个事件.这正是我们所预期的效果.

工程地址

完整的Package工程地址在https://github.com/kakashiio/Unity-IOC-Event

致谢

感谢百忙之中阅读本文,如果觉得我的文章帮到了你,欢迎:转载、关注git、为仓库增加star等.你的简单回馈将是我继续创作的动力.

作者

Kakashi

发布于

2022-05-10

更新于

2022-05-14

许可协议

评论