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 Pinsukka · Oct 12, 2015 at 07:25 AM · c#coroutinewaitforseconds

How to get precise spawn interval (C#)?

Hello! I am having some problems I did not anticipate with my current enemy spawning system. I have a 2D game and I'm using a coroutine with WaitForSeconds as interval between enemy spawning. The precision of this interval is very crucial to the gameplay experience and because I am well into the development and only noticed this problem now I am pretty worried to keep using my current system.

I made a test project with simplified version of my spawn system without any pooling or such things. In this test I am just instating square sprites that move slowly to right with an interval of 0.77 seconds. Here is the result:

alt text

This test perfectly replicates the problem I am having. As you can see, the spawn interval is not precise. The spacing between the spawned squares varies quite a bit and I have no idea what is behind this problem. Here is the spawn code:

 using UnityEngine;
 using System.Collections;
 
 public class Spawner : MonoBehaviour {
 
     public GameObject squareObj;
     private Vector3[] spawnPos;
     
     void Start () 
     {
         spawnPos = new Vector3[3];
         spawnPos [0] = new Vector3 (-10.0f, -2.0f, 0.0f);
         spawnPos [1] = new Vector3 (-10.0f, 0.0f, 0.0f);
         spawnPos [2] = new Vector3 (-10.0f, 2.0f, 0.0f);
         StartCoroutine ("Spawn");
     }
 
     IEnumerator Spawn()
     {
         while(true)
         {
             Instantiate (squareObj, spawnPos [0], Quaternion.identity);
             Instantiate (squareObj, spawnPos [1], Quaternion.identity);
             Instantiate (squareObj, spawnPos [2], Quaternion.identity);
             yield return new WaitForSeconds(0.77f);
         }
     }
 }

If anyone has any insights on this I would be glad to hear about it, thanks!

spawntest.jpg (18.0 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

3 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Baste · Oct 12, 2015 at 09:49 AM

WaitForSeconds(.77f) does not make the coroutine fire again after exactly .77 seconds - it makes the coroutine fire again the first frame after .77 seconds have passed.

Time.time gives you the exact time the current frame started at. You can use that to find out exactly how late your spawning is, and then move the objects to compensate:

 private IEnumerator Spawn() {
     float extraTime = 0f;

     while (true) {
         Vector3 extraMovement = Vector3.right * movementSpeed * extraTime;

         Instantiate(squareObj, spawnPos[0] + extraMovement, Quaternion.identity);
         Instantiate(squareObj, spawnPos[1] + extraMovement, Quaternion.identity);
         Instantiate(squareObj, spawnPos[2] + extraMovement, Quaternion.identity);
         float spawnTime = Time.time;

         yield return new WaitForSeconds(.77f);

         extraTime = Time.time - .77f - spawnTime;
     }
 }

Where movementSpeed is how fast your spawned objects are moving, and you replace Vector3.right with the direction they're moving in.

You actually probably want to send extraTime to the movement script on the spawned objects, and let them decide how to move to offset that time, but this demonstrates the concept well enough.

Comment
Add comment · Show 4 · 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 Pinsukka · Oct 13, 2015 at 05:48 AM 0
Share

This all makes sense now, thanks a lot!

I tested implementing this concept into the test project and I was able to figure it out. Works like a charm now, I think I should be able to implement this same concept into my actual spawning system.

alt text

spawntest2.jpg (23.1 kB)
avatar image Pinsukka · Oct 15, 2015 at 07:12 AM 0
Share

Ok, I was too hasty to think the problem was solved.

First of all, your script doesn't seem to work as you intended. I wasn't asking for completely ready solution obviously, but because the example you gave is not doing what it is supposed to I am totally lost on this.

I understand the concept you are trying to convey, but for some reason the values don't match up. The extra time I get after spawning is never the same with the actual distance difference between objects, thus nothing gets fixed.

alt text

I printed some values. As you can see, the Diff0 to Diff3 (I have a script that calculates the actual extra distance difference between each gameobject) are all different compared to the extra time values that get printed. Diff1 to Diff3 are roughly equal to Extra Time 1-3, but for some reason the extra time 0 never matches with the actual distance between the first and second gameobject.

I have tried figuring this out for 2 days and came up with nothing, I'm totally confused... $$anonymous$$aybe this sort of spawning system simply isn't suited for my game, when the movement speed gets greater it amplifies this problem so much that it messes up my game totally.

values.jpg (64.8 kB)
avatar image Baste Pinsukka · Oct 15, 2015 at 08:54 AM 0
Share

Let's see;

SpawnTime1 looks correct - the extra time was .0096, which means that it spawns at 2.0096.

SpawnTime2 is off, though. The extra time was 0.0045, but it spawns at 4.014255. Why is that?

What's .0096 + .0045? It's 0.0141!

$$anonymous$$y original solution isn't quite there, as it allows the last spawn time to influence the waiting after the next one, which is an error that builds up. To actually find the accurate spawn time offset, I should have just figured out exactly what the spawn time should have been, and subtracted from that:

 private IEnumerator Spawn() {
     int spawnCount = 0;
     yield return null;

     while (true) {
         float extraTime = Time.time - (spawnCount * 2f);
         spawnCount++;
         Vector3 extra$$anonymous$$ovement = Vector3.right * movementSpeed * extraTime;

         Instantiate(squareObj, spawnPos[0] + extra$$anonymous$$ovement, Quaternion.identity);
         Instantiate(squareObj, spawnPos[1] + extra$$anonymous$$ovement, Quaternion.identity);
         Instantiate(squareObj, spawnPos[2] + extra$$anonymous$$ovement, Quaternion.identity);
         yield return new WaitForSeconds(2f);
     }
 }

In testing, this spawns blocks that are exactly 2 * movement speed appart. With the blocks moving to the right at 1m/s, block number 1 would be at x=21.49924 while block number 10 would be at x=3.499235. The tiny difference there is probably due to rounding errors, and does not accumulate over time.

Now, there's one mystery, and that's the first yield return null. It seems like spawning the first set of blocks with Time.time being 0 gives a bad position. Every block after that will be (almost) exactly 2 metres appart, but the first block will be off by ~.2f unless the script waits one frame first. I'm not quite sure why, though.

avatar image Pinsukka Baste · Oct 15, 2015 at 09:40 AM 0
Share

Hmmm, I get it now...

I was trying to fix things from completely wrong places all along it seems... I rewrote the test spawning script and indeed as you said, the distance between the blocks is exactly correct (well, almost, but the difference is so $$anonymous$$imal that it doesn't matter).

Now the spawning even works with high movement speeds just as precisely!

You saved me, thanks a ton man! You are a genius! :D

avatar image
0

Answer by Garazbolg · Oct 12, 2015 at 08:31 AM

There is a callback that is very precise about time interval and that is FixedUpdate(). You can change the interval at wich the FixedUpdate() is called by changing Time.fixedDeltaTime (at 0.2s by default) HOWEVER its on this same interval that the Physics is updated, thus if you change the value to 0.77 your physic will only be updated once every 0.77s wich isn't much.

If you're not planning on using any physics (Rigidbody and stuff) you can just change Time.fixedDeltaTime to 0.77.

  void Start()
  {
      Time.fixedDeltaTime =0.77f;
  }

 void FixedUpdate(){
       Spawn();
  }

 void Spawn()
      {
              Instantiate (squareObj, spawnPos [0], Quaternion.identity);
              Instantiate (squareObj, spawnPos [1], Quaternion.identity);
              Instantiate (squareObj, spawnPos [2], Quaternion.identity);
      }

If not you could change it to 0.11 sec and in your FixedUpdate Spawn your squares every 7 FixedUpdate() by adding a counter

 int nbFixedUpdate = 0;

 void FixedUpdate(){
      if(nbFixedUpdate++ % 7 == 0)
          Spawn();
 }


 void Spawn()
      {
              Instantiate (squareObj, spawnPos [0], Quaternion.identity);
              Instantiate (squareObj, spawnPos [1], Quaternion.identity);
              Instantiate (squareObj, spawnPos [2], Quaternion.identity);
      }

Hope it answers your question.

Comment
Add comment · Show 3 · 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 Pinsukka · Oct 12, 2015 at 09:15 AM 0
Share

Thanks for the tip! Seems to be precise after I changed the movement script to also update in FixedUpdate:

 void FixedUpdate () 
     {
         transform.Translate (Vector3.right * Time.deltaTime * 2.0f);
     }

However, this is obviously making the movement very clunky. I am not sure if changing the FixedUpdate rate to 0.016666 would be a good idea (~60 frames per second), I have some 2D rigidbodies and colliders to check if the player collides with them or not. I'm not using any complicated physics at all though, just checking collisions and have not used FixedUpdate in my project for anything else so far.

$$anonymous$$aybe I'll try converting my spawn system to this and see how smoothly it works.

avatar image Garazbolg Pinsukka · Oct 12, 2015 at 09:50 AM 0
Share

You should never move objects in FixedUpdate(), as a general rule if you need Time.deltaTime it belongs in Update().

The thing is that update is called each frame unlike fixedUpdate which is called at a regular time interval (0.02 s btw not 0.2s). It is made so that heavy calculation like Physics isn't done every single frame which would have a significent impact on your FPS. And it doesn't still have the advantage to be updated precisely every 0.02s.

https://unity3d.com/learn/tutorials/modules/beginner/scripting/update-and-fixedupdate

You should then move you squares in Update and spawn them in FixedUpdate.

avatar image Pinsukka Garazbolg · Oct 13, 2015 at 05:50 AM 0
Share

I see, thanks for letting me know!

avatar image
0

Answer by YoungDeveloper · Oct 12, 2015 at 10:23 AM

This is the usual problem, especially when you want to instantiate something more than once per frame.

 private void Start(){
      StartCoroutine(Run ());
 }

 private IEnumerator Run(){

     bool flag = true;
     float delay = 0.01f; 
     float currentTime = delay;

     //Test purposes
     Vector3 temp = Vector3.zero;


     while(flag){
         currentTime -= Time.deltaTime;

         while(currentTime < 0){
             currentTime += delay;

             //Do things here

             //Test
             GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
             go.transform.position = temp;
             temp.x += 1.2f;
         }

         yield return null;
     }
 }







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

34 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

Related Questions

automatic door with coroutine 2 Answers

Having trouble figuring out coroutines in C# 2 Answers

Why isn't my simple coroutine working? (and how can I make it infinite?) 2 Answers

Multiple Cars not working 1 Answer

C# WaitForSeconds isn't waiting for any seconds 2 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