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
0
Question by CorruptedTNC · Jun 29, 2015 at 04:00 PM · c#errorwaitforsecondsienumerator

Loop with WaitForSeconds in IEnumerator appears to be incorrect by 10-15%

So I've had a quick look around and I've seen some posts about not using loops in IEnumerators, however after reading the information available on the scripting reference there's nothing that I can see about them being particularly resource heavy or incorrect, so I figured I'd ask here.

This is the script I'm using for testing:

 using UnityEngine;
 using System.Collections;
 using UnityEngine.UI;
 
 public class iEnumeratorTest : MonoBehaviour 
 {
     public bool isRecording;
     public float recordingTime;
     public float timeTaken;
     public float tickTime;
 
     void Update()
     {
         if (isRecording)
         {
             recordingTime += Time.deltaTime;
         }
         else
         {
             if (recordingTime != 0f)
             {
                 Debug.Log("That took: " + recordingTime);
                 recordingTime = 0f;
             }
         }
     }
 
     public void ButtonClick()
     {
         StartCoroutine(Tick());
     }
 
     IEnumerator Tick()
     {
         isRecording = true;
         for (float f = timeTaken; f >= 0; f -= tickTime)
         {
             yield return new WaitForSeconds(tickTime);
         }
         isRecording = false;
     }
 }

Update() returns values roughly 10-15% higher than the time it should take to wait. For example, when tickTime was set to 0.1 and timeTaken set to 5, results returned varied between 5.15234 and 5.657412. The results have been similar when using both while and for loops and the actual time is closer to the desired time when increasing tickTime and decreasing timeTaken. This wouldn't be such a big deal however as the wait times are intended to increase dramatically towards the end 10-15% is a great deal of time to be wasted.

Comment
Add comment · Show 1
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 meat5000 ♦ · Jun 29, 2015 at 04:14 PM 0
Share

2 facts you should know.

1) Coroutines will halt a main thread

2) Frame times vary and it is impossible to get an exact wait time as a result :- Especially when your frames are lengthened by waiting on a coroutine.

2 Replies

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

Answer by Baste · Jun 29, 2015 at 04:25 PM

This is due to how Coroutines and WaitForSeconds works. A coroutine is run together with Update, until it's done. If a coroutine yields another IEnumerator, that one is called until it's done, before the main coroutine starts getting called.

WaitForSeconds is implemented somewhat like this:

 IEnumerator WaitForSeconds(float duration) {
     float startTime = Time.Time;
     while(Time.time - startTime < duration)
         yield return null;
 }

This means that it will return on the first Update frame that more time than the duration has passed.

Since this is inevitably more than exactly the duration given as the argument, each WaitForSeconds will add a slight positive error. So, the lower your ticktime, the larger the error.

This isn't actually a big problem - if you want to wait 5 seconds, use WaitForSeconds(5). The effect won't kick in at exactly 5 seconds, but it will kick in at the first frame that's drawn after 5 seconds has passed, so it will look exactly the same as if you somehow magically managed to make the coroutine return at exact 5 seconds.

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 CorruptedTNC · Jun 29, 2015 at 05:00 PM 0
Share

The only reason I'm using a tick system is so I can have a bar fill up gradually while reducing unnecessary resource usage in update, any idea if I can still do this without using IEnumerators or update?

avatar image Baste · Jun 29, 2015 at 05:59 PM 0
Share

Yeah, you set the bar's fill to the current fill percentage:

 bar.fillAmount = (Time.time - startTime) / duration;

Where I assume fillAmount is between 0 and 1.

In general, if you have a UI bar (or element) that's reflecting some variable in a script, you shouldn't have the script both update the value, and keep the bar filled - that'll lead to your script getting huge, and possible introduce bugs where the two gets seperated.

Ins$$anonymous$$d, you should put a new script on the bar that looks at the value in the first script, and fills the bar according to that, blind to what the value actually is representing.

avatar image CorruptedTNC · Jun 30, 2015 at 07:48 AM 0
Share

Filling the bar was never an issue, deciding when to do it was the issue since the previous solution was inaccurate. Having a separate script for essentially 2 lines of code and reading across scripts each frame is going to end up even more inefficient, guess the only other solution is on Update(). Thanks again for the help.

avatar image
-1

Answer by jenci1990 · Jun 29, 2015 at 04:40 PM

The 'Update' method not depend of time. It's called when the camera render was completed. Your coroutine wait fix seconds, so there are some difference. Try frame base coroutine:

 IEnumerator Tick() {
     isRecording = true;
     float f = 0;
     while (f <= timeTaken) {
         yield return new WaitForEndOfFrame();
         f += Time.deltaTime;
     }
     isRecording = false;
 }


Sorry for my english!

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

24 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

Related Questions

How do I re-instantiate an object after it is destroyed from the scene? 1 Answer

Multiple Cars not working 1 Answer

Distribute terrain in zones 3 Answers

Can't get past WaitForSeconds in my coroutine 1 Answer

Problem with using IEnumerator for method, c# 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