欢迎光临
我们一直在努力

15.事件中心:实现

一、实现目标

敌人死亡时,需要通知其他系统执行对应逻辑:

敌人死亡
├── Level:游戏成功通关
└── Player:人物升级

Enemy 不直接引用 Level 和 Player,而是通过 EventMgr 发送死亡事件,从而降低对象之间的耦合度。

二、事件中心的数据结构

Dictionary<string, UnityAction> eventDic;

  • string:事件的唯一标识名。
  • UnityAction:事件触发时需要执行的方法。
  • Dictionary:根据事件名称找到对应的所有监听方法。
    敌人死亡事件使用常量保存:

public const string EnemyDieEvent = "EnemyDieEvent";

使用常量可以统一事件名称,避免手写字符串时出现拼写错误。

三、添加事件监听

Level 和 Player 在 Awake() 中监听敌人死亡事件:

EventMgr.Instance.AddEventListener
(
EventMgr.EnemyDieEvent,
ClearLevel
);

如果字典中已经存在该事件,就使用 += 添加监听方法;否则创建新的事件记录。

四、触发事件

敌人血量归零后触发死亡事件:

EventMgr.Instance.EventTrigger(EventMgr.EnemyDieEvent);

事件中心找到 EnemyDieEvent 对应的委托,并执行其中保存的全部方法。

按下 J

敌人扣除一点生命值

Hp <= 0

触发 EnemyDieEvent

Level.ClearLevel()
Player.UpGrade()

五、防止重复死亡

private bool isDead;

敌人死亡后把 isDead 设置为 true:

isDead = true;

检测按键时判断敌人是否存活:

if (Input.GetKeyDown(KeyCode.J) && !isDead)

这样可以防止生命值归零后重复触发死亡事件。

六、移除事件监听

监听者销毁时,需要移除自己注册的方法:

private void OnDestroy()
{
EventMgr.Instance.RemoveEventListener(
EventMgr.EnemyDieEvent,
ClearLevel
);
}

Awake → 添加监听
OnDestroy → 移除监听

如果一个事件已经没有任何监听者,就从字典中删除该事件记录。

七、完整代码

1. EventMgr.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class EventMgr : Singleton2<EventMgr>
{
// Key:事件名称
// Value:事件触发时需要执行的所有方法
private readonly Dictionary<string, UnityAction> eventDic = new();

// 敌人死亡事件的唯一标识
public const string EnemyDieEvent = "EnemyDieEvent";

private EventMgr()
{
}

/// <summary>
/// 触发事件。
/// </summary>
/// <param name="eventName">需要触发的事件名称。</param>
public void EventTrigger(string eventName)
{
if (eventDic.TryGetValue(eventName, out UnityAction unityAction))
{
unityAction?.Invoke();
}
}

/// <summary>
/// 添加事件监听。
/// </summary>
/// <param name="eventName">需要监听的事件名称。</param>
/// <param name="unityAction">事件触发时执行的方法。</param>
public void AddEventListener(
string eventName,
UnityAction unityAction)
{
if (string.IsNullOrEmpty(eventName) || unityAction == null)
{
Debug.LogError("输入的事件名称或者函数不合法");
return;
}

if (eventDic.ContainsKey(eventName))
{
eventDic[eventName] += unityAction;
}
else
{
eventDic.Add(eventName, unityAction);
}
}

/// <summary>
/// 移除指定的事件监听。
/// </summary>
/// <param name="eventName">事件名称。</param>
/// <param name="unityAction">需要移除的方法。</param>
public void RemoveEventListener(
string eventName,
UnityAction unityAction)
{
if (string.IsNullOrEmpty(eventName) || unityAction == null)
{
Debug.LogError("输入的事件名称或者函数不合法");
return;
}

if (!eventDic.ContainsKey(eventName))
return;

eventDic[eventName] -= unityAction;

// 没有监听者后删除该事件
if (eventDic[eventName] == null)
{
eventDic.Remove(eventName);
}
}

/// <summary>
/// 清空所有事件监听。
/// </summary>
public void Clear()
{
eventDic.Clear();
}

/// <summary>
/// 清空指定事件的所有监听。
/// </summary>
public void Clear(string eventName)
{
if (eventDic.ContainsKey(eventName))
{
eventDic.Remove(eventName);
}
}
}

2. Enemy.cs

using UnityEngine;

public class Enemy : MonoBehaviour
{
public int Hp = 5;

// 记录敌人是否已经死亡
private bool isDead;

private void Update()
{
if (Input.GetKeyDown(KeyCode.J) && !isDead)
{
Hp;
Debug.Log($"敌人扣血,剩余生命值:{Hp}");

if (Hp <= 0)
{
Die();
}
}
}

private void Die()
{
isDead = true;
Debug.Log("怪兽死亡");

EventMgr.Instance.EventTrigger(
EventMgr.EnemyDieEvent
);
}
}

3. Level.cs

using UnityEngine;

public class Level : MonoBehaviour
{
private void Awake()
{
EventMgr.Instance.AddEventListener(
EventMgr.EnemyDieEvent,
ClearLevel
);
}

private void ClearLevel()
{
Debug.Log("游戏成功通关");
}

private void OnDestroy()
{
EventMgr.Instance.RemoveEventListener(
EventMgr.EnemyDieEvent,
ClearLevel
);
}
}

4. Player.cs

using UnityEngine;

public class Player : MonoBehaviour
{
private void Awake()
{
EventMgr.Instance.AddEventListener(
EventMgr.EnemyDieEvent,
UpGrade
);
}

private void UpGrade()
{
Debug.Log("人物升级");
}

private void OnDestroy()
{
EventMgr.Instance.RemoveEventListener(
EventMgr.EnemyDieEvent,
UpGrade
);
}
}

运行游戏后连续按下 J:

敌人扣血,剩余生命值:4
敌人扣血,剩余生命值:3
敌人扣血,剩余生命值:2
敌人扣血,剩余生命值:1
敌人扣血,剩余生命值:0
怪兽死亡
游戏成功通关
人物升级

赞(0)
未经允许不得转载:171主机测评 » 15.事件中心:实现
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址