发布与订阅模式
先简单了解一下发布与订阅模式。
发布与订阅模式用来作为中间人,一个把发布者和订阅者架接在一起的代理。发布者是当完成某些过程的时候触发事件的对象,订阅者是希望当发布者发布的时候希望被通知的对象。
优势
松耦合
发布者与订阅者独立。可扩展性
更干净的设计
灵活性
容易测试
缺点
发布/订阅模式最大的有点是解耦,但同时也是最大的缺点:
中间人也许不会通知系统消息传送的状态。所以我们无法知道消息传送是成功的还是失败的。
发布者不知道订阅者的状态。
随着订阅者和发布者数量的增加,不断增加的消息传送回导致架构的不稳定,容易在负载大的时候出问题。
订阅者更可能得到的恶意消息。
需要中间人,消息规范和相关的规则会给系统增加一些复杂度。
描述
- 创建一个地图包含多个区域(plane)和若干巡逻兵(patrol)(使用动画);
- 巡逻兵在设定区域(与plane的距离小于2.8)内感知到玩家(hero),会自动追击玩家;
- 失去玩家目标后,继续巡逻;
- 计分:玩家每次甩掉一个巡逻兵计一分(即走出该巡逻兵的设定范围),与巡逻兵碰撞游戏结束。
设计
场景的管理
导演类SSDirector、用户交互类UserGUI、场记类SceneController
(具体作用参见牧师与魔鬼(P&D) 第一版(基础 MVC 实现))动作的管理
动作基类SSAction、动作管理基类SSActionManager、实战动作管理CCActionManager
(具体作用参见牧师与魔鬼(P&D) 第二版(添加动作管理))
巡逻兵追击玩家动作CCCatchAction、角色正常移动动作CCMoveAction、角色数据记录(作为游戏组件)MoveData场记SceneController请了记分员Scorekeeper(图中未标出)、巡逻兵管理员(工厂模式生成巡逻兵)PatrolFactory
玩家控制器HeroController、巡逻兵控制器PatrolController、摄像头控制器CameraFlow
发布者HeroController
public class HeroController : MonoBehaviour{
public delegate void ScoreKeep();
//这里的scoreKeep是发布者
public static event ScoreKeep scoreKeep;
...
void FixedUpdate() {
//判定分数
if (scoreKeep != null) {
scoreKeep ();
...
}
}
}
SceneController中添加订阅者
public class SceneController : MonoBehaviour, ISceneController, IUserAction {
...
void OnEnable() {
//这里的scorekeeper.judge是订阅者
HeroController.scoreKeep += scorekeeper.judge;
}
...
}
订阅者scorekeeper.judge
public class Scorekeeper {
...
//寻找plane数组中与玩家距离小于2.8的plane,就认为玩家在这个plane中,如果这个plane编号与玩家<MoveData>组件记录的plane编号不同,分数加一,并更新玩家<MoveData>中的plane编号。如果plane数组中没有与玩家距离小于2.8的plane,就将玩家<MoveData>的plane编号设为-1。
public void judge() {
GameObject[] plane = sceneController.plane;
for (int i = 0; i < plane.Length; ++i) {
if (Vector3.Distance (plane [i].transform.position, sceneController.hero.transform.position) <= 2.8) {
if (i != sceneController.hero.GetComponent<MoveData> ().planeNum) {
sceneController.hero.GetComponent<MoveData> ().planeNum = i;
++score;
Debug.Log (score);
}
return;
}
}
//处于交界
sceneController.hero.GetComponent<MoveData>().planeNum = -1;
}
}
动画状态机(Animator)
这里给每个游戏角色都用了动画状态机,默认状态是idle,通过一个bool值决定idle和run的切换,通过一个trigger任何状态下切换到death。
这里注意每个切换都有一个Has Exit Time勾选项,如果勾选了该项,在动画转换时会等待当前动画播放完毕才会转换到下一个动画,如果当前动画是循环动画会等待本次播放完毕时转换,所以对于需要立即转换动画的情况时记得要取消勾选。
遇到的小问题
物体加了刚体(Rigid Body)和碰撞器(Collider)之后,悬空了。应该要适当调整碰撞器的大小尽可能与物体接近。
项目地址
https://gitee.com/Ernie1/unity3d-learning/tree/hw6/hw6
参考
https://segmentfault.com/a/1190000003963671
https://www.cnblogs.com/lovesong/p/5272752.html