Search Unity

Coroutines dying in 5.6?

Discussion in 'Editor & General Support' started by Fizzer, Apr 12, 2017.

  1. Fizzer

    Fizzer

    Joined:
    Nov 16, 2016
    Posts:
    40
    I updated my Unity app to 5.6.0f3 and I'm seeing some coroutines just die. As in, they do a "yield return new WaitForSeconds()" or "yield return null" and then the next line never executes. I'm 100% sure that the object running the coroutine is not being destroyed or deactivated, that StopCoroutine or StopAllCoroutines is not being called, and that I'm also passing a reasonable number of seconds into WaitForSeconds.

    I haven't quite nailed down a repro yet, but I'm wondering if anyone else has seen this problem or might know more information. I've seen it in the editor, standalone Windows builds, and in WebGL builds, and seen it happen in different coroutines throughout in my app.

    When it happens it only happens once, and then everything works fine again until I restart the app. Strangely, it also doesn't seem tied to the first coroutine my app runs, and instead picks some random time to happen.

    I even tried downgrading to 5.5 for a bit and saw the problem go away, and re-upgraded to 5.6 and saw it come back. So I'm pretty sure something is up with 5.6.
     
    Last edited: Apr 12, 2017
  2. mbianchini

    mbianchini

    Joined:
    Aug 25, 2016
    Posts:
    5
    I think there is something buggy with the coroutine structure in Unity 5.6. My coroutines are experiencing problems too with the 5.6 update. I hope this problem has already been discovered by the Unity team. If you figured out something could you please share? Thanks!
     
  3. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Please give more information, it's not a widely known issue as far as I can see... example possible?
     
  4. Fizzer

    Fizzer

    Joined:
    Nov 16, 2016
    Posts:
    40
    Unfortunately I was never able to nail down what caused it. It must be conflicting somewhere with something else my app is doing, but I can't for the life of me figure what that would be.

    I did some make some progress tracking it. I found a reliable repro, and kept commenting out big sections of my app until the repro went away. Doing this for several hours narrowed it down to two image objects I was inserting into a dialog. If I commented out both of those images, coroutines worked fine.

    The code causing the issue narrowed down to simply "new GameObject("...").AddComponent<Image>()". I realized that these objects were being inserted into a canvas with normal Transforms instead of RectTransform. Changing them to RectTransforms fixed it! "new GameObject("...", typeof(RectTransform)).AddComponent<Image>()"

    So I added some asserts and checks to my app to ensure that I was always ensuring I only ever add RectTransform objects to my canvases. I found a few other places where I had forgotten to do this. After fixing all of these, the coroutine problem happened... less often. But it still happens. And now I can't reliably repro it.

    In the end I had spent way too much time on this issue, so I ended up just writing my own coroutine system and stopped using Unity's. It was actually pretty easy to write once I thought it through. This has worked around the problem for me.

    It would be really nice if there was a way to debug it. I want a way to say "tell me all coroutines that are active on this MonoBehaviour, and what it's waiting on." I want to see if the coroutine is really deleted, or if it's still in memory on Unity's side and blocked on something. But Unity does not expose this information anywhere as far as I can tell, even in the editor.
     
    doriathen likes this.
  5. Vectorbox

    Vectorbox

    Joined:
    Jan 27, 2014
    Posts:
    232
    I've been scripting a few coroutines in 5.6 over the last few days and I haven't noticed any 'non-completion' problems. I appreciate the fact that your coroutines work in 5.5, but have you considered adding a conditional boolean to force completion ?. At very least it may help to shed some light on the issue.

    Example

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class RoutineTest : MonoBehaviour {
    6.     private bool routineActive;
    7.  
    8.     // --
    9.     void Update () {
    10.         if (!routineActive && Input.GetKeyDown (KeyCode.Z)) {
    11.             StartCoroutine (TargetCoroutine ());
    12.         }
    13.     }
    14.     // --
    15.     private IEnumerator TargetCoroutine () {
    16.         routineActive = true;
    17.         //
    18.         // ** Code **
    19.         //
    20.         yield return new WaitForSeconds (2.0f);
    21.         routineActive = false;
    22.     }
    23. }
    24.  
     
  6. mbianchini

    mbianchini

    Joined:
    Aug 25, 2016
    Posts:
    5
    At the moment I am not able to reproduce the effect exactly to find out what is causing it. The conditional boolean is a good way to test it. I'll try to nail the cause adn answer with more information. Thank you all for the info.
     
  7. Fizzer

    Fizzer

    Joined:
    Nov 16, 2016
    Posts:
    40
    Yes, I've added flags like what you suggest to watch. And what I see happening is that, the line after "yield return new WaitForSeconds" is never called. I could leave the app open for an hour, but the routineActive variable would stay true forever.

    I've also added Debug.Log statements before and after the "yield return new WaitForSeconds" and what I see is that, when this happens, the before one gets called, but the after one never does.

    I should also mention, no errors or warnings are in the console, and I'm 100% sure that no exceptions are being thrown (there's no try/catch or anything like that)

    Thanks for the suggestion!
     
  8. Vectorbox

    Vectorbox

    Joined:
    Jan 27, 2014
    Posts:
    232
    Try attaching the following 'TargetRoutine' script to an empty game object. It's working as expected here.

    TargetRoutine Script

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class TargetRoutine : MonoBehaviour {
    7.     public List<string> testList;
    8.     public bool routineActive;
    9.  
    10.     // --
    11.     void Awake () {
    12.         testList = new List<string> ();
    13.     }
    14.     // --
    15.     void Start () {
    16.         PopulateTestList ();
    17.     }
    18.     // --
    19.     void Update () {
    20.         if (!routineActive && Input.GetKeyDown (KeyCode.Z)) {
    21.             StartCoroutine (targetRoutine ());
    22.         }
    23.     }
    24.     // --
    25.     void PopulateTestList () {
    26.         for (int i = 0; i < 10; i++) {
    27.             testList.Add ("Item " + i);
    28.         }
    29.     }
    30.     // --
    31.     private IEnumerator targetRoutine() {
    32.         routineActive = true;
    33.         //
    34.         Debug.Log ("Start Coroutine");
    35.         //
    36.         foreach (string item in testList) {
    37.             Debug.Log (item);
    38.             yield return new WaitForSeconds (0.25f);
    39.         }
    40.         Debug.Log ("End Coroutine");
    41.         routineActive = false;
    42.     }
    43. }
    44.  
     
  9. Cyborn

    Cyborn

    Joined:
    May 6, 2016
    Posts:
    2
    if I execute my code like this, everything works again, but it definitely looks like a bug to me.

    A co routine does not seem to be executed anymore as soon as it passed any yield return statements.
     
  10. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I tested the code directly above this post and works fine to me. This is expected behaviour. I get End Coroutine message when it is finished.

    Do you have any code relating to Time class?
     
  11. Vectorbox

    Vectorbox

    Joined:
    Jan 27, 2014
    Posts:
    232
    If someone could upload an example script (one that fails to complete), I'd be happy to test it on my system.
     
    Last edited: Apr 14, 2017
  12. Cyborn

    Cyborn

    Joined:
    May 6, 2016
    Posts:
    2
    I tested a bit more and narrowed it down to my coroutines not working if I add them on anything that is directly on a canvas, if I attach them to another gameobject it does work fine.

    public class TestTester : MonoBehaviour {

    // Use this for initialization
    void Start () {
    StartCoroutine(HurpDurp());
    }

    private IEnumerator HurpDurp()
    {

    while (true)
    {
    Debug.Log("HurperDurper");
    yield return null;
    }
    }


    // Update is called once per frame
    void Update () {

    }
    }

    this code works on a regular gameobject in my scene, if I attach it to my canvas or a gameobject in a canvas, it stops executing the first time it reaches the yield return statement
     
  13. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Probably best you file a bug report (help menu->etc) and attach your example so Unity can reproduce if you think it's an issue. It's just not an issue I can find.
     
  14. Vectorbox

    Vectorbox

    Joined:
    Jan 27, 2014
    Posts:
    232
    I've tested your script attached to a canvas button with the following result...

     
  15. electroflame

    electroflame

    Joined:
    May 7, 2014
    Posts:
    177
    For what it's worth I've got this issue as well. At first I thought I was going crazy, but it appears to be somewhat random and intermittent. It absolutely has something to do with yielding, though, as you can set a break point before a yield, and after a yield, and the before-yield will break correctly, but the after-yield break will never be hit. I'm not sure if the coroutine itself is dying, or if the yield is somehow never returning -- either way sometimes you'll get a crazy yield-of-death that stops a coroutine (or coroutines) dead and there's no way to detect it or recover.

    It's definitely a 5.6 issue, as I have completely unchanged code that worked great in 5.5 that no longer works in 5.6. Given its random nature, I'm having a real tough time finding a reliable repro case. In my case, it's got nothing to do with UI or Canvases (at least, it hasn't affected my UI yet), so I don't think it's related solely to coroutines and UI.

    I'm pretty much at a loss here. Thankfully I found this thread as I was getting crazy debug results and was getting increasingly confused. If anyone's got any ideas I'm game for a bit of debugging, but after spending most of today on this I can't seem to find a rhyme or reason to this, aside from being as certain as I can be that this happens on yields (WaitForSeconds seems to trigger this the most frequently). Really strange, but also super frustrating!
     
    doriathen likes this.
  16. Vectorbox

    Vectorbox

    Joined:
    Jan 27, 2014
    Posts:
    232
    I've been unable to reproduce the issue so it's hard to track down the culprit, but I'd like someone to navigate to Edit / Project Settings / Time and check that Time Scale is not set to zero

    You could also try adding the following to the script...

    Code (csharp):
    1.  
    2. void Awake () {
    3. Time.timeScale = 1.0f;
    4. }
    5.  
    This will at very least eliminate a time scale issue
     
  17. aicam-spain

    aicam-spain

    Joined:
    Apr 17, 2017
    Posts:
    3
    We have the same problem in our projects. We have been testing, and we have arrived at the conclusion that the problem is due to the coexistence of an active canvas and coroutines in a same script.

    If we activate a canvas before the start of the coroutine, and do nothing else inside except waiting for 0.5 seconds, the execution fails. An example of the coroutine is the next:
    Code (CSharp):
    1. private IEnumerator Test()
    2. {
    3.      while (true)
    4.      {
    5.           Debug.Log("Testing...");
    6.           yield return new WaitForSeconds(0.5f);
    7.      }
    8. }
    9.  
    But as long as we didn't touch the canvas, execution seemed fine.

    Another test was adding a public variable with a canvas attached, and trying three things with the state of the canvas:
    1. Canvas deactivated and then start the coroutine: Everything is fine.
    2. Canvas activated and then start the coroutine: Not working.
    3. Canvas deactivated and then start the coroutine, activating the canvas after a few seconds: Working only as long as the canvas is not active.
     
    Last edited: Apr 18, 2017
  18. electroflame

    electroflame

    Joined:
    May 7, 2014
    Posts:
    177
    I can rule this out on my end. I double-checked that Time Scale wasn't zero in the inspector and I even set it in my start-up script (setting it straight to 1, and also setting it to 0 and then 1 just in case) and it didn't make a difference. Coroutines still randomly break.

    I'm still not sure about the UI thing, as I'm having Coroutines break on standard MonoBehaviours that have no references to any UI stuff. UI exists in the scene, but even if that's disabled or removed Coroutines still randomly break.
     
  19. electroflame

    electroflame

    Joined:
    May 7, 2014
    Posts:
    177
    I've done a little bit of extra digging, so here's where I'm at right now:

    I created this class:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. /// <summary>
    4. /// Suspends the coroutine execution for the given amount of seconds.
    5. /// </summary>
    6. public class WaitForSeconds : CustomYieldInstruction
    7. {
    8.     private float waitTime;
    9.  
    10.     public override bool keepWaiting
    11.     {
    12.         get { return Time.time < waitTime; }
    13.     }
    14.  
    15.     public WaitForSeconds(float time)
    16.     {
    17.         waitTime = Time.time + time;
    18.     }
    19. }
    Which basically hides the default WaitForSeconds and allows me to set a breakpoint inside the keepWaiting property. While watching the Time.time < waitTime, I discovered that it indeed looks like the Coroutine is being killed, or dying. The behaviour I'm seeing is:
    • The keepWaiting breakpoint is being hit correctly. It's waiting for the statement to be false.
    • Eventually, after a few hits but before the statement finishes (i.e. evaluates to false) the breakpoint stops being hit.
    • The only conclusion I can make from this is that after a while (when the yield gets close to completing) something happens that stops the evaluation dead. keepWaiting is no longer checked, even though the statement never evaluated to false (which means the IEnumerator never continues).
    At this point I'm pretty sure this is a bug. I'm thinking that the only thing to do is to roll back to 5.5, as something seems to be fundamentally wrong with the way 5.6 handles IEnumerators and yielding. I wish there was more I could do in terms of making this reproducible, but as far as I can tell this is completely random. Once it starts happening, though, I can't find a way to track it down or get rid of it.
     
  20. Deleted User

    Deleted User

    Guest

    I have the same problem with my project here I am going to file a bug report. Hopefully they will fix it soon.
     
  21. aicam-spain

    aicam-spain

    Joined:
    Apr 17, 2017
    Posts:
    3
    We've also been doing a few extra experiments, in particular with a plugin from the asset store (Graph and Chart).

    In our experience, coroutines (including those with yield wait) seem to work fine when we eliminate a function from the plugin that essentially creates and manipulates a canvas.
    Oddly, it also seems to work fine if the object with the script (and thus, the canvas) is set in the root of the hierarchy, but that could be related to a conditional included in the plugin's script that searches for a canvas on the object's parent.
    To test this, we tried adding a canvas to a separate object from the same script, and tested with said object on the root and as a child of a different object:
    • When the object was on the root, adding a canvas didn't stop the coroutine.
    • When the object was a child of a different object, adding a canvas immediately caused the coroutines to stop.
    For now, we've decided to roll back to 5.5, but we'll keep doing some tests with this plugin on 5.6 until the problem is solved.
     
  22. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Well the blog clearly warns about timescale and co-routine, but it doesn't explain it being intermittent with timescale = 1. Bug reports will solve it though :)
     
  23. b_bachmann

    b_bachmann

    Joined:
    Sep 21, 2012
    Posts:
    2
    i think it's a timing problem. In my project the problem occours in a script where the coroutine runs on Start(). Put the script in "Script Execution Order" after DefaultTime solves the problem for me :)
    Strangely, the problem does not occur when larger resources from a Resources-folder must loaded.
     
  24. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    But the real problem is undiagnosed if it was working fine in 5.5 or below. That's why anyone who uses coroutines (I do not) should file a bug report just in case there are other issues lurking :)
     
  25. b_bachmann

    b_bachmann

    Joined:
    Sep 21, 2012
    Posts:
    2
    you are right! I published to WebGL and on some devices the Coroutines runs fine and on other they break
     
  26. keraj37

    keraj37

    Joined:
    Nov 25, 2011
    Posts:
    28
    Same here, my solution for now is to rollback to 5.5. I hope it is going to be fixed soon.
     
  27. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Hello.
    There is a known bug that is likely the cause of some or even all of these issues.
    If you add a UI element to a GameObject and the GameObject does not contain a RectTransform then the old transform will need to be replaced. This process causes the GO to be disabled, old transform replaced for a RectTransform and then re-enable the GO. However when the GO is disabled the OnDisabled message is fired and causes issues such as coroutines stopping. The current workaround to this is to create your GameObjects with RectTransforms.
    Like so

    Code (CSharp):
    1. var go = new GameObject("name", typeof(RectTransform));
    Now the swap will not need to occur and your problems should be solved.
    We are working on a fix for this but for now just ensuring any GameObjects that are created have a RectTransform will fix the issue and is probably better for performance(like 0.00001% ;))
     
  28. keraj37

    keraj37

    Joined:
    Nov 25, 2011
    Posts:
    28
    Thanks for this tip, but I am quite sure it is not a case in my project.

    Unless I don't understand something here: do you mean that creating any GameObject without RectTransform will cause disabling all components including those attached to other GameObjects?

    My coroutines are broke on a GameObject that for sure has RectTransform as it is created in scene graph in hierarchy and is not instantiated dynamically.
     
  29. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,282
    Its possible that yours is a different problem. I would need to see the project. Have you filed a bug report?
     
  30. keraj37

    keraj37

    Joined:
    Nov 25, 2011
    Posts:
    28
    Thanks, I will then fill the bug report.
     
    karl_jones likes this.
  31. KarlKarl2000

    KarlKarl2000

    Joined:
    Jan 25, 2016
    Posts:
    606
    I'm getting similar errors with "time.deltatime" I use alot the following code:
    Code (CSharp):
    1.  
    2. float Timer = 0;
    3. float CoolDown = 2;
    4. bool something = false;
    5.  
    6. Void Stuff()
    7. {
    8.  Timer += Time.deltaTime;
    9.  if (Timer >= CoolDown)
    10.  {
    11.   something = true;
    12.  }
    13. }
    14.  
    I notice 5.6 isn't accurate in counting deltaTime and won't always trigger the code after reaching "CoolDown"
     
    Last edited: Apr 30, 2017
  32. Deleted User

    Deleted User

    Guest

    I have changed my code according to this and the bug still persists to be there. I guess the issue we are having is different. I have also filed a bug about this already.
     
  33. Sparrowfc

    Sparrowfc

    Joined:
    Jan 31, 2013
    Posts:
    100
    I've met the same issue! And it only happens in iOS. After playing the game for some time, maybe 10 min maybe the whole day, all my assetbundle can no longer be loaded. After several test and log I'm sure the problem is caused by coroutine unresponding for no reason. All my download coroutine unresponded after 'yield retrun www'.
    So far this thread is the closest one to my problem. Hoping for some real help!
    P.S. also found an old thread, gona try it first. https://forum.unity3d.com/threads/fixed-coroutine-issue-coroutine-stopped-for-no-reason.329317/
    PPS IT'S DEFINITELY AN UNITY BUG!!!! I've checked all the stupid faults. the code is like below:
    Code (CSharp):
    1. IEnumerator LoadImg(string url)
    2. {
    3.     if (!enabled || !gameObject.activeInHierarchy)
    4.         Debug.LogError("Manager is Disabled!");
    5.     WWW www = new WWW(url);
    6.     Debug.Log(url + " | Start");
    7.     yield return www;
    8.  
    9.     Debug.Log(url + " | Finish");
    10.     .....
    11. }
     
  34. keraj37

    keraj37

    Joined:
    Nov 25, 2011
    Posts:
    28
    Just a note for unity dev team: in my project we are targeting WebGL and this problem doesn't occur in WebGL build. It only occurs in editor. So for me it is just fine, as I can easily take care in editor of it.
     
  35. Alloc

    Alloc

    Joined:
    Jun 5, 2013
    Posts:
    241
    Has there been any progress to this issue? We can't really run our game at all on 5.6, it hangs at totally different occurences (sometimes at starting to the main menu, sometimes when loading a game, sometimes after loading right when entering the actual gameplay) and works just fine on 5.3. As a lot of these places use coroutines (e.g. all the loading before main menu or before entering gameplay is done in coroutines) I wonder if this might be related, especially due to the randomness of the issue.
     
  36. electroflame

    electroflame

    Joined:
    May 7, 2014
    Posts:
    177
    I've been watching all of the various threads on this issue and there hasn't really been any movement on it (at least publicly).

    Really a shame. Can't update to 5.6 with this, as it breaks pretty much everything to some extent. Seeing multiple patch releases go by and no word or estimate on this is pretty disappointing. Especially if it's tied to the RectTransform thing (which I'm not even sure of) then Unity has a fully fleshed-out bug report open and there's been no public activity on it.

    Sticking with 5.5 seems like the only solution right now.
     
  37. X28

    X28

    Joined:
    Jul 15, 2015
    Posts:
    8

    thank you !!
    you save me ;)
     
    karl_jones likes this.
  38. AmbOcclusion

    AmbOcclusion

    Joined:
    Sep 17, 2012
    Posts:
    33
    I'm experiencing this issue and I'm super mega willing to pass my full project to someone at Unity if it means this can get pushed forward. It is reproducible 100% of the time on the same coroutine in my project.

    Situation:
    My game has a countdown timer at the start of a match
    • Countdown UGUI gameobject actives are set to false
    • yield return new WaitForSeconds(1.0f);
    • Start a loop to enable the countdown UGUI gameobjects one at a time, and do GetComponent<Animation>().Play()
    • yield return new WaitForSeconds(1.0f); Between each loop iteration
    My coroutine will stop at any of the WaitForSeconds either before or during the loop. Sometimes it'll countdown 3...2...Nothing, others it just goes 3...Nothing.

    There are bug fixes in 5.6.1 I want to take advantage of, and I'm on the verge of dropping every coroutine in my game.
     
  39. CDNexus

    CDNexus

    Joined:
    Apr 15, 2017
    Posts:
    2
    I think I am encountering this issue, I have two different entry points for starting the same co-routine. Depending on which is used the routine never returns after the first:
    Code (CSharp):
    1. yield return new WaitForSeconds(...)
     
  40. CDNexus

    CDNexus

    Joined:
    Apr 15, 2017
    Posts:
    2
    I added a dummy OnDisable override to my class, then added a breakpoint. Sure enough. The call stack showed that at one point I added a game object it was disabling the class holding on to my coroutines.

    Once I overwrote it as above, it solved my instance of this bug. Thanks @karl_jones
     
  41. sigono-chao3

    sigono-chao3

    Joined:
    Jun 16, 2017
    Posts:
    2
    In our case, we called
    Code (CSharp):
    1. UnityEditor.PrefabUtility.ConnectGameObjectToPrefab
    in editor play mode by mistake. But nothing happened in 5.5.

    After we upgraded our project to 5.6, call to this in play mode will disable entire hierarchy then enable it immediately, killing all the coroutines in that hierarchy.
    And it would only happen in Windows editor, not in Mac editor.

    Tested with 5.6.2f1 and 5.6.2p1
     
  42. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    I never had any issue with Coroutineswith 5.6.1 . I had some allocation issue on prevs versions. So good job on improving Coroutines. Make sure you trigger IEnumaroter correctly.
     
  43. markoal

    markoal

    Joined:
    Aug 31, 2015
    Posts:
    30
    I had some troubles also with coroutines in 5.6.1f1 and WaitForSecondsRealtime, so it might help someone.
    I had this code:

    Code (CSharp):
    1. protected void Awake()
    2.     {
    3.         _wait = new WaitForSecondsRealtime(RealTimeChangeDuration);
    4.     }
    5.  
    6.     protected override void OnCallback()
    7.     {
    8.             StartCoroutine(ChangeTimeScale());
    9.     }
    10.  
    11.     IEnumerator ChangeTimeScale()
    12.     {
    13.         Time.timeScale = TargetTimeScale;
    14.         Time.fixedDeltaTime = TargetFixedTimestep;
    15.         yield return _wait;
    16.  
    17.         Time.timeScale = DEFAULT_TIME_SCALE;
    18.         Time.fixedDeltaTime = DEFAULT_FIXED_TIMESTEP;
    19.     }
    And I realized that yield return _wait works only for the first time, I always use this approach for WaitForSeconds, however, if you are using WaitForSecondsRealtime you need to create a new variable on each call. I did improve my code and added some flags to make sure coroutine that manages Time is not started before the old one is finished.

    Code (CSharp):
    1. protected override void Awake()
    2.     {
    3.     }
    4.  
    5.     protected override void OnCallback()
    6.     {
    7.         if (!_isInCoroutine)
    8.             StartCoroutine(ChangeTimeScale());
    9.     }
    10.  
    11.     IEnumerator ChangeTimeScale()
    12.     {
    13.         _isInCoroutine = true;
    14.         Time.timeScale = TargetTimeScale;
    15.         Time.fixedDeltaTime = TargetFixedTimestep;
    16.         yield return new WaitForSecondsRealtime(RealTimeChangeDuration);
    17.  
    18.         Time.timeScale = DEFAULT_TIME_SCALE;
    19.         Time.fixedDeltaTime = DEFAULT_FIXED_TIMESTEP;
    20.         _isInCoroutine = false;
    21.     }