我试图绕过行为树,所以我要花一些测试代码。我正在努力解决的一件事是,当出现更高优先级的东西时,如何抢占当前正在运行的节点。
考虑一下以下简单的士兵行为树:
假设经过了一些滴答声并且附近没有敌人,那名士兵正站在草地上,因此选择了“ 坐下”节点以执行该命令:
现在,“ 坐下”动作需要时间才能执行,因为要播放动画,因此它会返回Running
其状态。一两个勾号过去了,动画仍在运行,但是敌人在附近吗?条件节点触发器。现在,我们需要尽快抢占Sit down节点,以便执行Attack节点。理想情况下,士兵甚至都不会坐下来–如果他只是开始坐下,他可能会反转动画方向。为了增加逼真度,如果他已经超过动画中的临界点,我们可以选择让他完成坐下然后再站起来,或者让他跌跌撞撞地对威胁做出反应。
尽我所能,我一直无法找到有关如何处理这种情况的指南。过去几天(而且很多)我消费的所有文献和视频似乎都绕过了这个问题。我能找到的最接近的东西是重置运行中的节点的概念,但这并不能使像Sit down这样的节点有机会说“嘿,我还没有完成!”
我想到也许在我的基类上定义一个Preempt()
or Interrupt()
方法Node
。不同的节点可以按照自己认为合适的方式进行处理,但是在这种情况下,我们将尝试让士兵尽快站起来然后返回Success
。我认为这种方法还需要我的基地Node
将条件的概念与其他行动分开。这样,引擎只能检查条件,如果条件通过,则可以在开始执行动作之前先抢占任何当前正在执行的节点。如果未建立这种区分,则引擎将需要不加选择地执行节点,因此可能会在抢占正在运行的节点之前触发新的操作。
供参考,以下是我当前的基类。再说一次,这是一个高峰,所以我试图使事情尽可能简单,仅在需要时以及当我理解时才增加复杂性,这就是我现在正在努力的目标。
public enum ExecuteResult
{
// node needs more time to run on next tick
Running,
// node completed successfully
Succeeded,
// node failed to complete
Failed
}
public abstract class Node<TAgent>
{
public abstract ExecuteResult Execute(TimeSpan elapsed, TAgent agent, Blackboard blackboard);
}
public abstract class DecoratorNode<TAgent> : Node<TAgent>
{
private readonly Node<TAgent> child;
protected DecoratorNode(Node<TAgent> child)
{
this.child = child;
}
protected Node<TAgent> Child
{
get { return this.child; }
}
}
public abstract class CompositeNode<TAgent> : Node<TAgent>
{
private readonly Node<TAgent>[] children;
protected CompositeNode(IEnumerable<Node<TAgent>> children)
{
this.children = children.ToArray();
}
protected Node<TAgent>[] Children
{
get { return this.children; }
}
}
public abstract class ConditionNode<TAgent> : Node<TAgent>
{
private readonly bool invert;
protected ConditionNode()
: this(false)
{
}
protected ConditionNode(bool invert)
{
this.invert = invert;
}
public sealed override ExecuteResult Execute(TimeSpan elapsed, TAgent agent, Blackboard blackboard)
{
var result = this.CheckCondition(agent, blackboard);
if (this.invert)
{
result = !result;
}
return result ? ExecuteResult.Succeeded : ExecuteResult.Failed;
}
protected abstract bool CheckCondition(TAgent agent, Blackboard blackboard);
}
public abstract class ActionNode<TAgent> : Node<TAgent>
{
}
有谁能指导我朝正确的方向发展?我的想法是否正确,还是像我担心的那样幼稚?
Stop()
在退出活动节点之前调用一些回调)