Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 /
  • Help Room /
avatar image
0
Question by Capricornum · Feb 13, 2018 at 05:33 PM · performancecoroutinewaitforsecondsstartcoroutine

Performance Issue with Coroutines

Dear Community,

In my game I come to a point where I want several tasks to be performed before the player can interact with the game again. The tasks being: My 5 heroes attacking all 3 enemies one after another by shooting balls at them. Then the enemies attacking the heroes one after another and finally instantiating a new bunch of objects into a grid. I am using coroutines to do that. The problem is that at this point my computer is going wild. (loud and hot - its a macbook 2015) And I don't know why. Since I'd like to eventually play this game on my iPad, it really needs to be simple.

Here is a picture:

alt text

Essentially the code goes like this:

 /*
 using ...
 public class InputManager : MonoBehaviour {
      public Hero[] heroes;
      public Monster[] monsters;
      public GridManager grid;
      public int timesToClick = 4;
   
      void Update () {
           // When you click on the grid, deactivate the tiles beneath.
           // timesToClick--;
           // Instantiate a gameObject "Ammo".
           // start Ammo's coroutine that lets it fly to the hero.
           // in Ammo's script: when "Ammo"'s.position == hero.position call hero.ReceivedAmmo
           // that starts the coroutine Fight() in this script
      }

      IEnumerator Fight() {
           foreach (Hero hero in heroes) {
                hero.Attack();
                yield return new WaitForSeconds (timeBetweenAttacks);
           }
           foreach (Monster monster in monsters) {
                monster.Attack();
                yield return new WaitForSeconds (timeBetweenAttacks);
           }
           grid.UpdateGrid();
           yield return new WaitForSeconds (timeToUpgradeGrid);
           timesToClick = 4;
      }
 }
 */

hero.Attack and monster.Attack are functions in my hero and monster class. When my heroes attack they each instantiate 3 "Ball" objects (another custom class), and start "Ball's" coroutine moving their transform from the heroes position to each of the monsters position. Essentially like this:

 /*
 public class Ball
      public static int ballCount;
      public float speed;
      private Vector3 targetPos;

      void Awake {
            ballCount++;
      }
 
      IEnumerator Attack(Monster target, int damage)
      {
           targetPos = target.transform.position;
           while (this) {
                speed *= Time.DeltaTime;
                transform.position = Vector3.MoveTowards (transform.position, targetPos, speed);
                if (transform.position == targetPos) {
                     target.HasBeenDamaged(damage);
                     ballCount--;
                     Destroy (this.gameObject);
                }
                yield return new WaitForFixedUpdate ();
           } 
      }
 */

This essentially is the Hero Script:

 /*
 public class Hero : MonoBehaviour {
      public int damage;
      public InputManager InputManager;
 
      void ReceivedAmmo () {
           damage++;
           //do some UI stuff 
           if (inputManager.timesToClick == 0) {
                StartCoroutine (inputManager.Fight());
           }
      }
 }
 */

Can anyone explain why this is so computationally demanding?

game-screenshot.jpg (209.4 kB)
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
2
Best Answer

Answer by Legend_Bacon · Feb 13, 2018 at 06:24 PM

Hello there,

As mentioned above, you really shouldn't be starting coroutines in Update(). There's a risk that you'll be calling StartCoroutine() multiple times a second, which would end up multiplying the performance costs as you'd be running the same coroutine in parallel many, many times.

One thing you could do is declare a coroutine variable at the top: private Coroutine cor_fightCoroutine = null;.

Then, instead of starting a loose coroutine, you can keep a reference to it by doing this:

 if(cor_fightCoroutine == null)    
 {    
      cor_fightCoroutine = StartCoroutine(Fight());    
 }

Then, make sure to end your coroutine with yield return null;. That way, you can only start it when it's not running already.


Another thing about your coroutine in Ball. In the code you posted, you seem to be comparing transform.position to target, which is a Monster. Did you mean targetPos? You also use target in your MoveTowards() function...

instead of having your while loop rely on its own script's existence to keep running, I recommend you directly use the distance. In that case, that would look something like:

 public class Ball
 {
       public float speed;
       private Vector3 targetPos;
  
       IEnumerator Attack(Monster target, int damage)
       {
            targetPos = target.transform.position;
            // replace 0.25f with a variable that would serve as the "I've reached my target" 
            // distance
            while (Vector3.Distance(transform.position, targetPos) > 0.25f)
             {
                 speed *= Time.DeltaTime;
                 transform.position = Vector3.MoveTowards (transform.position, targetPos, speed);
                 yield return null;
            } 
             target.HasBeenDamaged(damage);
             Destroy(gameObject);
             yield return null;
       }
 }



