• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by DRRosen3 · Feb 21, 2015 at 07:39 PM · c#coroutineyieldienumerator

Why Won't My Coroutine Yield?

This section of my code won't yield. It just goes right through it.

 while(angle > 1.0f)
         {
             my_transform.rotation = Quaternion.Slerp(my_transform.rotation, fwdRotation, Time.deltaTime * 2.0f);
             angle = Quaternion.Angle(my_transform.rotation, fwdRotation);
             Debug.Log ("Inside loop" + angle);
             yield return null;
         }

I know it does, because I can watch it happen during run-time, because the following line of code happens BEFORE the while loop above is complete.

 yield return StartCoroutine(MoveBall(col));
         rigidbody.WakeUp();  //<<<<<<-------THIS ONE RIGHT HERE SHOULDN'T HAPPEN UNTIL THE WHILE LOOP IS DONE AND THE ORB IS DESTROYED.

Any ideas why this is happening? Here's the full code...

 using UnityEngine;
 using System.Collections;
 
 public class EmptyBall : MonoBehaviour
 {
     public Ball.BallTypes this_ball_type;
     public PlayerCharacter this_player;
     public AudioClip capture_attempt;
     public AudioClip attempting_capture;
     public AudioClip capture_success;
     public AudioClip capture_fail;
     public GameObject capture_orb_prefab;
 
     private Transform my_transform;
     private RaycastHit hit;
     private float distance_to_ground;
     private CalculateCapture calculate_capture_script = new CalculateCapture();
 
     void Start()
     {
         my_transform = transform;
     }
     void Update()
     {
         if(Physics.Raycast(transform.position, -Vector3.up, out hit)){
             distance_to_ground = hit.distance;
         }
     }
     void OnCollisionEnter(Collision col)
     {
         if(col.gameObject.tag == "Capturable")
         {
             Monster this_monster = col.gameObject.GetComponent<Monster>();
             if(!this_monster.is_captured)
             {
                 audio.PlayOneShot(capture_attempt);
                 col.gameObject.GetComponent<Animation>().enabled = false;
                 StartCoroutine(Capture(col));
             }
             else
             {
 
             }
         }
         else
         {
             Destroy(this.gameObject);
         }
     }
 
     private IEnumerator Capture(Collision col)
     {
         yield return StartCoroutine(MoveBall(col));
         rigidbody.WakeUp();
         while(distance_to_ground > 0.2f)
         {
             yield return null;
         }
         rigidbody.isKinematic = true;
         yield return StartCoroutine(TryToCatch(col));
     }
     private IEnumerator MoveBall(Collision col)
     {
         Vector3 move_to = new Vector3(transform.position.x-1.5f, col.contacts[0].point.y+1.5f, transform.position.z-1.5f);
         while(Vector3.Distance(transform.position, move_to) > 0.01f)
         {
             rigidbody.velocity = Vector3.zero;
             rigidbody.angularVelocity = Vector3.zero;
             rigidbody.Sleep();
             transform.LookAt(col.transform.position);
             transform.position = Vector3.Lerp(transform.position, move_to, 5f * Time.deltaTime);
             yield return null;
         }
         animation["Open_Top"].speed = 5;
         animation.Play("Open_Top");
         GameObject orb = Instantiate(capture_orb_prefab, col.gameObject.GetComponentInChildren<Renderer>().renderer.bounds.center, Quaternion.identity) as GameObject;
         col.gameObject.SetActive(false);
         while(Vector3.Distance(my_transform.position, orb.transform.position) > 0.01f)
         {
             Vector3 orb_target = new Vector3(my_transform.position.x, my_transform.position.y, my_transform.position.z);
             orb.transform.position = Vector3.Lerp(orb.transform.position, orb_target, 2.7f * Time.deltaTime);
             yield return null;
         }
         orb.transform.parent = my_transform;
         animation["Close_Top"].speed = -5f;
         animation["Close_Top"].time = animation["Close_Top"].length;
         animation.Play("Close_Top");
         Vector3 flatFwd = new Vector3(my_transform.forward.x, 0, my_transform.forward.z);
         Quaternion fwdRotation = Quaternion.LookRotation(flatFwd, Vector3.up);
         float angle = Quaternion.Angle(my_transform.rotation, fwdRotation);
         Debug.Log (angle);
         while(angle > 1.0f)
         {
             my_transform.rotation = Quaternion.Slerp(my_transform.rotation, fwdRotation, Time.deltaTime * 2.0f);
             angle = Quaternion.Angle(my_transform.rotation, fwdRotation);
             Debug.Log ("Inside loop" + angle);
             yield return null;
         }
         Destroy(orb);
         yield return null;
     }
     private IEnumerator TryToCatch(Collision col)
     {
         Monster this_monster = col.gameObject.GetComponent<Monster>();
         audio.PlayOneShot(attempting_capture);
         yield return new WaitForSeconds(attempting_capture.length+1);
         bool try_to_capture = calculate_capture_script.AttemptCapture(this_monster.status_condition, this_ball_type, this_monster.cur_hp,
                                                                       this_monster.cur_max_hp, this_monster.capture_rate);
         if(try_to_capture){
             this_monster.is_captured = true;
             this_monster.trainers_name = this_player.players_name;
             Monster temp = this_monster;
             PlayerMonsterData data_holder_monster = new PlayerMonsterData (temp.is_setup, temp.is_captured, temp.trainers_name, temp.monster_name,
                                                                                 temp.nick_name,
                                                                                 temp.is_from_trade, temp.level, temp.gender, temp.nature, temp.max_hp,
                                                                                 temp.cur_max_hp, temp.max_atk, temp.max_def, temp.max_spatk, temp.max_spdef,
                                                                                 temp.max_spd, temp.cur_hp, temp.cur_atk, temp.cur_def, temp.cur_spatk,
                                                                                 temp.cur_spdef, temp.cur_spd, temp.last_required_exp, temp.current_exp,
                                                                                 temp.next_required_exp);
             if(this_player.players_monster_roster.Count < 6)
             {
                 this_player.players_monster_roster.Add(data_holder_monster);
             }
             else
             {
                 this_player.players_monster_inventory.Add(data_holder_monster);
             }
             this_monster.is_captured = false;
             this_monster.SetDead();
             audio.PlayOneShot(capture_success);
             yield return new WaitForSeconds(capture_success.length);
         }
         else
         {
             animation["Open_Top"].speed = 5;
             animation.Play("Open_Top");
             audio.PlayOneShot(capture_fail);
             yield return new WaitForSeconds(animation["Open_Top"].length);
             col.gameObject.SetActive(true);
             col.gameObject.GetComponent<Animation>().enabled = true;
             animation["Close_Top"].speed = -5f;
             animation["Close_Top"].time = animation["Close_Top"].length;
             animation.Play("Close_Top");
             yield return new WaitForSeconds(capture_fail.length);
         }
         Destroy(gameObject);
         yield return null;
     }
 }

