Unity学习90天-第7天-学习委托与事件(简化版)

欢迎回来! 今天我们来搞定理解委托和事件的核心概念,用"受伤、得分、游戏结束"三个游戏场景掌握解耦思路!

一、为什么要用事件?先看"耦合"的痛点

假设玩家受伤时,需要同时做三件事:、

玩家受伤

├── UI 血条减少

├── 音效播放"受伤音"

└── 相机震动

不用事件的写法(强耦合):

cs 复制代码
// 玩家脚本里直接调用所有系统
void TakeDamage(float damage)
{
    health -= damage;
    
    UIManager.Instance.UpdateHealthBar(health);   // 直接调用UI
    AudioManager.Instance.PlayHurtSound();         // 直接调用音效
    CameraShake.Instance.Shake();                  // 直接调用相机
}

问题出在哪?

问题 后果
玩家脚本要知道 UI、音效、相机的存在 改一个系统可能连锁修改多处
删掉音效系统 → 玩家脚本报错 牵一发而动全身
新增"受伤特效"→ 要改玩家脚本 功能越多,玩家脚本越臃肿

类比 :强耦合就像公司老板直接给每个员工打电话布置任务------老板要记住所有员工的电话,换人了就要改号码。事件就是设一个"广播电台"------老板只管播报消息,员工自己决定要不要收听,互不依赖。

二、委托------函数的"类型"

委托(delegate)本质是:用来存储"函数"的变量类型

类比 :普通变量存的是数据(int 存数字,string 存文字),委托变量存的是方法(函数)

cs 复制代码
// 声明一个委托类型(规定:无参数,无返回值)
delegate void OnDamageHandler();

// 这个委托变量可以"存"符合格式的方法
OnDamageHandler myDelegate;

// 把方法赋给委托
void PlaySound() { Debug.Log("播放音效"); }
myDelegate = PlaySound;  // 注意不加括号

// 调用委托 = 调用它存的方法
myDelegate();  // 输出:"播放音效"

三、Action------最常用的内置委托

Unity 开发中,不需要自己声明 delegate,直接用 C# 内置的 Action 就够了:

类型 说明 示例
Action 无参数、无返回值 游戏结束通知
Action<T> 一个参数、无返回值 传递受伤数值
Action<T1, T2> 两个参数 传坐标、传名字+分数
cs 复制代码
Action onGameOver;              // 无参
Action<float> onDamage;         // 带一个float参数(伤害值)
Action<string, int> onScore;    // 带两个参数(玩家名、分数)

四、event 关键字------给委托加保护锁

光用 Action 有个隐患:任何地方都可以直接调用或清空它。加上 event 关键字,可以限制只有声明它的类能触发,外部只能订阅/取消订阅。

cs 复制代码
// 没有 event:任何脚本都能 GameManager.OnGameOver() 直接触发
public static Action OnGameOver;

// 加了 event:外部只能 += 或 -=,不能直接调用
public static event Action OnGameOver;
操作 有 event 无 event
订阅(+= 允许 允许
取消订阅(-= 允许 允许
从外部触发(() 报错 允许(危险!)
从外部清空(= null 报错 允许(危险!)

五、完整三步走------事件的标准用法

  • 第一步:声明事件(在"广播站"里)
  • 第二步:订阅事件(在"听众"里)
  • 第三步:触发事件(事情发生时)

六、完成一个得分事件委托

那么我们来完成一个小小的实列吧!

cs 复制代码
using UnityEngine;

// ── 得分管理器(发布者)──────────────────
public class ScoreManager : MonoBehaviour
{
    public static event Action<int> OnScoreChanged;  // 携带当前总分

    private static int totalScore = 0;

    // 静态方法,任何地方可以调用
    public static void AddScore(int points)
    {
        totalScore += points;
        OnScoreChanged?.Invoke(totalScore);
        Debug.Log("得分!当前总分:" + totalScore);
    }
}
cs 复制代码
// ── 分数UI(订阅者)──────────────────────
public class ScoreUI : MonoBehaviour
{
    void OnEnable()
    {
        ScoreManager.OnScoreChanged += UpdateScoreDisplay;
    }

    void OnDisable()
    {
        ScoreManager.OnScoreChanged -= UpdateScoreDisplay;
    }

    void UpdateScoreDisplay(int score)
    {
        Debug.Log("UI显示分数:" + score);
        // GetComponent<Text>().text = "分数:" + score;
    }
}

使用方式 :敌人死亡时,调用 ScoreManager.AddScore(10) 即可,不需要引用任何 UI 脚本。

七、一定要记住:订阅了就要取消订阅

cs 复制代码
// 错误写法:只订阅不取消
void Start()
{
    GameManager.OnGameOver += DoSomething;
}
// 这个对象销毁了,但 OnGameOver 还记着它 → 报空引用错误!

// 正确写法:成对出现
void OnEnable()
{
    GameManager.OnGameOver += DoSomething;
}

void OnDisable()
{
    GameManager.OnGameOver -= DoSomething;
}
场景 订阅位置 取消位置
场景内始终存在的对象 Awake / Start OnDestroy
会被禁用/启用的对象 OnEnable OnDisable

八、知识点总结

核心语法 作用
public static event Action OnXxx 声明静态事件
OnXxx?.Invoke() 安全触发事件
OnXxx += 方法名 订阅(开始监听)
OnXxx -= 方法名 取消订阅(停止监听)

今天的内容就到这里! 接下来我将连续更新90天的Untiy教程从基础到一个网络部分,有兴趣的朋友们可以收藏关注,谢谢!如果有疑问,评论区见。

相关推荐
嵌入式小企鹅7 分钟前
国产算力突破、RISC-V车规生态成型、AI编程工具免费化浪潮
学习·开源·ai编程·risc-v·昇腾·deepseek v4
万能菜道人13 分钟前
LVGL9.5版本的基础使用学习
学习
星夜夏空9914 分钟前
STM32单片机学习(1)——keil5安装以及环境部署
stm32·单片机·学习
我想我不够好。17 分钟前
2026.5.8 消防监控学习1hour
学习
Amazing_Cacao18 分钟前
CFCA精品可可产区认证课程风土体系(非洲):穿透浓厚表象,深度解剖精品可可底层的结构张力与多维对抗
笔记·学习·重构
库奇噜啦呼22 分钟前
【iOS】源码学习-类的结构分析
学习·ios·cocoa
小新同学^O^25 分钟前
简单学习--> 神经网络
人工智能·python·神经网络·学习
沉浸式学习ing32 分钟前
音视频内容怎么快速消化?视频转思维导图+精华速览的方法
人工智能·学习·ai·音视频·知识图谱·xmind
楼田莉子3 小时前
仿Muduo的高并发服务器:Http协议模块
linux·服务器·c++·后端·学习
AI机器学习算法9 小时前
《动手学深度学习PyTorch版》笔记
人工智能·学习·机器学习