The complete code implementation of the event system of the unity front-end architecture

Table of contents

Foreword:

1. Define the event base class

2. Define the event resource class

3. Define the event management class

4. Event usage examples


Foreword:

In Unity game development, the event system is very important. The main reasons are as follows:

  1. 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.

  2. 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.

  3. 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

 


Guess you like

Origin blog.csdn.net/lejian/article/details/131275445