用事件驱动 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 都已经运行过了。
挑战
简单让枪在状态变化时发出通知,并让 HUD 监听。
- 在
Gun.cs中,添加using System;、public event Action<int, int> OnAmmoChanged和public event Action<bool> OnReloading;在注释所说的位置触发它们。 - 在
AmmoHud.cs中,在OnEnable中订阅,在OnDisable中取消订阅:ammoText显示Ammo: 4/6,reloadText只在重新装填时显示。 - 在
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 在线编辑器