Comment

People who like this

0 Show 7
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Stardog · Feb 21, 2015 at 08:42 PM 0
Share

What does "Debug.Log ("Inside loop" + angle);" say? Also, put a log before the WakeUp.

Also, be careful you are not running more than one "StartCoroutine(Capture(col));" at once.

avatar image Jessespike · Feb 21, 2015 at 09:11 PM 0
Share

Does Orb have collision and coroutine code as well? Asking because when you Instantiate Orb in the same spot as the previous Col object, it might throw your transform calculations to something unexpected.

avatar image DRRosen3 · Feb 21, 2015 at 09:53 PM 0
Share

Inside the loop the log shows it slowly decrementing down from where it starts (44.xxx). There's only one StartCoroutine(Capture(col)) running.

Orb doesn't have a collider. It's just a prefab of a particle effect and it has no coroutine.

avatar image Uldeim · Feb 22, 2015 at 01:16 AM 0
Share

Although I admit that Coroutines still confuse me, I'm reasonably certain how IEnumerator functions work: Every time they are called, they return the next yielded value from the function.

That means that they return immediately after seeing a yield. They need to, since otherwise you couldn't use them in foreach loops.

It looks to me like your code is saying:

 Capture(col)
  - Wait for MoveBall to yield
  -- MoveBall does the *first* iteration of its first distance loop, then hits a yield
  -Capture stops waiting and continues, yielding it's new value (the result)

  During the next Update() step? (or whenever the next call of Coroutines is), 
  the Unity engine does the following in some order:
  - WakeUp()ing the rigidbody.
  - Doing the next iteration of the distance loop in TryToCatch()

