最终效果
前言
之前我们介绍过委托的用法,具体可以跳转:【unity小技巧】委托(Delegate)的基础使用和介绍
这期来讲讲事件,使用你会发现它和委托真的很像,那么他们具体有什么区别呢?
在Unity中,事件(Event)和委托(Delegate)是两种不同的概念,它们之间有一些区别:
- 委托(Delegate):
- 委托是一种类型,用于引用一个或多个方法。委托可以看作是函数指针或函数的代理。
- 委托用于实现回调函数、事件处理和方法的动态绑定。
- 委托是一种类型安全的方式来封装方法调用。
- 事件(Event):
- 事件是建立在委托之上的高级概念,它提供了一种更加简洁、安全的方式来实现观察者模式。
- 事件使用委托来通知其他对象或类,当特定情况发生时,让它们执行相应的操作。
- 事件可以通过“+=”(添加订阅者)和“-=”(移除订阅者)来管理委托的订阅和取消订阅。
何时使用事件和委托:
- 当需要实现一对多的消息传递时,可以使用事件。比如,当某个对象的状态发生变化时,需要通知多个其他对象做出相应的反应。
- 当需要回调函数或方法引用时,可以使用委托。比如,当某个操作完成后需要执行特定的方法,可以使用委托来实现回调。
综合来说,事件和委托在Unity开发中都有各自的用途,根据具体情况选择合适的方式来实现功能会更加方便和有效。
一、素材
https://assetstore.unity.com/packages/2d/environments/pixel-art-top-down-basic-187605
二、角色金币交互
1. 拾取金币
金币事件,定义当金币增加时触发的事件
public class GoldEvents { public event Action<int> onGoldGained; // 当金币增加时触发的事件 public void GoldGained(int gold) { onGoldGained?.Invoke(gold); // 触发金币增加事件 } }
游戏事件管理中心,初始化各种事件
public class GameEventsManager : MonoBehaviour { public static GameEventsManager instance { get; private set; } // 单例实例 public GoldEvents goldEvents; // 金币事件 private void Awake() { if (instance != null) { Debug.LogError("在场景中找到多个游戏事件管理器"); // 如果场景中存在多个游戏事件管理器,则输出错误信息 } instance = this; // 设置单例实例为当前实例 // 初始化所有事件 goldEvents = new GoldEvents(); // 初始化金币事件 } }
挂载脚本
管理玩家的金币,注册金币增加事件执行方法
// GoldManager类,负责管理玩家的金币 public class GoldManager : MonoBehaviour { [Header("Configuration")] // 游戏开始时玩家拥有的金币数量 [SerializeField] private int startingGold = 5; // 当前玩家持有的金币数量,外部只能获取不能设置 public int currentGold { get; private set; } // 在对象被激活时调用,初始化玩家的金币数量 private void Awake() { currentGold = startingGold; } // 注册事件监听器 private void OnEnable() { // 当玩家获得金币时,注册GoldGained方法来处理事件 GameEventsManager.instance.goldEvents.onGoldGained += GoldGained; } // 注销事件监听器 private void OnDisable() { // 当对象被禁用时,取消注册GoldGained方法 GameEventsManager.instance.goldEvents.onGoldGained -= GoldGained; } // 处理获得金币的事件 private void GoldGained(int gold) { // 增加玩家的金币数量 currentGold += gold; } }
配置脚本
设置脚本执行顺序,在Unity的"Project Settings"中,你可以通过"Script Execution Order"设置脚本的执行顺序,确保GameEventsManager的Awake方法总是在其他依赖它的组件之前执行,不然可能会报错。
定义金币脚本,控制金币拾取和金币重新生成
[RequireComponent(typeof(CircleCollider2D))] public class Coin : MonoBehaviour { [Header("Config")] [SerializeField] private float respawnTimeSeconds = 8; // 重生时间 [SerializeField] private int goldGained = 1; // 获得的金币数量 private CircleCollider2D circleCollider; // 圆形碰撞体 private SpriteRenderer visual; // 视觉元素 private void Awake() { // 初始化圆形碰撞体和视觉元素 circleCollider = GetComponent<CircleCollider2D>(); visual = GetComponentInChildren<SpriteRenderer>(); } // 收集金币的方法 private void CollectCoin() { circleCollider.enabled = false; visual.enabled = false; // 触发获得金币事件 GameEventsManager.instance.goldEvents.GoldGained(goldGained); StopAllCoroutines(); StartCoroutine(RespawnAfterTime()); } // 在一定时间后重生金币 private IEnumerator RespawnAfterTime() { yield return new WaitForSeconds(respawnTimeSeconds); circleCollider.enabled = true; visual.enabled = true; } // 触发进入触发器事件 private void OnTriggerEnter2D(Collider2D otherCollider) { if (otherCollider.CompareTag("Player")) { CollectCoin(); } } }
金币挂载脚本
运行效果
拾取金币后,currentGold加1,说明没问题
2. 显示金币数UI
一直查看金币变量数据变化,不是很方便,我们可以让金币数显示出来
修改GoldEvents,新增金币变化事件
public class GoldEvents { //。。。 public event Action<int> onGoldChange; // 当金币数量变化时触发的事件 public void GoldChange(int gold) { onGoldChange?.Invoke(gold); // 触发金币变化事件 } }
新增GoldUI,注册金币数量发生变化时调用的方法
public class GoldUI : MonoBehaviour { [Header("Components")] // 用于显示金币数量的TextMeshProUGUI组件 [SerializeField] private TextMeshProUGUI goldText; // 在对象激活时注册事件监听器 private void OnEnable() { // 当金币数量发生变化时,注册GoldChange方法来处理事件 GameEventsManager.instance.goldEvents.onGoldChange += GoldChange; } // 在对象禁用时注销事件监听器 private void OnDisable() { // 当对象被禁用时,取消注册GoldChange方法 GameEventsManager.instance.goldEvents.onGoldChange -= GoldChange; } // 处理金币数量变化的事件 private void GoldChange(int gold) { // 将金币数量转换为字符串,并更新UI显示 goldText.text = gold.ToString(); } }
同样配置GoldUI 脚本执行顺序
挂载脚本
修改GoldManager,广播当前金币数量的改变事件
// 在第一帧更新之前调用,广播当前金币数量的改变 private void Start() { GameEventsManager.instance.goldEvents.GoldChange(currentGold); } // 处理获得金币的事件 private void GoldGained(int gold) { // 增加玩家的金币数量 currentGold += gold; // 广播金币数量的变化 GameEventsManager.instance.goldEvents.GoldChange(currentGold); }
效果
优化(2024/6/4补充)
如果使用static静态字段,其实还可以省去GameEventsManager方法,直接调用即可,比如
UserEvents.cs
public class UserEvents { public static event Action<ClientData> onClient; public static event Action onUserList; public static void UserClient(ClientData clientData) { onClient?.Invoke(clientData); } public static void UserList() { onUserList?.Invoke(); } }
调用
UserEvents.UserList();
// 注册事件监听器 private void OnEnable() { UserEvents.onClient += UserClient; } // 注销事件监听器 private void OnDisable() { UserEvents.onClient -= UserClient; } private void UserClient(ClientData clientData) { curUserName.text = clientData.curUserName; }