Разъяснение с использованием замыканий в сопрограммах IEnumerator - PullRequest
0 голосов
/ 08 декабря 2018

У меня есть 2 сопрограммы IEnumerator, которые достаточно похожи, чтобы их можно было комбинировать:

A:

IEnumerator CountDown_A() {
  timeLeft_A = totalTime_A;
  while (timeLeft_A > 0) {
    yield return new WaitForSeconds(1); 
    timeLeft_A--;
  }
}

B:

IEnumerator CountDown_B() {
  timeLeft_B = totalTime_B;
  while (timeLeft_B > 0) {
    yield return new WaitForSeconds(1); 
    timeLeft_B--;
  }
}

Только 2 различияпеременные totalTime_A против totalTime_B и timeLeft_A против timeLeft_B.Эти переменные находятся вне области действия этой функции.

Проблема, с которой я имею модульную структуру этой сопрограммы, заключается в том, что увеличенные значения timeLeft_A и timeLeft_B должны применяться вне этой функции, поэтому мне нужно как-то передать ссылку на них.

Пользователь "Kurt-Dekker" опубликовал отличное решение в этой теме , но у меня возникли проблемы с применением его к моему коду.Он говорит «использовать замыкание (функтор), чтобы позволить сопрограмме изменить его с помощью обратного вызова»:

IEnumerator CountDown( System.Action<int> callback){
    ....
}

, который, я думаю, будет назван так:

StartCoroutine ( CountDown( (i) => { timeLeft_A = i; } ) );
StartCoroutine ( CountDown( (i) => { timeLeft_B = i; } ) );

Что яЯ не понимаю, как потом ссылаться / изменять фактическое значение передаваемого int внутри IEnumerator, если все, с чем мне нужно работать - это функция обратного вызова.Например, чтобы сделать следующее:

while(callback > 0){

или:

callback--;

Любая помощь приветствуется.

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Просто поместите мою реализацию здесь для потомков, основываясь на ответе Антуана Тири.

private static int totalTime_A = 45; // Num seconds
private int timeLeft_A = totalTime_A;
private static int totalTime_B = 30; // Num seconds
private int timeLeft_B = totalTime_B;

// Pass in totalTime separately from the callback (totalTime doesn't need to change)
IEnumerator CountDown(int totalTime, Action<int> callback){
  int timeLeft = totalTime; // Create new local var here to operate on it
  while (timeLeft > 0) {
    yield return new WaitForSeconds(1);
    callback(--timeLeft); // Then put the local var into callback after operating on it
  }
}

CountDown() называется так:

// timeLeft_A and timeLeft_B change globally
IEnumerator Countdown_A = CountDown(totalTime_A, (i) => { timeLeft_A = i; });
IEnumerator Countdown_B = CountDown(totalTime_B, (i) => { timeLeft_B = i; });
StartCoroutine(Countdown_A);
StopCoroutine(Countdown_A);
StartCoroutine(Countdown_B);
StopCoroutine(Countdown_B);
0 голосов
/ 08 декабря 2018

Я думаю, что это может ответить на ваш вопрос о том, как использовать System.Action<float> в сопрограмме.

В основном, когда вы вызываете StartCoroutine, вы даете максимальное время для своего счетчика в качестве обычного параметра иобратный вызов, этот обратный вызов принимает в качестве аргумента число с плавающей точкой (здесь это i ).

Если вы вызовете callback(--timeLeft); в своей сопрограмме, он выполнит переданный вами System.Action.

Здесь будет установлено timeLeft_A или timeLeft_B для переменной timeLeft соответствующей сопрограммы.

public float timeLeft_A = 0f;
public float timeLeft_B = 0f;

public float totalTime_A = 15f;
public float totalTime_B = 20f;

// Start is called before the first frame update
void Start()
{
    StartCoroutine(Cooldown(totalTime_A, (i) =>
    {
        timeLeft_A = i;
    }));

    StartCoroutine(Cooldown(totalTime_B, (i) =>
    {
        timeLeft_B = i;
    }));
}

IEnumerator Cooldown(float totalTime, Action<float> callback)
{
    var timeLeft = totalTime;
    while (timeLeft > 0)
    {
        yield return new WaitForSeconds(1);
        callback(--timeLeft);
    }
}
...