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 /
avatar image
0
Question by MiroslavMilanov · Jun 15, 2018 at 09:13 AM · waitforsecondsspawning problemsloopsspawning-enemies

Problem with WaitForSeconds() in a nested loop

Hi, I'm creating a horizontal wave defense game and I'm stuck at the enemy spawning algorithm. Currently I want to spawn an enemy at a random location every X seconds. Every time a mob spawns it reduces the weightCounter variable which simply said controls how many enemies will spawn. Once it hits 0, the wave ends. Apart from spawning enemies at random locations, there will be different arrangements, that's why I've put a main cycle like this:

 public void SpawnWave()
     {
         impact = gameController.playerStr / gameController.predictedStr;
         waveWeight = weight * impact;
         weightCounter = waveWeight;
         
         while(weightCounter > mobStats.weight)
         {
             StartCoroutine(MobCombination_1_Random());
         }
         weight = (weight + 1) * 1.05f;
         gameController.EndWave();
     }

From it I will call different mob combinations like this one:

 IEnumerator MobCombination_1_Random()
     {
         
         int mobNumber;
         mobNumber = (int)(weightCounter / mobStats.weight);
         if(mobNumber <= 7)
         {
             for (int i = 0; i < mobNumber; i++)
             {
                 Vector2 position = new Vector2(this.transform.position.x, Random.Range(-4f, 4f));
                 GameObject.Instantiate(flyingEnemy, position, Quaternion.identity);
                 weightCounter -= mobStats.weight;
                 Debug.Log("We made an enemy and now we're waiting one second.");
                 yield return new WaitForSeconds(1f);
                 Debug.Log("We waited one second.");
             }
         }
         else if (mobNumber > 7 && mobNumber <= 15)
         {
             int temp = Random.Range(8, mobNumber);
             for( int i = 0; i < temp; i++)
             {
                 Vector2 position = new Vector2(this.transform.position.x, Random.Range(-4f, 4f));
                 GameObject.Instantiate(flyingEnemy, position, Quaternion.identity);
                 weightCounter -= mobStats.weight;
                 yield return new WaitForSeconds(0.7f);
             }
         }
         yield break;
     }

The initial values of weight is 3 and impact is 1. The problem is that instead of creating one enemy every 1 second in this example, it creates 3 then after a second 2 then after another second 1. At that point the weightCounter is less than 0 and should have ended the while loop. Why does the WaitForSeconds() not work in this example?

P.S. I know that the yield break; is pointless, but I've added it just as a future reminder to myself that it's unfinished.

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 Bunny83 · Jun 15, 2018 at 10:37 AM

Your problem is not your coroutine but your SpawnWave method. Coroutines more or less run independently from the rest of your code or from the code that started the coroutine. Your SpawnWave method will run all it's code within the same frame. Actually if you would have some blocking code your game would actually freeze because if you have an infinite loop in a normal method the current frame can never complete.


In your case you're lucky that your game did not freeze. When you call StartCoroutine, Unity will execute your coroutine's code up to the first yield statement. At this point StartCoroutine will return to the caller (in your case back to your SpawnWave method).


So what you're actually doing here is starting 3 seperate coroutines at once which all run in parallel. Since you lower the global "weightCounter" before the first yield your while loop in SpawnWave can actually end properly. Though now you have 3 coroutines running in parallel and each will spawn an enemy in the frame they were started and each one will wait one second. Since you based your mobNumber on the weightCounter when the coroutine was started, the first coroutine has a count of 3, the second one a count of 2 and the last one a count of 1.


After the one second delay all 3 coroutines will continue. The last coroutine which has a count of one is already done with it's for loop and will terminate. The first and second coroutine each will spawn a new enemy and wait for another second. After that delay the two remaining coroutines will go for their next loop iteration, though the second one has reached it's count of 2 and will terminate while the first one spawns another enemy and waits again for one second. After that the first (and last remaining) coroutine will terminate as well.


If you want code to wait for some sequence to finish it has to be a coroutine itself. There is no way to simply wait in a normal method as this would freeze your game. You can simply turn your SpawnWave method into a coroutine and wait for your "MobCombination_1_Random" coroutine to finish.

 public IEnumerator SpawnWave()
 {
     impact = gameController.playerStr / gameController.predictedStr;
     waveWeight = weight * impact;
     weightCounter = waveWeight;
     
     while(weightCounter > mobStats.weight)
     {
         yield return StartCoroutine(MobCombination_1_Random());
     }
     weight = (weight + 1) * 1.05f;
     gameController.EndWave();
 }

Note that it may be simpler and more effective to handle the whole spawning logic in a single coroutine. Starting a coroutine has some overhead. Coroutines are not methods but statemachine objects. Each time you start a coroutine a new instance of that statemachine will be created.


I've once wrote a very simple but powerful spawning system over here. As you can see it's just a single coroutine that runs forever and simply lowers the spawn delays each time the waves have completed. Though you can get fancy how you want to increase difficulty each time. With this simple setup you can already design your waves completely in the inspector. Note that is was just written as a framework. I initially had the classic Warcraft3 tower defence in mind. Some maps spawn all enemies of a wave at once, others spawn one enemy at a time. The actual spawning is still missing. So you can use instantiate with or without a delay between each spawn or use an object pool to spawn the enemies.

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

Answer by EvilWarren · Jun 15, 2018 at 10:17 AM

Your issue is the StartCoroutine() call. You have this a while loop. Remember that whenever you yield in a coroutine it will return to the calling code. In this case your calling code is still in the while loop. I don’t have figures for all your variables but based on your description it looks like the coroutine MobCombination_1_Random() fires off 3 times before weightCounter > mobStats.weight is no longer true. This will occur in one frame. Your 3 coroutines will then terminate dependant on your conditional values and, again from your description, not all at the same time.

Based on the desired behaviour you are describing, you should only start this coroutine once i.e not in a loop.

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

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

86 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

Related Questions

Spawnining Characters and New Levels 0 Answers

How to spawn all humans at once and activate then with time? 1 Answer

Spawn multiple enemies in relation to gameobject [SOLVED] 1 Answer

Question about Unet and spawning 0 Answers

Spawning 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