Menu
CoddyTech

用事件驱动 UI

本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 15 课,共 64 课。

使用事件时,HUD 会跟随游戏变化,而游戏本身并不了解 HUD。Health 负责管理生命值,并会发出两种通知:生命值发生了变化,以及生命值降到了 0。它没有用于血条、文本或游戏结束画面的字段,因此同一个组件既能用于玩家,也能用于没有 HUD 的敌人,还能用于测试场景:

public class Health : MonoBehaviour
{
    public int maxHp = 100;
    public event Action<int, int> OnChanged;
    public event Action OnDied;
    public int Hp { get; private set; }

    void Awake()
    {
        Hp = maxHp;
    }

    public void TakeDamage(int amount)
    {
        if (Hp == 0) return;
        Hp = Mathf.Max(Hp - amount, 0);
        OnChanged?.Invoke(Hp, maxHp);
        if (Hp == 0) OnDied?.Invoke();
    }
}
// HealthBar.cs,位于 HUD 上
public Health health;
public Image bar;

void OnEnable() { health.OnChanged += Show; }
void OnDisable() { health.OnChanged -= Show; }

void Start()
{
    Show(health.Hp, health.maxHp);
}

void Show(int hp, int max)
{
    bar.fillAmount = (float)hp / max;
}

血条只会在处于激活状态时监听事件,这由 OnEnable 和 OnDisable 配对实现。事件会传递两个数值,因此 Show 不需要从 Health 获取其他信息。GameOverScreen 会订阅 OnDied 来显示面板,受伤音效则会在自己的脚本中订阅 OnChanged,Health 不需要为它们中的任何一个做出改变。Hp { get; private set; } 允许 HUD 读取生命值,但不能修改它。

事件会报告变化,因此在第一次受到攻击之前,血条什么也没听到。它会在 Start 中绘制一次当前生命值,而不是在 OnEnable 中绘制:Unity 会逐个对象调用 Awake,然后再调用 OnEnable,但不保证调用顺序。因此,血条的 OnEnable 可能会在 Health 的 Awake 设置 Hp 之前运行,这样血条就会显示 0。等到任何 Start 运行时,所有 Awake 都已经运行过了。

challenge icon

挑战

简单

让枪在状态变化时发出通知,并让 HUD 监听。

  1. 在 Gun.cs 中,添加 using System;、public event Action<int, int> OnAmmoChanged 和 public event Action<bool> OnReloading;在注释所说的位置触发它们。
  2. 在 AmmoHud.cs 中,在 OnEnable 中订阅,在 OnDisable 中取消订阅:ammoText 显示 Ammo: 4/6,reloadText 只在重新装填时显示。
  3. 在 Start 中,各绘制一次 gun.Ammo 和 gun.Reloading。

按下“开始”,把枪里的子弹打光两次。然后点击“检查”。

亲手试试

using UnityEngine;
using TMPro;

public class AmmoHud : MonoBehaviour
{
    public Gun gun;
    public TMP_Text ammoText;
    public GameObject reloadText;
}

Unity 游戏架构中的所有课

自己练习: Unity 在线编辑器