Search Unity

Using MoveNext() with a For-Loop?

Discussion in 'Scripting' started by Vad3rInHale, Sep 16, 2020.

  1. Vad3rInHale

    Vad3rInHale

    Joined:
    Apr 19, 2015
    Posts:
    96
    Hi all,

    I've done this before with a Coroutine, using yield return null statements like stepping through an algorithm, but now I have a new problem I'm not sure how to tackle.

    Basically, there are abilities out there that trigger off conditions, so I want to give them the logic flow until they have resolved their ability. This could be immediately, 1 second, or 1 minute. So, I was trying to have a for loop set up where I could call MoveNext() to process the next element. Obviously, this is a Coroutine. And so the issues I'm running into here is when "_conditionals.ConditionsMet();" is resolved immediately, the yield return null doesn't occur before ConditionalAbilityResolved() is called, therefore skipping the last element.

    Does this issue make sense? Is there a solve where I don't need to yield a return?

    Thanks, Alex

    Code (CSharp):
    1. void StartResolvingConditionals(ConditionalAbility.Condition condition)
    2.     {
    3.         List<ConditionalAbility> conditionalAbilities = new List<ConditionalAbility>();
    4.         for (int i = 0; i < allConditionalAbilities.Count; i++)
    5.         {
    6.             if (allConditionalAbilities[i].condition == condition) conditionalAbilities.Add(allConditionalAbilities[i]);
    7.         }
    8.  
    9.         currentConditionalsRoutine = ConditionalsRoutine(conditionalAbilities);
    10.         TryResolveNextAbility();
    11.     }
    12.  
    13.     public void ConditionalAbilityResolved()
    14.     {
    15.         TryResolveNextAbility();
    16.     }
    17.  
    18.     void TryResolveNextAbility()
    19.     {
    20.         if (!currentConditionalsRoutine.MoveNext())
    21.         {
    22.  
    23.         }
    24.     }
    25.  
    26.  
    27.     IEnumerator currentConditionalsRoutine;
    28.     IEnumerator ConditionalsRoutine(List<ConditionalAbility> conditionals)
    29.     {
    30.         List<ConditionalAbility> _conditionals = new List<ConditionalAbility>(conditionals);
    31.         for (int i = 0; i < _conditionals.Count; i++)
    32.         {
    33.             print("Resolving conditional - " + i);
    34.             // Resolve in order of priority
    35.             _conditionals[i].ConditionsMet();
    36.             yield return null;
    37.         }
    38.     }
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Not true. This is simply a C# iterator: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/iterators

    It only becomes a Coroutine when you start it with
    StartCoroutine
    . Coroutines are a Unity-specific thing which takes advantage of how C# iterators work. Iterators are a C# thing. They are therefore related, but not one and the same.
    Not really, but maybe this will help? Whenever you call MoveNext(), ConditionalsRoutine will execute until it reaches either the end of the method (the for loop is finished), or it encounters any
    yield
    statement, such as the one on line 36.

    I'm having trouble understanding what you mean by this:
    The ConditionsMet() call on line 35 will always fully resolve before the yield statement on line 36. So the yield statement obviously will not occur before it is called.
     
  3. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    If I'm understanding this correctly, you probably want to use WaitUntil as your yield instruction in your coroutine.
    I.E: wait until the condition of the current ConditionalAbility element in the List is met before continuing:
    Code (CSharp):
    1. IEnumerator ConditionalsRoutine(List<ConditionalAbility> conditionals) {
    2.    for(int i = 0; i < conditionals.Count; i++) {
    3.       //Assuming ConditionsMet returns a bool.
    4.       yield return new WaitUntil(() => conditionals[i].ConditionsMet());
    5.    }
    6. }
    There's also no need to create a copy of the List you pass into the coroutine. You can just use the direct reference.
     
  4. Vad3rInHale

    Vad3rInHale

    Joined:
    Apr 19, 2015
    Posts:
    96
    Hey, I think you helped me out with this Coroutine/Enumerator question before. This one is a bit different.

    So, _conditionals.ConditionsMet(); will call ConditionalAbilityResolved() once it's done resolving it's ability. If it does this instantly (without waiting a bit), TryResolveNextAbility(); will call MoveNext(), for which it says false, because the callback gets to it's yield return null before the first call does.

    I've implemented this for now, which works but isn't pretty

    Code (CSharp):
    1. {
    2. List<ConditionalAbility> conditionalAbilities_CurrentTurn = new List<ConditionalAbility>(conditionalAbilities);
    3.         currentConditionalsRoutine = conditionalAbilities_CurrentTurn.GetEnumerator();
    4.  
    5.         TryResolveNextAbility();
    6.     }
    7.  
    8.     public void ConditionalAbilityResolved()
    9.     {
    10.         TryResolveNextAbility();
    11.     }
    12.  
    13.     void TryResolveNextAbility()
    14.     {
    15.         if (currentConditionalsRoutine.MoveNext())
    16.         {
    17.             ConditionalAbility conditionalAbility = currentConditionalsRoutine.Current as ConditionalAbility;
    18.             print("player: " + conditionalAbility.card.myPlayer);
    19.             conditionalAbility.ConditionsMet();
    20.         }
    21.     }
    22.  
     
  5. Vad3rInHale

    Vad3rInHale

    Joined:
    Apr 19, 2015
    Posts:
    96
    Hi,
    This call is new to me, and seems like it would work for a different scenario. Unfortunately, ConditionsMet() doesn't return a bool, even though the bad naming suggests it might. It does some stuff, and then calls ConditionalAbilityResolved() at some X time later, or instantly.

    Also, the way I've gone about this functionality before, it using while(bool) yield return null.
    I guess with WaitUntil I don't have to have a global bool? Kind of works like a lambda?