0
\$\begingroup\$

I have a base class called Ability and then children classes for each ability like BasicMeleeStrike. I have a turn based game so the Do() method of each ability does it's logic sequentially. I'm trying to make coding a little nicer looking in each child ability class and I want to not have to yield out from in them but call base class functions that will do the operations over time but I want them to wait until they come back to the child class Do() function but what I have doesn't work. Is this possible?

class Ability{
   public virtual IEnumerator Do(Actor target){}
   protected IEnumerator JumpToTarget(Actor target){
      yield return StartCoroutine(_JumpToTarget(target);
   }
   private IEnumerator _JumpToTarget(Actor target){
      // inside here I tween to a location so it's yielding return null until it's done
   }
}

class BasicMeleeAttack : Ability{
   public override IEnumerator Do(Actor target){
      JumpToTarget(target);
      anim.Play("attack");
   }
}

No jumping happens but the attack animation plays. When I put a breakpoint on anim.Play() it's hit right away. Is there a way to make it stay inside JumpToTarget(target) until it's finished without having to do a yield inside BasicMeleeAttack's Do() function? I just think it's much friendlier to not have to code that for all my actions.

\$\endgroup\$
4
  • \$\begingroup\$ Execution runs until it hits a return, yield, await, or the end of the method (or an Exception is thrown...). So if you want execution to pause somewhere mid-stream, you have to tell it so using the yield or await keywords. Why is it that you don't want to put a yield inside Do()? It's no more lines than you have now, and it's more explicit about the desired behaviour, so it seems like a win-win. \$\endgroup\$
    – DMGregory
    Commented Oct 12, 2017 at 22:40
  • \$\begingroup\$ It's no more lines but I find the implied yield is cleaner and less typing. JumpToTarget(target) is much cleaner than yeild return StartCoroutine(JumpToTarget(target)); even if it loses some meaning. I know the meaning as everything in Do() is sequential in nature. So basically there is no way around this? \$\endgroup\$
    – user441521
    Commented Oct 12, 2017 at 22:43
  • 1
    \$\begingroup\$ If you don't use yield then you're making an explicit statement "DO NOT wait for this IEnumerator/coroutine to finish before proceeding." If that's not what you mean, then you need to tell the compiler what you want it to do instead. \$\endgroup\$
    – DMGregory
    Commented Oct 12, 2017 at 23:24
  • \$\begingroup\$ Is there a way to do this without StartCoroutine()? Like when using WaitForSeconds() it's yield return new WaitForSeconds(). it doesn't use StartCoroutine() in the way I'm using it. \$\endgroup\$
    – user441521
    Commented Oct 12, 2017 at 23:43

1 Answer 1

2
\$\begingroup\$

Is there a way to do this without StartCoroutine()?

Sure, you can step the IEnumerator manually.

var nestedRoutine = JumpToTarget(target);
while(nestedRoutine.MoveNext())
    yield return null; // Wait a frame and check again next time.
                       // Note that this ignores what nestedRoutine yielded!
                       // If it's waiting on a WWW to download, and we don't
                       // handle that case ourselves, we'll pump it too early.

// Execution resumes here once JumpToTarget finishes all its work.

But this seems like substantially more work than just

yield return StartCoroutine(JumpToTarget(target));
// Execution resumes here once JumpToTarget finishes all its work.
// This correctly handles all yield return types that Unity accepts,
// since we're letting the Unity engine handle the yielding logic.
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .