Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Resolved Coroutines acts strangely in WebGL

Discussion in 'Scripting' started by hyperscroll, Apr 28, 2021.

  1. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    Hi, I encountered strange behaviour of coroutines in my WebGL game. I want to spawn lights in the loop with a varying delay between the iterations. The best way was to make a coroutine and I did just that and it works perfectly in the editor. The problem occurs only in a web browser - it looks like coroutines are completely out of sync, e.g one light shows up for an excessive amount of time, then disappears and two lights show up at once, even though they should be spawned sequentially. At this point, I am completely out of ideas, as coroutines are said to be working in WebGL.
    My workflow in scripts:
    By pressing a button, the function StartGame is invoked (invoking occurs in GameManager script) in which coroutine is started (this occurs in SpawnLights script). In coroutine, for each loop starts in which lightPrefab is instantiated and time interval is calculated.
    Also note, that the code might not be the most efficient since this is a very short and supposedly fast project.
    Thank you for any help in advance!

    In GameManager:
    Code (CSharp):
    1. public void startClick()
    2.     {
    3.         inGame = true;
    4.         inGameUI.gameObject.SetActive(true);
    5.         spawnLights.Invoke("StartGame", 1);
    6.     }
    In SpawnLights:
    Code (CSharp):
    1. IEnumerator DisplayCoroutine()
    2.     {
    3.         foreach (var letter in sequence)
    4.         {
    5.             yield return new WaitForSecondsRealtime(intervalTime);
    6.             letterCounter++;
    7.             lightComponent = lightPrefab.GetComponent<Light>();
    8.             lightComponent.color = coloursDict[letter];
    9.             Instantiate(lightPrefab, GenerateSpawnPosition(), lightPrefab.transform.rotation);
    10.             Debug.Log("Curent interval: " + intervalTime);
    11.             intervalTime = calculateInterval(letterCounter);
    12.         }
    13.         gameManager.Invoke("ShowGameOver", 4);
    14.     }
    15.  
    16. public void StartGame()
    17.     {
    18.         GenerateSequence(gameManager.sequenceLength);
    19.         StartCoroutine(DisplayCoroutine());
    20.     }
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    There is nothing wrong with coroutines in webGL.

    Also, just because things work one way in the editor doesn't mean it always is the same in the build.

    The first thing I see is you are grabbing the Light component off your lightPrefab. Note that changing the prefab is not what you normally want to do. Usually you'll want to instantiate the copy, then access the copy and change it's settings. Changing the prefab can have odd results sometimes depending on what you change. Keep that in mind for the future.

    Normally you'd want to target the instantiated object and not the prefab.

    Can you show your calculateInterval method?
     
    Joe-Censored likes this.
  3. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    Thanks for the reminder about object copies, must have overlooked that.
    Here is the calculateInterval method:

    Code (CSharp):
    1. private float calculateInterval(int nucNumber)
    2.     {
    3.         return (Mathf.Pow(difficultyFactor, nucNumber)) * maxInterval + minInterval;
    4.     }
    But I'm doubting it has something to do with those oddities.
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    Have you tried using just WaitForSeconds instead of WaitForSecondsRealtime? I know I don't use the Realtime version often.

    Some possible issues are either it's running multiple times, possibly from registering a double click or that intervalTime was creating a value small enough at some point to allow it to quickly run the loop.

    I have used coroutines fine on webGL myself, so that shouldn't be an issue.
     
  5. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    intervalTime value is probably not an issue, because a constant time of 2 seconds produced the same artefacts. But I'm trying to tweak those values and rounding the intervalTime to two decimal places gave some smoother appearance of the lights, oddly.
    I even managed to avoid the coroutines, by implementing a system based on invokes but again, it worked in the editor, not on the website.

    Code:

    Code (CSharp):
    1. public void DisplayLights()
    2.     {
    3.         letterIndex++;
    4.         letterCounter++;
    5.         intervalTime = calculateInterval(letterCounter);
    6.         char currentLetter = sequence[letterIndex];
    7.         GameObject prefabInstance = Instantiate(lightPrefab, GenerateSpawnPosition(), lightPrefab.transform.rotation);
    8.         Light instancedLightComponent = prefabInstance.GetComponent<Light>();
    9.         instancedLightComponent.color = coloursDict[currentLetter];
    10.         if (!(letterCounter >= sequence.Length))
    11.             Invoke("DisplayLights", (float)intervalTime);
    12.         Debug.Log("Curent interval: " + intervalTime);
    13.         if (letterCounter == sequence.Length)
    14.             gameManager.Invoke("ShowGameOver", 4);
    15.     }
    I will try some adjusted values combined with WaitForSeconds, but I used WaitForSeconds for my first run and it produced the problem.
     
  6. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,793
    A timer incremented by +=Time.deltaTime will be much more performant and doesn't need garbage collecting.
     
    Joe-Censored likes this.
  7. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
  8. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    I will try to do that.


    I will check it too.
     
  9. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,887
    Put StopAllCoroutines before the StartCoroutine, or block it with an IsRunning check.
     
  10. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    I did that - no change.

    No errors during gameplay and all printed intervalTime values are correct.


    Tried it out and no change.

    To visualise the problem, here are the recordings - the one with smooth transitions with 3 lights most of the time active is the right one, from the editor. The other is from the website (sorry for the noise - turn down your volume!).

    Website version:


    Editor version (how it should be):
     
  11. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    What are you using to turn off the lights?
     
  12. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    Each light prefab has a controlling script that tweens its intensity to a certain value, then invokes a method that tweens its intensity to zero and then the object is destroyed.
    Originally, I interpolated their intensity (using Mathf.Lerp), but the problem also appeared.

    Code (in LightController):

    Code (CSharp):
    1. void Start()
    2.     {
    3.         lightComponent = GetComponent<Light>();
    4.         lightComponent.intensity = 0.0f;
    5.         LeanTween.value(0.0f, maxIntensity, birthTime).setOnUpdate((float value) =>
    6.         {
    7.             float inter = lightComponent.intensity;
    8.             inter = value;
    9.             lightComponent.intensity = inter;
    10.         }).setEase(LeanTweenType.easeInQuad);
    11.         Invoke("TweenFadeOut", 4);
    12.     }
    13.  
    14. void TweenFadeOut()
    15.     {
    16.         LeanTween.value(maxIntensity, fadeOut, transitionTime).setOnUpdate((float value) =>
    17.         {
    18.             float inter = lightComponent.intensity;
    19.             inter = value;
    20.             lightComponent.intensity = inter;
    21.         }).setEase(LeanTweenType.easeInQuad).setOnComplete(() => Destroy(gameObject));
    22.     }
     
  13. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
  14. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    I swapped it, it's all working in the editor but there is no change on the website, sadly.
     
  15. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    I'm going to say there is something else going on then. There may just be that one obscure thing that hasn't come to light yet that is making it behave like it is. Unfortunately, in this case with it behaving in the editor, it may be tough to solve it.

    I've dealt with this in a few webGL games that I had to debug. It takes a lot of running the browser Javascript debugger and putting in Debug messages from my experience.

    I guess the other option is, in the SetOnComplete call of the FadeOut script, you could set it up to call DisplayLights() which should guarantee it doesn't try to spawn another light until the previous is faded out. Or, depending on the time needed, maybe even set a SetOnComplete in the FadeIn tween (again, depending on when you wanted it to try to spawn a new light). But playing around with this could give you a bit more flexibility.

    Unfortunately, I honestly don't have much more I can offer with the info I have.
     
  16. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    Thanks for your effort. I will be looking into this and maybe it's a good idea to move the project to a newer version (for some not obvious reasons I'm using 2018.4.32f1)
     
  17. hyperscroll

    hyperscroll

    Joined:
    Nov 28, 2013
    Posts:
    9
    Changing the Unity version helped. Thank you all for your effort!
     
  18. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,539
    This had nothing to do with coroutines but with the used rendering / lighting method and light limits. Those differ from target platform to target platform and of course from Unity version to Unity version. You can also check the player settings for each platform. Different lighting models allow different amount of lights to light up the same object at once. My guess would be that you try to light up a single object with too many lights for your used lighting model. Unity does sort the lights by priority and only applies the one which has the greatest influence on the object. There's no light limit when it comes to static / precomputed lights (it just takes longer to build), but having many dynamic / realtime lights that affect the same surface / object will always cause issues. I think the global illumination model does support much more lights than in the past. Though be careful as you may explode your memory usage that way. See this thread for a bit of insight.
     
    hyperscroll and Brathnann like this.