J'essaie de me familiariser avec les arbres de comportement, alors j'enrichis du code de test. Une chose avec laquelle je me bats est de savoir comment préempter un nœud en cours d'exécution lorsque quelque chose de priorité plus élevée se présente.
Considérez l'arbre de comportement simple et fictif suivant pour un soldat:
Supposons qu'un certain nombre de tiques se soient écoulées et qu'il n'y avait pas d'ennemi à proximité, le soldat se tenait sur l'herbe, donc le nœud S'asseoir est sélectionné pour l'exécution:
L' action S'asseoir prend maintenant du temps à exécuter car il y a une animation à jouer, elle revient donc Running
comme son statut. Une ou deux tiques passent, l'animation est toujours en cours, mais l' ennemi est-il proche? déclencheurs de noeud de condition. Maintenant, nous devons préempter le nœud Sit as ASAP afin que nous puissions exécuter le nœud Attack . Idéalement, le soldat ne finirait même pas de s'asseoir - il pourrait plutôt inverser sa direction d'animation s'il commençait juste à s'asseoir. Pour plus de réalisme, s'il a dépassé un certain point de basculement dans l'animation, nous pourrions plutôt choisir de le laisser finir de s'asseoir puis de se relever, ou peut-être de le faire trébucher dans sa hâte de réagir à la menace.
J'ai beau essayer, je n'ai pas pu trouver de conseils sur la façon de gérer ce genre de situation. Toute la littérature et les vidéos que j'ai consommées ces derniers jours (et ça fait beaucoup) semblent contourner ce problème. La chose la plus proche que j'ai pu trouver est ce concept de réinitialisation des nœuds en cours d'exécution, mais cela ne donne pas aux nœuds comme Sit Down une chance de dire "hé, je n'ai pas encore fini!"
J'ai pensé à définir peut-être une méthode Preempt()
ou Interrupt()
sur ma Node
classe de base . Différents nœuds peuvent le gérer comme bon leur semble, mais dans ce cas, nous essayons de remettre le soldat sur ses pieds dès que possible, puis de revenir Success
. Je pense que cette approche exigerait également que ma base Node
ait le concept de conditions séparément des autres actions. De cette façon, le moteur ne peut vérifier que les conditions et, si elles réussissent, préempter tout nœud en cours d'exécution avant de démarrer l'exécution des actions. Si cette différenciation n'était pas établie, le moteur devrait exécuter les nœuds sans discernement et pourrait donc déclencher une nouvelle action avant de préempter celle en cours d'exécution.
Pour référence, voici mes classes de base actuelles. Encore une fois, c'est un pic, j'ai donc essayé de garder les choses aussi simples que possible et d'ajouter de la complexité uniquement lorsque j'en ai besoin et quand je le comprends, ce avec quoi je me bats en ce moment.
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>
{
}
Quelqu'un at-il une idée qui pourrait m'orienter dans la bonne direction? Est-ce que ma pensée va dans le bon sens, ou est-ce aussi naïf que je le crains?
Stop()
rappel avant de quitter les nœuds actifs)