Sto tentando di mettere la testa intorno agli alberi comportamentali, quindi sto aggiungendo del codice di prova. Una cosa con cui sto lottando è come prevenire un nodo attualmente in esecuzione quando arriva qualcosa di prioritario.
Considera il seguente albero di comportamento semplice e fittizio per un soldato:
Supponiamo che sia passato un certo numero di zecche e non ci siano nemici nelle vicinanze, il soldato era in piedi sull'erba, quindi il nodo Siediti è selezionato per l'esecuzione:
Ora l' azione Sit down richiede tempo per essere eseguita perché è presente un'animazione da riprodurre, quindi ritorna Running
come suo stato. Passano un segno di spunta o due, l'animazione è ancora in esecuzione, ma il nemico è vicino? trigger nodo condizione. Ora dobbiamo anticipare il nodo Sedere al più presto in modo da poter eseguire il nodo Attacco . Idealmente, il soldato non finiva nemmeno di sedersi: avrebbe potuto invertire la direzione dell'animazione se avesse appena iniziato a sedersi. Per un maggiore realismo, se ha superato un punto critico nell'animazione, potremmo invece scegliere di lasciarlo finire di sedersi e poi stare di nuovo in piedi, o forse farlo inciampare nella fretta di reagire alla minaccia.
Prova come potrei, non sono stato in grado di trovare una guida su come gestire questo tipo di situazione. Tutta la letteratura e i video che ho consumato negli ultimi giorni (ed è stato molto) sembrano aggirare questo problema. La cosa più vicina che sono stato in grado di trovare è stato questo concetto di reimpostazione dei nodi in esecuzione, ma ciò non offre a nodi come Sit down la possibilità di dire "ehi, non ho ancora finito!"
Ho pensato forse di definire un metodo Preempt()
o Interrupt()
sulla mia Node
classe base . Diversi nodi possono gestirlo come meglio credono, ma in questo caso proveremmo a rimettere in piedi il soldato il prima possibile e poi a tornare Success
. Penso che questo approccio richiederebbe anche che la mia base Node
abbia il concetto di condizioni separatamente rispetto ad altre azioni. In questo modo, il motore può controllare solo le condizioni e, se passano, impedire qualsiasi nodo attualmente in esecuzione prima di iniziare l'esecuzione delle azioni. Se questa differenziazione non fosse stabilita, il motore dovrebbe eseguire i nodi in modo indiscriminato e potrebbe quindi innescare una nuova azione prima di anticipare quella in esecuzione.
Per riferimento, di seguito sono riportate le mie classi di base correnti. Ancora una volta, questo è un picco, quindi ho cercato di mantenere le cose il più semplice possibile e aggiungere complessità solo quando ne ho bisogno e quando lo capisco, che è ciò con cui sto lottando in questo 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>
{
}
Qualcuno ha qualche intuizione che potrebbe guidarmi nella giusta direzione? Il mio pensiero è nella giusta direzione o è ingenuo quanto temo?
Stop()
callback prima di uscire dai nodi attivi)