Приостановить выполнение функции с помощью сопрограммы - PullRequest
0 голосов
/ 11 марта 2020

Я пытаюсь реализовать деревья поведения в untiy, и сейчас я использую функцию Tick () для каждого узла / составного / действия.

То, что я хочу сделать, это, например, последовательность: :

  1. Начать итерацию для l oop в списке узлов

  2. Запустить функцию тиков на текущем узле

  3. Дождитесь, пока отметка узла вернет Failed / Success
  4. Go к следующему узлу, и продолжайте с go до шага 1

    проблема, с которой я столкнулся с шагом 3. Я пытаюсь выполнить sh с сопрограммой.

проблема в том, что утверждение верно, переменная nodeStat имеет значение None. Когда секунд ожидания для выполнения выполняется, элемент управления возвращается к 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();
    }
}

Ответы [ 2 ]

1 голос
/ 11 марта 2020

Рассмотрите возможность создания Execute() сопрограммы:

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
}
0 голосов
/ 11 марта 2020

Помните, что Corutine работает аналогично потоку (асинхронное выполнение).

Обратите внимание, что, поскольку @akaBase указывает на комментарии, проблема заключается в том, что вы оцениваете nodeStat сразу после вызова Coroutine, вы не ждете сопрограмма к fini sh.

Таким образом, вы отправляете несколько асинхронных вызовов (по одному для каждого узла), но не ожидаете их всех.

Одним из подходов может быть добавление событие / делегат / действие до вашего перерыва в Recursive, где вы сохраняете результат одного узла, а затем оцениваете на другом сопрограмме (который должен быть вашим текущим исполнением), что, если какой-то делегат ложен, результат ложен.

Что-то вроде:

    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();
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...