Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • 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
2
Question by jedd.ahyoung · Jul 18, 2014 at 07:19 AM · coroutine

When using coroutines, what should I be wary of?

I have discovered coroutines (as of yesterday) and I have come to the conclusion that they are awesome. Even something as simple as jumping in a 2D platformer has benefitted from the use of a coroutine on buttondown; thanks to coroutines, I now have less code and better functionality! (Less state variables to look at in my update calls!)

Naturally, discovering the greatness that is coroutines, I'm thinking I should attempt to use them whenever I can. Yet, as a developer, I know that overzealousness and overdependence on a single paradigm can be foolish - at best, inefficient; at worst, crippling.

I believe that I know the benefits of using coroutines. What, if any, are the drawbacks? What are the potential pitfalls that I can run int? Finally, what other options do I have to turn to if the drawbacks/pitfalls of coroutines will cause crippling problems?

Thanks for your input! I know that this is a broad question, but I'm hoping that it'll help me (and any other Unity developers that want to learn best practices and know the platform better).

Comment
Add comment
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

2 Replies

· Add your reply
  • Sort: 
avatar image
3

Answer by CHPedersen · Jul 18, 2014 at 08:41 AM

Coroutines are indeed awesome. :) Mostly, I think, because they're such a creative use of .Net's iterators and the MoveNext method that I highly doubt the .Net designers saw it coming, but it's really a testimony to the flexibility of that particular language feature. :)

I recommend you read this blog post about coroutines, from Richard Fine aka SuperPig, who is a really experienced developer and took the time to carefully explain exactly what a coroutine is and how it's implemented in Unity:

http://www.altdev.co/2011/07/07/unity3d-coroutines-in-detail/

Understanding this article in-depth will help you answer most of your questions about coroutines and their pitfalls yourself in the future, also. But, I've taken the liberty to add a quick summary of the common misconceptions and misunderstandings here anyway:

  1. Coroutines are not threads. The code you put in a coroutine is executed entirely in serial by the main thread. There is a scheduled time in the script execution order where coroutines are executed, please see this page: http://docs.unity3d.com/Manual/ExecutionOrder.html

  2. Because coroutines are not threads, they are not designed to perform work in parallel. Their primary purpose is to spread work out over multiple frames so it either animates nicely if it's a visual effect, or prevents stalling the engine and causing a hiccup if it's a concentrated amount of heavy workload, like multiple object instantiations of heavy meshes.

  3. A coroutine is an object that implements IEnumerator. This object is added to a list of currently active coroutines inside MonoBehaviour when StartCoroutine is called. This list is then traversed once per Update, and the IEnumerator's MoveNext method is called, which triggers the execution of the next cycle in the coroutine. Therefore, if you kill the MonoBehaviour you used to start a coroutine, the coroutine dies, too. They are not totally fire-and-forget.

  4. Always carefully define a coroutine's exit-condition(s). They are often used to animate something from A to B because they exit and terminate themselves so beautifully when written right. But if the loop's exit conditions is ill-defined, it might run forever, and you won't find out until something starts acting weird.

  5. Beware of starting more coroutines of the same type based on some user-interaction. You could end up having multiple coroutines try to effect changes to the same program state, and thus, they will end up fighting over who gets to do the work. As an example, suppose you have a coroutine open and close a door. Clicking once opens, clicking again closes. If I click twice really fast, two coroutines open and start fighting over opening/closing the door. The result is a door that either twitches, or is frozen in mid-animation.

  • Probably more I've forgotten. Please add as comments if you think of something. ;)

Comment
Add comment · Show 5 · 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 jedd.ahyoung · Jul 18, 2014 at 03:28 PM 0
Share

Hey, great list! I've been careful to use StopCoroutine() when necessary. However, I do have a coroutine on my $$anonymous$$ainCamera that sets the angle based on the player's X velocity. I've included code below; is this a trap (trap #5, to be exact)?

 public class CameraController : $$anonymous$$onoBehaviour {
 
     public Transform target;
     public float maxTime = 10.0f;
 
     // Use this for initialization
     void Start () {
     
     }
     
     // Update is called once per frame
     void Update () {
         var pos = this.transform.position;
         pos.x = target.position.x;
         pos.y = target.position.y;
         this.transform.position = pos;
 
         var angle = this.transform.rotation;
         angle.y = (target.rigidbody2D.velocity.x / 2) * $$anonymous$$athf.Deg2Rad;

         StartCoroutine("AngleCamera", angle); // This is the trap I'm worried about....?
     }
 
     private IEnumerator AngleCamera (Quaternion target) {
         float elapsedTime = 0.0f;
         Quaternion startingPosition = this.transform.rotation;
         while (elapsedTime < maxTime) {
             elapsedTime += Time.deltaTime;
             transform.rotation = Quaternion.Slerp(this.transform.rotation, target, elapsedTime / maxTime);
             yield return new WaitForEndOfFrame();
         }
     }
 }
