Unity3D学习笔记(6) 智能巡逻兵


代码仓库


发布与订阅模式

先简单了解一下发布与订阅模式。
这里写图片描述
发布与订阅模式用来作为中间人,一个把发布者和订阅者架接在一起的代理。发布者是当完成某些过程的时候触发事件的对象,订阅者是希望当发布者发布的时候希望被通知的对象。

优势

  • 松耦合
    发布者与订阅者独立。

  • 可扩展性

  • 更干净的设计

  • 灵活性

  • 容易测试

缺点

发布/订阅模式最大的有点是解耦,但同时也是最大的缺点:

  • 中间人也许不会通知系统消息传送的状态。所以我们无法知道消息传送是成功的还是失败的。

  • 发布者不知道订阅者的状态。

  • 随着订阅者和发布者数量的增加,不断增加的消息传送回导致架构的不稳定,容易在负载大的时候出问题。

  • 订阅者更可能得到的恶意消息。

  • 需要中间人,消息规范和相关的规则会给系统增加一些复杂度。


描述

  • 创建一个地图包含多个区域(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

猜你喜欢

转载自blog.csdn.net/z_j_q_/article/details/80244539