Unfortunately, I don't know of a way to wait for a Coroutine to complete before returning. I usually use events and delegates for this. Coroutines are NOT synchronous, and yield return will not make them so.

Alternatively, one obvious solution to solve the stated problem would be to put the rigidbody.WakeUp() code at the end of MoveBall() itself.

avatar image DRRosen3 Uldeim · Feb 22, 2015 at 03:15 AM 0
Share

This is also incorrect. If that was true, the rigidbody.WakeUp(); would occur right before the animation["Open_Top"].speed = 5; However, it doesn't. The rigidbody.Wakeup(); does not occur until the while loop starts.

...and an IEnumerator IS a Coroutine. >.> Anyway...

"The execution of a coroutine can be paused at any point using the yield statement. The yield return value specifies when the coroutine is resumed. Coroutines are excellent when modelling behaviour over several frames. Coroutines have virtually no performance overhead. StartCoroutine function always returns immediately, however you can yield the result. This will wait until the coroutine has finished execution." -- Taken from the Unity documentation on Coroutines. When I declare the coroutine private IEnumerator Capture(Collider col) the very first line in that coroutine reads yield return StartCoroutine(MoveBall(col));. Now since I called that Coroutine, inside of a Coroutine with the yield return statement, that means the coroutine "Capture" will not continue executing until the coroutine "MoveBall" is completed.

avatar image Jessespike Uldeim · Feb 22, 2015 at 04:43 AM 0
Share

Yea, yield return should block execution for the Coroutine to finish before proceeding. Why it's not working here is the mystery. At this point, I would start commenting out code in chunks, and see if anything changes. Adding debug.logs to any information that may be useful. Hopefully its not some other script or object in the scene causing problems.

avatar image Fattie · Feb 22, 2015 at 04:47 AM 0
Share

"Coroutines are NOT synchronous, and yield return will not make them so" huh? you just say StartCoroutine(...) to make it run "now" (synchronously) and yield return.. to make the calling routine wait.

2 Replies

· Add your reply
  • Sort: 
avatar image
Best Answer

Answer by DRRosen3 · Feb 22, 2015 at 08:16 AM

Solved it myself. The problem was with lines 60 through 73.

 while(Vector3.Distance(transform.position, move_to) > 0.01f)
          {
              rigidbody.velocity = Vector3.zero;
              rigidbody.angularVelocity = Vector3.zero;
              rigidbody.Sleep();
              transform.LookAt(col.transform.position);
              transform.position = Vector3.Lerp(transform.position, move_to, 5f * Time.deltaTime);
              yield return null;
          }


This while loop was causing the rigibody to wake up and go to sleep each time it iterated. Upon its final iteration, the rigidbody was already awake. This still doesn't explain why the loop that follows wouldn't fully execute however.

Comment
Fattie

People who like this

1 Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Fattie · Feb 22, 2015 at 09:12 AM 0
Share

"was causing the rigibody to wake up ..." thank God I was right as usual ;-)

glad you found what I said was causing the problem ;-)

avatar image

Answer by Fattie · Feb 22, 2015 at 04:55 AM

hi Rosen, what's up

1) you know, you have a problem that your code is not really usable dude! No routine should be more than, say, 6 or 7 lines of code. You'll have to take 1 or 2 minutes to refactor the code so that it is clear. (To begin with , it would be impossible to find any flaws, in the current code, since it's just long-form "goes on-and-on" code, there's no named concepts, you know?)

After you do that ...

2) your thinking and use of coroutines seems quite correct.

