Próbuję omijać drzewa zachowań, więc dodam kod testowy. Jedną z rzeczy, z którymi się zmagam, jest sposób na zapobieganie obecnie działającemu węzłowi, gdy pojawia się coś o wyższym priorytecie.
Rozważ następujące proste, fikcyjne drzewo zachowań dla żołnierza:
Załóżmy, że minęła pewna liczba tyknięć i nie było w pobliżu wroga, żołnierz stał na trawie, więc do wykonania wybrano węzeł Usiądź :
Teraz akcja Usiądź zajmuje trochę czasu, ponieważ do odtworzenia jest animacja, więc powraca Running
jako jej status. Przechodzi tyknięcie lub dwa, animacja nadal działa, ale wróg jest blisko? wyzwala węzeł warunku. Teraz musimy jak najszybciej zatrzymać węzeł Usiądź, abyśmy mogli wykonać węzeł Ataku . Idealnie byłoby, gdyby żołnierz nie skończył nawet siadania - zamiast tego mógłby odwrócić kierunek animacji, gdyby tylko zaczął siadać. Dla większego realizmu, jeśli przekroczy on punkt krytyczny w animacji, moglibyśmy zamiast tego pozwolić mu skończyć siadanie, a następnie stanąć ponownie, lub może sprawić, że potknie się w pośpiechu, aby zareagować na zagrożenie.
Próbowałem, jak mogłem, ale nie byłem w stanie znaleźć wskazówek, jak poradzić sobie z tego rodzaju sytuacją. Cała literatura i filmy, które zużyłem w ciągu ostatnich kilku dni (a było ich dużo), wydają się omijać ten problem. Najbliższą rzeczą, jaką udało mi się znaleźć, była koncepcja resetowania uruchomionych węzłów, ale nie daje to takim węzłom, jak Sit, szansy na powiedzenie „hej, jeszcze nie skończyłem!”
Pomyślałem o zdefiniowaniu metody Preempt()
lub Interrupt()
metody w mojej Node
klasie bazowej . Różne węzły poradzą sobie z tym, jak uznają za stosowne, ale w tym przypadku postaramy się przywrócić żołnierza na nogi JAK NAJSZYBCIEJ, a następnie wrócić Success
. Myślę, że takie podejście wymagałoby również, aby moja baza Node
miała pojęcie warunków oddzielnie od innych działań. W ten sposób silnik może sprawdzać tylko warunki i, jeśli przejdą, zablokować aktualnie wykonywany węzeł przed rozpoczęciem wykonywania akcji. Gdyby to zróżnicowanie nie zostało ustalone, silnik musiałby wykonywać węzły bez rozróżnienia, a zatem mógłby uruchomić nowe działanie przed zablokowaniem działającego.
Dla odniesienia poniżej są moje obecne klasy podstawowe. Ponownie, jest to skok, więc starałem się utrzymać rzeczy tak proste, jak to możliwe i dodawać złożoności tylko wtedy, gdy ich potrzebuję i kiedy to rozumiem, z czym obecnie mam problem.
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>
{
}
Czy ktoś ma wgląd, który mógłby mnie poprowadzić we właściwym kierunku? Czy moje myślenie jest prawidłowe, czy obawiam się tak naiwności?
Stop()
oddzwanianie przed