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;
    }
}

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.

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 …

  1. your thinking and use of coroutines seems quite correct.

  2. 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! :slight_smile: 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!