avatar image jedd.ahyoung · Jul 18, 2014 at 03:41 PM 0
Share

Specifically.....I'm starting a coroutine in an update statement, which means I'm starting one on every frame. This essentially means that they won't finish until a character stops moving. So what I should do is use a state variable for the quaternion rotation, and simply have the coroutine start in the Start() function.....and have it run forever, attempting to target the target rotation. If the rotation is complete (if this.transform.rotation is close enough to target.rotation), it'll yield return null; if not, it'll attempt to slerp to the correct transform. This means that I'd only start one coroutine that would run forever, ins$$anonymous$$d of running multiple routines?

avatar image robertbu · Jul 18, 2014 at 03:56 PM 1
Share

Yes jedd.ahyoung, you are starting a coroutine every frame. So if your app was running at 60 fps, you will have 600 coroutines running at any one time, all trying to do something similar, and only the last one executed doing anything. Apart from that issue, I'm surprised this code is working at all. You are directly manipulating the 'y' of a Quaternion (i.e. angle). Quaternion components are not angles, and they are not measured in degrees. Nor is their representation stable (i.e. they go from -1 to 1 but you cannot simply use values in the -1 to 1 range and manipulate them).

As for your rotation code, assu$$anonymous$$g you got the angle issue sorted out, you could replace the whole coroutine with a line like this in Update():

 transform.rotation = Quaternion.RotateTowards(transform.rotation, angle, time.deltaTime * speed);

This code is slightly different in that it rotates at a specific speed (degrees per second), where your code rotates in fixed time no matter how far apart the actual and descried rotation are.

avatar image jedd.ahyoung · Jul 18, 2014 at 07:21 PM 0
Share

Hmm. I'll look into that method. I'd chosen Slerp because of the transition easing. Doing it in the update frame might be a better idea, since with my solution I'd be starting a never-ending coroutine (and as such, what's the point, right?).

@robertbu As for the angle issue, I don't think I'm manipulating the quaternion directly. x, y, and z are properties, not fields; I believe that they correspond to Euler angles and have their own getters and setters to manipulate the underlying quaternion. This is why, although transform.rotation is a quaternion, you can set transform.rotation.x and transform.rotation.y to degrees, like you can in the editor. That said, I'll check the documentation on this to make sure that I'm correct (as I wouldn't want to leave that statement unsupported).

avatar image jedd.ahyoung · Jul 18, 2014 at 10:23 PM 0
Share

@robertbu Well, it turns out that you're right. I looked it up here and it looks as though those properties are actually setting the quaternion directly, not the Euler angles. The strange thing is.....it's working. Checking the editor, the rotation degrees are actually matching up perfectly. I know a little bit about quaternions, but defintely not enough to state that I know what I'm doing....this is something I'll have to solve, now! If you or anyone else knows more about Quaternions, perhaps you could share some knowledge about why this is working?

avatar image
1

Answer by smallbit · Jul 18, 2014 at 08:20 AM

Coroutines are cool and one of the essential parts of any of my Unity projects. One problem you may encounter is that if you use the WaitForSeconds operator in your coroutine and you pause your game (timescale = 0) It will never yield. Than you probably need to write a custom WaitForSeconds that uses (realtimeSinceStartup value instead of Time.deltaTime). link text.

Comment
Add comment · Show 2 · 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 KevinGelking · Jul 18, 2014 at 08:24 AM 0
Share

Adding to this and generalizing, the biggest thing you need to watch out for with coroutines is to make sure they actually -have- an Ending Condition in general, else it will loop through infinitely, freezing up Unity~

avatar image jedd.ahyoung · Jul 18, 2014 at 07:14 PM 0
Share

I hadn't thought of this. (Incidentally, from reading your answer, I now have learned how to pause games. :D) I'll definitely look out for this pitfall, although I'd imagine that you'd want most coroutines to pause execution as well? I suppose it depends on your use case.

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.

Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Working projectile scripts do not work due to if condition 1 Answer

Method keeps looping when using waitForSeconds 1 Answer

Segment within a nested If does not get evaluated 2 Answers

Make a coroutine run when Time.timeScale = 0; 3 Answers

convert timer from update() to coroutine or invokerepeating 0 Answers


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