Reputation: 59
I am trying to implement behaviour trees in untiy and right now i am using a Tick() function for each Node/Composite/Action
What I want to do is in a sequence,for example,is :
Begin the for loop itereating in the list of nodes
Start the tick function on the current node
Wait until the node tick returned Failed/Success
Go to the next node and continue to go to step 1
the issue i am having is with the step 3. I am tring to accomplish this with a coroutine.
the problem is that the assertion is true, the nodeStat var is None. When the WaitFor Seconds is executed, the control gets back to Debug.Assert(nodeStat != Status.None);
line
public class Sequence : Composite
{
protected override void Start()
{
status = Status.Running;
}
Status nodeStat = Status.None;
IEnumerator Recursive(Node nod)
{
while (true)
{
Debug.Log(nod + "is running");
Status status = nod.Tick();
Debug.Log(Time.time + " " + status);
if (status == Status.Running)
{
Debug.Log("start coroutine at : " + Time.time);
yield return new WaitForSeconds(0.2f);
Debug.Log("Comtinue coroutine at : " + Time.time);
}
else
{
Debug.Log("has finished coroutine");
nodeStat = status;
break;
}
}
}
protected override Status Execute()
{
foreach(Node nod in ListaNoduri)
{
GameManager.Instance.StartCoroutine(Recursive(nod));
Debug.Assert(nodeStat != Status.None);
if (nodeStat == Status.Failure)
return Status.Failure;
}
return Status.Success;
}
protected override void Exit()
{
base.Exit();
}
public override Status Tick()
{
//Debug.Log(status);
return base.Tick();
}
}
Upvotes: 1
Views: 144
Reputation: 4061
Consider making Execute()
a coroutine:
IEnumerator Execute()
{
foreach(Node nod in ListaNoduri)
{
//wait for the current node to be done before continuing
yield return GameManager.Instance.StartCoroutine(Recursive(nod));
Debug.Assert(nodeStat != Status.None);
if (nodeStat == Status.Failure){
//do something on failure
yield break;
}
}
//do something on success
}
Upvotes: 1
Reputation: 4283
Remember that Corutine works similar to a thread (asynch execution).
Note that as @akaBase point on comments, the problem is that you are evaluating nodeStat just after calling the Coroutine, you aren't waiting the coroutine to finish.
So you are dispatching multiple asynch calls (one for every Node) but you aren't waiting for all of them.
One approach could be to add an event/delegate/action before your break on Recursive, where you store the result of one Node, and then evaluate on another Coroutine (that should be your current Execute), that if some delegate is false, the result is false.
Something like:
private List<bool> nodeExecutionResultsList = new List<bool>();
private Status statusResult = Status.Failure;
private Action OnAllNodesEnd = null;
protected override void Execute()
{
OnAllNodesEnd = () => { Debug.Log("Every Node Ends!"); };
foreach (Node nod in ListaNoduri)
{
GameManager.Instance.StartCoroutine(Recursive(nod));
}
}
IEnumerator Recursive(Node nod)
{
while (true)
{
Debug.Log(nod + "is running");
Status status = nod.Tick();
Debug.Log(Time.time + " " + status);
if (status == Status.Running)
{
Debug.Log("start coroutine at : " + Time.time);
yield return new WaitForSeconds(0.2f);
Debug.Log("Comtinue coroutine at : " + Time.time);
}
else
{
Debug.Log("has finished coroutine");
nodeStat = status;
//Do the assert NOW, and register on the list the result
Debug.Assert(nodeStat != Status.None);
nodeExecutionResult.Add(nodeStat == Status.Failure);
break;
}
}
}
private IEnumerator EvaluateAllNodes()
{
while(evaluating)
{
//there are the same number of results as nodes
if(nodeExecutionResultsList.Count == ListaNoduri.Count)
{
//check if someone fails
statusResult = (nodeExecutionResult.Contains(false)) ? Status.Failure : Status.Success;
}
}
OnAllNodesEnd?.Invoke();
}
Upvotes: 0