Table of contents
1. Define the event base class
2. Define the event resource class
3. Define the event management class
Foreword:
In Unity game development, the event system is very important. The main reasons are as follows:
-
Loose coupling: Through the event system, we can achieve loose coupling of the code, which means that we can decouple the classes in the code. In game development, if the code is tightly coupled, when the amount of code increases, it is easy to cause code confusion and difficulty in maintenance. The loose coupling of the event system can reduce this pressure and allow for better composition and management of game objects.
-
High efficiency: Using the event system allows game objects to broadcast their own state without requiring each game object to detect the state of other objects, which can have a significant impact on performance and improve game efficiency.
-
Easy to expand: When the game needs new functions, through the event system, we can easily expand the code, just create new events and let other codes subscribe to the new events without changing the original code.
In short, using the event system can help us design and manage game objects, increase the readability and maintainability of the code, and also improve the efficiency and scalability of the game. In Unity game development, the event system can be said to be an integral part.
1. Define the event base class
public class UEvent
{
public readonly int type;
public UEvent(int eventType)
{
this.type = eventType;
}
public static T New<T>() where T: UEvent, IPoolItem,new()
{
return Engine.Pools.Pop<T>();
}
}
2. Define the event resource class
The event resource class is an internal class that actually handles events, and it has three important interfaces
TriggerEvent triggers the corresponding event
Subscribe to Subscribe corresponding events
Unsubscribe Unsubscribe corresponding event
using System;
using System.Collections.Generic;
internal class UEventSource<T> where T : UEvent
{
private readonly Dictionary<int, List<Action<T>>> events = new Dictionary<int, List<Action<T>>>();
internal void TriggerEvent(T value)
{
var key = value.type;
if (!events.TryGetValue(key, out List<Action<T>> outList))
return;
for (var i = 0; i < outList.Count; i++)
{
outList[i]?.Invoke(value);
}
}
internal void Subscribe(int type, Action<T> handler)
{
if (events.ContainsKey(type))
{
List<Action<T>> list = events[type];
if (list == null)
{
list = new List<Action<T>>();
events[type] = list;
}
#if UNITY_EDITOR
if (list.Contains(handler))
{
ULog.Warn("重复注册事件 {0} {1}", type, handler);
return;
}
#endif
list.Add(handler);
}
else
{
List<Action<T>> list = new List<Action<T>> {handler};
events[type] = list;
}
}
internal void Unsubscribe(int type, Action<T> handler)
{
if (events.ContainsKey(type))
{
List<Action<T>> list = events[type];
if (list != null)
{
list.Remove(handler);
}
}
}
internal void Destroy()
{
events.Clear();
}
3. Define the event management class
The event management class is an external operation class, which provides asynchronous triggering and immediate triggering functions, including several interfaces:
Subscribe event interface
public void Subscribe(int type, Action<UEvent> handler)
unsubscribe interface
public void Unsubscribe(int type, Action<UEvent> handler)
Immediately trigger the interface
private void TriggerEventImmediate(UEvent value)
send event
public void SendMessage(UEvent value, bool isImmediate=true)
isImmediate indicates whether to trigger immediately, otherwise wait for the next frame to trigger
Complete code of event management class
using System;
using System.Collections.Generic;
public class EventsComponent
{
private readonly HashSet<int> firingTypes = new HashSet<int>();
private readonly UEventSource<UEvent> source = new UEventSource<UEvent>();
private readonly List<UEvent> tickEvents = new List<UEvent>();
public void SendMessage(UEvent value, bool isImmediate=true)
{
if (isImmediate)
{
TriggerEventImmediate(value);
return;
}
tickEvents.Add(value);
}
public void Update()
{
if (tickEvents.Count == 0)
return;
foreach (var t in tickEvents)
{
TriggerEventImmediate(t);
}
tickEvents.Clear();
}
public void Subscribe(int type, Action<UEvent> handler)
{
source.Subscribe(type, handler);
}
public void Unsubscribe(int type, Action<UEvent> handler)
{
source.Unsubscribe(type, handler);
}
public void Destroy()
{
tickEvents.Clear();
firingTypes.Clear();
source.Destroy();
}
private void TriggerEventImmediate(UEvent value)
{
firingTypes.Add(value.type);
source.TriggerEvent(value);
firingTypes.Remove(value.type);
}
}
4. Event usage examples
In a level, the death of a game character is the only condition for us to determine the result. We hope that the death of the character will trigger the result. Let’s write a simple example using this as an example.
Define event type
public class EventLogicCode
{
public const int Dead = 1;
}
Define death events
/// <summary>
/// 死亡事件
/// </summary>
public class EventDead : UEvent
{
public int _actorId;
public EventDead() : base(EventLogicCode.Dead)
{
}
}
The role class code is as follows
public class Charactor
{
private EventsComponent _events; //记得给事件组件赋值
private int _id;
....
//角色死亡调用
public void Dead()
{
EventDead dead = new EventDead();
dead.id = id;
_events?.SendMesage(dead);
}
}
result control class
public class ResultController : MonoBehaviour
{
private EventsComponent _events;//记得赋值
private int _winId;
.....
private void OnEnable()
{
_events?.Subscribe(EventLogicCode.Dead, BeginFade);
}
private void OnDisable()
{
_events?.Unsubscribe(EventLogicCode.Dead, BeginFade);
}
public void OnDead(UEvent e)
{
EventDead dead = e as EventDead;
if(dead.id == _winId)
{
GameWin();
}
}
private void GameWin()
{
....
}
}
This class describes whether the Id is the same as the victory condition when the character dies, and if it is, then the victory is won