Estoy tratando de entender los árboles de comportamiento, así que estoy agregando un código de prueba. Una cosa con la que estoy luchando es cómo evitar un nodo actualmente en ejecución cuando aparece algo de mayor prioridad.
Considere el siguiente árbol de comportamiento simple y ficticio para un soldado:
Supongamos que ha pasado un número de garrapatas y que no había ningún enemigo cerca, el soldado estaba de pie sobre la hierba, por lo que se selecciona el nodo Sentarse para la ejecución:
Ahora la acción de sentarse toma tiempo para ejecutarse porque hay una animación para reproducir, por lo que regresa Running
como su estado. Pasa una o dos marcas, la animación aún se está ejecutando, pero ¿el enemigo está cerca? desencadenantes de nodo de condición. Ahora tenemos que adelantarnos al nodo Sit down lo antes posible para que podamos ejecutar el nodo Attack . Idealmente, el soldado ni siquiera terminaría de sentarse; en cambio, podría invertir su dirección de animación si solo comenzara a sentarse. Para mayor realismo, si ha pasado algún punto de inflexión en la animación, podríamos optar por dejar que termine de sentarse y luego ponerse de pie nuevamente, o tal vez hacer que se apresure a reaccionar ante la amenaza.
Por más que lo intenté, no he podido encontrar orientación sobre cómo manejar este tipo de situación. Toda la literatura y videos que he consumido en los últimos días (y ha sido mucho) parecen eludir este problema. Lo más cercano que he podido encontrar ha sido este concepto de restablecer los nodos en ejecución, pero eso no le da a los nodos como Sit sentarse la oportunidad de decir "¡hey, aún no he terminado!"
Pensé en quizás definir un método Preempt()
o Interrupt()
en mi Node
clase base . Diferentes nodos pueden manejarlo como mejor les parezca, pero en este caso intentaremos que el soldado vuelva a ponerse de pie lo antes posible y luego regrese Success
. Creo que este enfoque también requeriría que mi base Node
tenga el concepto de condiciones por separado de otras acciones. De esa forma, el motor solo puede verificar las condiciones y, si pasan, evitar cualquier nodo que se esté ejecutando actualmente antes de comenzar la ejecución de las acciones. Si no se estableciera esta diferenciación, el motor necesitaría ejecutar nodos indiscriminadamente y, por lo tanto, podría desencadenar una nueva acción antes de adelantarse a la ejecución.
Como referencia, a continuación están mis clases base actuales. Nuevamente, esto es un pico, así que he intentado mantener las cosas lo más simples posible y solo agrego complejidad cuando lo necesito, y cuando lo entiendo, que es con lo que estoy luchando en este momento.
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>
{
}
¿Alguien tiene alguna idea que pueda guiarme en la dirección correcta? ¿Mi pensamiento está en la línea correcta, o es tan ingenuo como temo?
Stop()
devolución de llamada antes de salir de los nodos activos)