Other than that, I can't see anything else that would be wrong here.


I hope that helps!

Cheers,

~LegendBacon

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 Capricornum · Feb 13, 2018 at 07:04 PM 0
Share

Both of you, thanks a lot. I had to update the question above because when simplifying it for the forum I made 2 mistakes. 1 Fight() is called in the heroScript, not in update. 2. In the ballScript I am comparing the balls.position to targetPos not to target.

With this Updated version do you know why the trouble with my performance is occurring? I am terribly sorry for the mess-up.

avatar image Legend_Bacon Capricornum · Feb 14, 2018 at 09:42 AM 0
Share

No worries, these things happen. Thanks for taking the time to simplify the code before posting it. I assumed that's what you meant anyways, so it doesn't matter!

Other than that, I can't see much that would cause a performance spike here, except perhaps the instantiate call. You could use object pooling if you aren't already.

Did you take a look at the profiler (Window -> Profiler)? It can be very useful to find out what is costing you so much perf at runtime.

Then, maybe throw a Debug.Log inside your coroutines. This will help you find out if they are getting called more than they should.

I hope that helps!

Cheers,

~LegendBacon

avatar image Capricornum Legend_Bacon · Feb 14, 2018 at 03:55 PM 0
Share

Dear Legend_Bacon (I love your name :-))

Thanks a million for taken time. I am really grateful.

This Profiler is amazing. I didn't think I'd be ready to use it yet. But its not too complicated. I have found many interesting things. Apparently my coroutines don't seem to be the problem. Ins$$anonymous$$d its a mixture of (1. Instantiate, like you said. Especially for my particleSystems with Children Objects. I will have to look into object pooling. 2. Debug.Log which is surprisingly costy. 3. A big 'Editor Overhead" which I understand will not be present in the actual build. 4. Some weird thing occasionally happening called: "Font.CacheFontForText" I believe it has to do with "BestFit" being enabled on some UI Text Elements. 5. An occasional EventSystem.Update. Something to do with my mouse and the UI.)

After all maybe its just my macs cooling system. The game runs at 100 FPS and yet he is making noises.

So in other words. Thanks a lot. And cheers to you too :-)

avatar image Capricornum · Feb 13, 2018 at 07:08 PM 0
Share

Thank you for the tip of using distance within while. I will definitely do that.

avatar image anihilnine · Jun 23, 2021 at 07:24 AM 0
Share

yielding null does not stop a coroutine - it makes it wait for 1 frame

avatar image
3

Answer by Lilius · Feb 13, 2018 at 06:03 PM

Is the performance the only issue here? I think you are starting the coroutines each frame when timesToClick is 0. You couldadd bool isFighting, set it to true when coroutine Fight is started, and in the end of the coroutine set it to false. Then in your update start coroutine when timesToClick ==0 AND isFighting == false

       bool isFighting
       
       void Update () {
            if (timesToClick == 0 && !isFighting) {
                 StartCoroutine(Fight());
            }
       }
       
       IEnumerator Fight() {
            isFighting = true;
            foreach (Hero hero in heroes) {
                 hero.Attack();
                 yield return new WaitForSeconds (timeBetweenAttacks);
            }
            foreach (Monster monster in monsters) {
                 monster.Attack();
                 yield return new WaitForSeconds (timeBetweenAttacks);
            }
            grid.UpdateGrid();
            yield return new WaitForSeconds (timeToUpgradeGrid);
            timesToClick = 4;
            isFighting = false;
       }
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 Legend_Bacon · Feb 13, 2018 at 06:07 PM 0
Share

Definitely agree. Starting coroutines in update, even when you are very careful with your Ifs and conditions, is a big no-no in my book.

avatar image Capricornum Legend_Bacon · Feb 13, 2018 at 07:04 PM 0
Share

Both of you, thanks a lot. I had to update the question above because when simplifying it for the forum I made 2 mistakes. 1 Fight() is called in the heroScript, not in update. 2. In the ballScript I am comparing the balls.position to targetPos not to target (as pointed out by you Legend_Bacon in your comment below.

With this Updated version do you know why the trouble with my performance is occurring? I am terribly sorry for the mess-up.

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

100 People are following this question.

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

Related Questions

Coroutine WaitForSeconds ignoring StopAllCoroutines... How can I do it? 0 Answers

Show image then wait for x seconds and hide it again. 0 Answers

Wait time after coroutine's wait seconds is complete 0 Answers

Coroutine not working 1 Answer

Coroutine getting player input before timer runs out help 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