这节内容继续完善上节 GameManager 的功能:当角色人物死亡时,敌人播放胜利的动画。实现上述功能,我们采取观察者模式。即角色人物死亡时我们通知订阅的观察者,使其做出响应。
1. 添加胜利动画
我们首先为史莱姆添加胜利动画。如图 1 所示,我们为胜利动画再添加一个层。添加层的原因是,胜利动画是 Any State 可以转移的,在其他层的基础上添加,可能会出现层覆盖导致动画不生效的情况。
如果在 Base Layer 基础上添加胜利动画,需要确保动画先“回到” Base Layer。代码逻辑上会有点奇怪。
同时新增 Winning 变量,用于指示胜利动画的转移条件。
2. 实现观察者接口
如代码清单 1 所示,我们实现游戏结束的观察者接口。其中只有一个接口函数,它响应游戏结束时的事件。
如代码清单 2 所示,我们让之前的敌人控制器再继承观察者接口(第 1 行)。实现的接口在第 54 至 57 行,胜利响应的结果就是将状态变更为胜利状态(第 8 行添加)。在状态转移函数中我们实现胜利状态的逻辑(第 44 至 50 行),它将胜利变量设置为真,并取消其余状态变量。设置的变量参与每帧动画变量设置操作(第 37 行)。
3. 实现被观察者
为了方便我们直接让 GameManager 充当被观察者。如代码清单 3 所示,其中提供了添加观察者函数(AddEndGameObserver)、移除观察者函数(RemoveEndGameObserver)以及通知观察者函数(NotifyEndGame)。在 Update() 函数中,进行角色人物的血量跟踪,如果人物血量为 0,则通知观察者对游戏结束做出响应。
最后要考虑的是添加和移除观察者。如代码清单 4 所示,我们将其放置在 OnEnable() 和 OnDisable() 周期。但是实际运行下来添加观察者放在 OnEnable() 周期中,此时 GameManager 的 Awake() 周期都还没被执行到,实例会为空。因此我们先将其放在 Start() 周期中。
单例基于 Unity 上下的周期,当时看着就觉得有定拿捏不稳(因为我对 Unity 的加载逻辑不了解)。
现在也不明白为什么 GameManager 脚本会比 EnemyController 脚本后加载。网上倒是找到可以指定脚本加载顺序的方法,但是这种方式也感觉怪怪的。
实验下来,Unity 各个脚本的初始化加载是同步的:因为我用 AutoResetEvent 想等 GameManager Awake() 执行完毕再返回实例,直接就卡住了。
这块留作问题。
为了验证效果,我们将角色人物的血量设置低一点。如图 2 所示,当角色人物死亡时,史莱姆播放胜利动画。