3) as Star already told you add two lines of debug

 Debug.Log("before...");
 yield return StartCoroutine(MoveBall(col));
 Debug.Log("after...");
 rigidbody.WakeUp();

and we'll be a mile closer to understanding the problem. it's almost certainly simply the case that something else is waking the rigidbody, which can happen for many reasons.

And indeed (even more importantly) to repeat what Star said

 Debug.Log("I am in the OnCollision and about to run Capture...");
 StartCoroutine(Capture(col));

as it could surely be running more than one of those. (I find it tedious and tricky to know just what physx is doing with OnCollisionEnter)

In any event be sure to go back to point (1), y'hear! :) it's not workable like that .. and it will only take a minute to write it "as code" with separate concepts.

For example: your incredibly long MoveBall passage should look something like this (this is purely an example)

 whatever MoveBall()
 {
 CalculateCurrentCriticalBallValues( ball b )
 newPosition = GuessBestNewPosition( ball B, enemies E )
 goodPositon = CheckIfPositionPossible( newPosition )
 while ( ! goodPosition )
    AdjustPositionSlightlyAllowingForWalls()
 yield return StartCoroutine( MoveOnSplineTo( newPosition )
 BeginPlayingExplosionAnimations()
 yield return new WaitForSeconds(  knownExplosionAnimeTime )
 yield return StartCoroutine( TrimFinalPosition( b )  )
 BeginSettleAnimations()
 yield return new WaitForSeconds(  knownSettleTime )
 }

Note that that is perfectly understandable. If you read through that you know EXACTLY what is happening - right?

there's a fantastic principle in coding .. "Code should be self-explanatory".

(essentially: "use only very long object and method names, which totally explain what is happening when read as prose")

another thing you hear which means the same is "you shouldn't even need comments". ie, the code should be self-explanatory. Hope it helps!

Comment

People who like this

0 Show 14 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image DRRosen3 · Feb 22, 2015 at 05:10 AM 0
Share

Anything before "After you do that..." and anything after "...is doing with OnCOllisionEnter)" is COMPLETELY irrelevant and unhelpful. I fail to see why people CONTINUE to (try to) bring people down, in a place where you're meant to be HELPFUL.

avatar image Fattie · Feb 22, 2015 at 05:31 AM 0
Share

dude I think you are just reading tone in to it that is not there. what would you want someone to say? i love you but your code is unusable, since no routine should be more than a handful of lines of code - you could have rewritten in in the time we've both spent on these comments now! I will add some smileys to cheer things up

avatar image Fattie · Feb 22, 2015 at 05:31 AM 0
Share

Oh and you're welcome for the time I spent on providing the answer to your problem.

avatar image DRRosen3 · Feb 22, 2015 at 05:36 AM 0
Share

Dude? You don't have to be patronizing to be helpful. 1. "your code is horrible dude!" is completely disrespectful. 2. "You are miles, a lightyear, away from worrying about stuff like whether coroutines work." is disrespectful and degrading. As I said before you don't have to patronize someone ("i love you but your code is unusable") to help them. You don't seem to see that, so I'm not going to waste my time trying to explain that concept to you. However, the problem has NOTHING to do with how many lines of code I put into the routine...and that's what I'm here for, the problem. Not your opinion of how many lines should or should not be in a code. Oh...and I've nothing to be grateful for, you haven't solved anything. You'll know when the problem is answered because I'll mark it answered.

avatar image Fattie · Feb 22, 2015 at 05:42 AM 0
Share

You sound silly. language like "your code is horrible dude!" can be read with a smiley, it's nothing. the actual literal reason you have a problem is that the code is not factored: as I said your use of coroutines is on the face of it correct. i have literally provided the explanation (which Star already did, as I said) ("something else is waking the rigidbody") and I have literally provided the solution! (after you refactor, add the three lines of Debug and you will have the solution instantly)

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

6 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Waiting twice inside coroutine (C#) 2 Answers

What am I doing wrong with IEnumerator? 1 Answer

Yield Wait for Seconds (A little help here) 2 Answers

Trouble Resuming after Yielding while Inside Coroutine 1 Answer

Coroutines not passing yield 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges