转自:http://www.gameres.com/thread_256279_1_1.html
GameRes发布,文/paladin_t,转载请注明GameRes和作者
追击、群聚是极常用到的敌人行为模式。靠拢玩家位置的移动叫追击;群聚指一批敌人集体采用统一的行动,就像一群群沙丁鱼一样。群聚敌人往往配合追击来对玩家施加压力,因此本文隐含用追击配合做群聚示例。
如果群聚行为的敌人没有对群体内其他敌人的感知能力,将造成一种很奇葩的现象:扎堆。我见过很多游戏可以控制主角兜来兜去让群聚敌人集中到一堆,或者你不动他们自己就会走成一条直线。
<ignore_js_op>
这很烦人,玩家会认为敌人非常傻,如果玩家要采取躲避敌人的策略,挑战性的要素只剩下对走位的决策和移动速度的比拼。更恶心的是往往敌人比你挨着我我挨着你还要亲密,甚至全部叠加集中到一个点上。
<ignore_js_op>
喂,这太蠢了点吧。这是最raw最simple的实现方式,谁都能想出来。
<ignore_js_op>
敌人始终单纯的趋向主角(也许还会做出对主角移动预判的计算,但不在本文讨论范畴内),在主角从a点移动到b点的过程中敌人始终保持这样的趋向性。
<ignore_js_op>
用彩色曲线对例图中敌人的移动轨迹做描绘,也许是这个样子:
<ignore_js_op>
<ignore_js_op>
看到了吧,敌人群体的范围越来越小了。做为开发者兼玩家,这种prototype级别的做法会给我留下此游戏完成度很低的印象。
物理引擎在现代游戏中已经不是什么稀奇物件了,不管3D游戏还是2D游戏,都能找到物理机制和游戏表达结合的非常好的例子,某些游戏走的更远,对物理机制利用的更深入,物理就是这种游戏的全部乐趣。如果你的游戏中恰巧有一个可以随手拿来利用一下的物理引擎,也许你可以用它来防止敌人们过于亲密,比如给每个敌人一个比渲染包围体略大一些的防撞车碰撞体。
<ignore_js_op>
这看起来就像敌人间互相有了斥力场,酷!大功告成,万事大吉!
你得承认凡是做游戏鬼点子多的人才不会满足于把方案思考到能用就好的程度。我不是排斥使用物理引擎,相反我非常喜欢物理机制,我曾给我的首款App定制实现了一个物理引擎,效率良好,运转稳定,最关键的是这个精简版的物理引擎功能不多不少刚刚好满足我的需求,完全没必要用更加高大上的库。对于防止群聚行为敌人扎堆这件事我不想用物理引擎也是因为:完全没必要用啊。某些RTS不同兵种间有搭配性玩法,需要不同兵种的密度均衡,如果用物理引擎反而很难做。询问了几个同行,似乎不用物理引擎对这事都没什么辙,没办法,又得自己动手了。让我们来看看如果不使用物理引擎自己处理敌人间的“推搡”是怎样的情况。
<ignore_js_op>
如果有两只敌人,只需要做1次计算让一只跟另一只保持适当距离就行了;如果有3只敌人,需要做3次这样的计算;如果有4只,需要算6次;如果有5只呢,要算10次……这种算法的复杂度是O(n(n-1)/2),可想而知敌人数量多了这将成为CPU的灾难。经验告诉我,复杂度中有高达形如“n乘以n”这样的因子的算法都是要不得的,需要想办法优化或做折衷。
思考优化方案从现实切入吧,我们每个人都有在方阵队伍里齐步走的经历,左右脚步调的统一依靠“一二一”口号和踏步声音做同步数据源;在方阵中相对位置的固定不变我们不需要也不可能通过获取方阵中其他所有人的位置信息做反馈调整,事实上我们只是瞄着视线范围内与我们临近的同学来做模糊判定的。先考虑某一只敌人不管别的敌人同学自己单独行动的行为:
<ignore_js_op>
这很容易,只需要朝着主角的位置奔去就行了,我们不妨把例图中优先计算行动的敌人1当成走方阵时的排头同学,他们只需要走好自己的就行了。
取敌人1和主角位置连线(长度为R的)线段上某一点,(比如不妨定R为线段长度的一半),从主角位置到这一点(以R为半径)做一个圆,好歹我们可以知道敌人群体的总数量有几只,把这个圆平均划分为n份,每一只都朝着相对应的划分的端点袭去,嗯,也许还得给每只敌人做些移动方向的微调;由于敌人1距主角越来越近,R是越来越小的,这个圆也会越来越向主角收缩,于是我们神奇的达成了敌人的这一套行为:
1. 群体移动趋势一致统一
2. 个体间在圆收缩到很小之前不会靠的非常近
<ignore_js_op>
圆只需要划分一次,某一只敌人只需要知道自己是第几号就可以计算出速度向量,看,复杂度一下降低到了O(n),单次计算中的向量计算也锐减。
再引申谈一点点,群聚敌人分成若干堆怎样实现呢,不妨这样来看,每一堆算做一个聚集群体应用上文算法,再把某一群体看做一个整体,若干个这样的群体同理应用群聚算法(或其他行为模式)。
<ignore_js_op>
本文算法代码可在github获取。