在目前的项目中,我们有使用到一个叫Apex Path的插件来做自动寻路的事情,这个插件整体来说还是棒棒哒,完全对得起$65的价格,我在写这篇文章的时候,刚好这货又在做半价促销了,链接在此。
今天先不展开聊这个Apex Path插件了,今天主要是要吐槽一下Behavior Designer插件的增强包Movement Pack,在初次接触到Behavior Designer插件之时,顿时觉得这货能帮忙解决AI行为树的大部分问题,立马买回来尝试了几把,发现真心不错,可以解决很多问题。然后在他们网站上发现还有一个额外的用于处理移动的增强包,果断再次入手。拿回来简单地测试了一番,感觉还不错。但是在后续的开发中,总是会发现一些奇怪的问题,大体的表现就是NPC完全在没有移动到应该移动到的攻击位置就开始攻击了以及类似的问题,而且这个问题是在有多个使用了Behavior Designer控制的NPC出现在场景中的时候才会出现。
这个真心奇了怪了,作为一个朝内程序猿,我一直都奔着崇洋媚外的态度来看待国外程序员大大们,一直认为他们好牛逼好牛逼,然后先入为主地认为是自己用得肯定有问题,多次检查了自己的代码,依然没能找到问题。最终我就只能搞了一个空场景,就放了两个NPC,让NPC做最简单的行为,把Log加上,尼玛最后知道真相的我,眼泪掉下来啊,有木有!
我们来看看Behavior Designer Movement Pack For Apex Path中Patrol脚本中关于Apex中UnitNavigationEventMessage回调的处理。
// Add the waypoint back on the stack when the destination is reached public override void Handle(UnitNavigationEventMessage message) { switch (message.eventCode) { case UnitNavigationEventMessage.Event.WaypointReached: movableAgent.MoveTo(waypoints[waypointIndex].position, true); waypointIndex = (waypointIndex + 1) % waypoints.Length; break; } }
然后我们对比一下Apex Path插件中自带的一个PatrolBehaviour脚本是如何处理这个UnitNavigationEventMessage的回调的。
void IHandleMessage.Handle(UnitNavigationEventMessage message) { if (message.entity != this.gameObject || message.isHandled) { return; } if (message.eventCode == UnitNavigationEventMessage.Event.WaypointReached) { message.isHandled = true; MoveNext(true); } else if (message.eventCode == UnitNavigationEventMessage.Event.DestinationReached) { message.isHandled = true; StartCoroutine(DelayedMove()); } }
细心对比一下,我们就会发现,在后者的处理逻辑中,加入了一个关于这个消息的entity对象是否为当前脚本所绑定的GameObject对象的判断,如果当前接收到的消息不是当前这个GameObject发出的,是不做任何逻辑,直接return的。然后再回过头来看看Behavior Designer Movement Pack For Apex Path中的处理,你就知道问题出在哪儿了。
因为Apex Path的GameServices中的MessageBus实现机制是任何对象都可以通过GameServices.messageBus访问到这个全局静态的MessageBus对象,并且可以通过subscribe和unsubscribe方法来进行消息的订阅和取消订阅。也就因为这个任何MessageBus的全局机制,任意注册了的对象都会收到回调,所以我们需要在处理消息回调的时候自行判断这个消息是否是我们需要的,或者说需要判断这个消息是否是发给自己的。MessageBus的机制就是一个广播,只要有任何一个对象触发了事件,注册了消息的所有对象都会收到广播,所以需要收到广播消息的对象自行甄别这货是否是发给自己的。
Behavior Designer Movement Pack For Apex Path脚本中对于回调的处理显然是忽略了这个甄别的过程,所以就会出现一些完全不在意料之中的事情,一切都能解释得通了。
Opsive的这个团队在Behavior Designer的工作确实非常惊艳,让人竖大拇哥,但是在这种问题上犯错误只能让我觉得这是实习生做的,或者就是完全没有真正地使用过Apex Path,然后就直接推出了一个似是而非的Movement增强包,我是很不以为然的。今天看到这个Movement的插件还升级了,Update需要花费$10,鉴于这个问题,我想暂时还是不升级了,虽然我很想看看他们升级的版本中是否已经修复了这个低级错误,等我今天闲下来了,我得给他们发个邮件问问。
给Opsive团队发过邮件了,邮件回复说是新的版本中已经加入这个判断了,问题已经修复,回复内容如下:
Hello,
Thank you for letting me know. Have you imported the most recent version of the Apex Path integration off of the integrations page? This integration doesn’t include an ApexPathSteeringBase file. It only includes ApexPathMovement as the base class. Within that class there is a check for the GameObject:
public virtual void Handle(UnitNavigationEventMessage message)
{
if (!message.entity.Equals(gameObject)) {
return;
}
switch (message.eventCode) {
case UnitNavigationEventMessage.Event.DestinationReached:
arrived = true;
break;
}
}
I am not setting message.isHandled to true because all I am doing when destination is reached is setting a flag so a new destination hasn’t been set yet.
Thank you,
Justin