为什么要用事件中心系统呢?
如果没有事件中心系统,我们实现某个功能可能会导致组件之间更加紧密的耦合,降低代码的灵活性和可维护性。通常是直接调用方法,这样当这个功能需要修改时,我们又要去找到对应的功能去进行修改。
假设我们想要在玩家得分时更新 UI。没有事件系统的情况下,我们可以通过直接调用方法来实现这一功能。
// UIManager
public class UIManager : MonoBehaviour
{
public Text scoreText;
public void UpdateScore(int newScore)
{
scoreText.text = "Score: " + newScore;
}
}
// GameController
public class GameController : MonoBehaviour
{
public UIManager uiManager;
private int playerScore;
public void ScorePlayer(int points)
{
playerScore += points;
uiManager.UpdateScore(playerScore); // 直接调用 UIManager 的方法来更新分数
}
}
这种方法简单直观,但会导致 Ga9meController
和 UIManager
之间的紧密耦合。如果以后你需要修改 UI 管理的方式,可能需要修改多个地方的代码,不便于管理,当然这种也有优势,就是简单直接,适用于功能简单、需求明确的小项目
事件中心模式
(Event System Pattern)是一种设计模式,用于处理游戏中各个组件之间的通信。它通过集中管理所有的事件,使得不同的模块或对象之间可以通过事件进行解耦,从而避免直接依赖。这种模式对于大型游戏项目尤为重要,可以帮助减少各个模块之间的耦合度,提高代码的可维护性和扩展性。
使用字典和委托来实现(委托可以用unity自带的)
只需要触发一个事件,真正需要关心的是这个功能在什么时候执行,只需要在那里加一个监听事件即可
在这个示例中,EventManager
负责管理事件的注册、注销和触发,而 Player
组件是监听事件的对象。当玩家得分时,GameController
会触发 "PlayerScored"
事件,而 Player
会响应这个事件并执行相关的操作。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// // 事件中心
/// </summary>
public class EventManager : BaseManager<EventManager>
{
private Dictionary<string, UnityAction> eventDictionary = new Dictionary<string, UnityAction>();
// 注册事件
public void StartListening(string eventName, UnityAction listener)
{
if (eventDictionary.ContainsKey(eventName))
{
eventDictionary[eventName] += listener;
}
else
{
eventDictionary.Add(eventName, listener);
}
}
// 注销事件
public void StopListening(string eventName, UnityAction listener)
{
if (eventDictionary.ContainsKey(eventName))
{
eventDictionary[eventName] -= listener;
}
}
// 触发事件
public void TriggerEvent(string eventName)
{
if (eventDictionary.ContainsKey(eventName))
{
eventDictionary[eventName]?.Invoke();
}
}
/// <summary>
/// 清空事件中心
/// 用在场景切换中
/// </summary>
public void Clear()
{
eventDictionary.Clear();
}
}
/// <summary>
/// // 监听器
/// </summary>
public class Player : MonoBehaviour
{
private void OnEnable()
{
EventManager.GetInstance().StartListening("PlayerScored", OnPlayerScored);
}
private void OnDisable()
{
EventManager.GetInstance().StopListening("PlayerScored", OnPlayerScored);
}
void OnPlayerScored()
{
Debug.Log("玩家分数!");
}
}
/// <summary>
/// // 触发事件
/// </summary>
public class GameController : MonoBehaviour
{
public void ScorePlayer()
{
EventManager.GetInstance().TriggerEvent("PlayerScored");
}
}
问题:虽然实现了事件中心,但是并不通用,无法做到参数传递,使用 object
作为事件参数类型是一种更通用的方法
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// // 事件中心
/// </summary>
public class EventManager : BaseManager<EventManager>
{
private Dictionary<string, UnityAction<object>> eventDictionary = new Dictionary<string, UnityAction<object>>();
// 注册事件
public void StartListening(string eventName, UnityAction<object> listener)
{
if (eventDictionary.ContainsKey(eventName))
{
eventDictionary[eventName] += listener;
}
else
{
eventDictionary.Add(eventName, listener);
}
}
// 注销事件
public void StopListening(string eventName, UnityAction<object> listener)
{
if (eventDictionary.ContainsKey(eventName))
{
eventDictionary[eventName] -= listener;
}
}
// 触发事件
public void TriggerEvent(string eventName,object info)
{
if (eventDictionary.ContainsKey(eventName))
{
eventDictionary[eventName]?.Invoke(info);
}
}
/// <summary>
/// 清空事件中心
/// 用在场景切换中
/// </summary>
public void Clear()
{
eventDictionary.Clear();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// // 触发事件
/// </summary>
public class GameController : MonoBehaviour
{
public void ScorePlayer()
{
EventManager.GetInstance().TriggerEvent("PlayerScored",this);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// // 监听器
/// </summary>
public class Player : MonoBehaviour
{
private void OnEnable()
{
EventManager.GetInstance().StartListening("PlayerScored", OnPlayerScored);
}
private void OnDisable()
{
EventManager.GetInstance().StopListening("PlayerScored", OnPlayerScored);
}
void OnPlayerScored(object info)
{
Debug.Log("玩家分数!");
}
}
每次转换 object
为特定类型时,可能会有一定的性能损失,尤其是如果事件触发频繁且参数较大时,但是使用这种方法代码更通用灵活。