nickk2002
nickk2002

Reputation: 59

Pause execution of a function using a coroutine untiy

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 :

  1. Begin the for loop itereating in the list of nodes

  2. Start the tick function on the current node

  3. Wait until the node tick returned Failed/Success

  4. 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

Answers (2)

Pluto
Pluto

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

Lotan
Lotan

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

Related Questions