躲避游戏(智能巡逻兵)
概述
这次的游戏是基于智能巡逻兵作业的”困难”躲避游戏。水平有限,UI 较为简陋。
圆球为玩家,通过上下左右方向键控制;立方体为巡逻兵,当玩家出现在侦察范围会自动追击,追到则失败。
玩家摆脱巡逻兵,巡逻兵变色,依旧巡逻,但不会再次追逐玩家。
多次点击开始按钮可以生成更多巡逻兵。
巡逻兵的巡逻路线采用随机生成矩形,然后再在边上随机选点构成多边形,作为巡逻路线。
制作巡逻兵
首先,给一个立方体挂载一个 spehere collider,并设置半径为 10 和勾选 trigger 作为触发器,用来探测玩家。
然后编写其模板代码。
每个巡逻兵,需要发现玩家的标志变量,存储巡逻路径的变量,追踪玩家动作,巡逻动作。
巡逻兵平常进行巡逻动作,按照存储的巡逻路径,按顺序巡逻。当发现玩家,停止动作,执行追逐玩家动作。如果被甩掉,则变色,并继续执行巡逻动作,但不会再次追逐玩家。
代码中的注释可以辅助理解。
1 | // flag for if dropped off by player |
制作巡逻路径
巡逻路径,采取生成随机点列表作为路径,让巡逻兵从一个点,运动到另一个点,作为巡逻动作。
关于随机点生成,我们首先生成一个随机的矩形左下角点和边长,然后算出其余三个顶点。接着在它的四条边,每条边随机选取一个点,作为巡逻路径点,这样可以得到一个随机的凸四边形。
这里支持生成凸多边形,只要在边上随机取点时,每次多取一个,然后按顺序加入巡逻列表,那么最多可以生成凸八边形。
这个巡逻路径,耗时比较久,也就没时间考虑 UI 了。
1 | public float positionRange = 20.0f; |
制作巡逻兵工厂
工厂模式,不是什么陌生的事情了。这里只有巡逻兵的工厂,因为玩家只有一个,所以也只需要给巡逻兵配备工厂了。没有就从闲置资源获得,不够就生产,大致就是这个逻辑了。
1 | private GeneratePatrolPath pathGenerator; |
发布订阅者模式
对一个事件做出反应的东西往往有很多个,那个时候,将事件的感知和处理逻辑写在一起,是一种很难以维护的方式。
比较典型一点的构造应当是:多个订阅者拥有自己的对同一类事件的各自的事件处理逻辑,然后在每个订阅者实例化的时候,向一个专门负责事件发布与接收的地方注册自己的事件处理函数(回调函数),然后发布者在感知并产生事件之后,向同样的负责事件分发的地方传送自己的事件,然后该事件依次进入不同订阅者的逻辑中,实施其自己的工作。
因为对于这个游戏来说,只有巡逻兵对事件发生感知并发生事件,事件逻辑已经足够简单,所以发布者和订阅者都可以简化成一个:发布者是每个巡逻兵,订阅者是SceneController。所以在通过这部分代码理解发布订阅模式时,多方订阅的部分可能需要自己继续想象。
基于本次游戏的逻辑简单性,简化了事件分发的逻辑,直接用了一个static类把事件处理逻辑写在了上面,实际上并不完全是上面说的结构。
1 | public static void ChasePlayer(IPatrol patrol, Transform player) { |
制作玩家
给一个圆球挂载碰撞器和刚体并做成预制。
在这里,我要提出一个 de 了两天的 bug! 碰撞发生的条件是双方具有碰撞器,且一方具有刚体,触发的条件是双方具有碰撞器,一方具有刚体,一方的碰撞器勾选了 isTrigger。我一开始就按照这个去设计的巡逻兵与玩家的预制。但是,触发可以触发,碰撞却完全没有被检测到。在 onCollisionEnter中的输出语句也没有执行,甚至两个物体撞在了一起,玩家”进入”了巡逻兵内部。查找了很多资料,比如连续检测,连续动态检测,降低速度等等,都没有解决这个问题。
玩家的逻辑代码很简单,主要是移动函数:
1 | void FixedUpdate() { |
杂项
剩下的都是以前写过的代码,就不一一贴出来了。具体前往github查看。