Search Unity

Question Constructing Coroutines Dynamically?

Discussion in 'Scripting' started by colinsauce00, Jan 11, 2024.

  1. colinsauce00

    colinsauce00

    Joined:
    Jul 13, 2021
    Posts:
    9
    Hello all, I have a system of lighting up little platforms upon players standing on them and unlighting them when they step off of them using coroutines and loops. The code works fine and this is solely a question on if it is possible to refine my method of doing so as my programming abilities seem to have reached their limit for this. The optimization I am trying to reach is not having to have 38 coroutines for the 38 respective blocks. I used to do this with a couple coroutines that would manage all the blocks but if a player were to quickly step on and off 2 or more blocks glitches would appear. The issue was that a coroutine that could handle all of them does not know WHICH block is being stepped off of so how does it know to unlight specifically that block? Looking at the code and how the loops work will show what I mean when you try to think of a system where one coroutine could do this:

    I only am showing it with 2 blocks and one coroutine. Coroutine "F2" would simply be F1 copy and pasted. OneObj and TwoObj are the gameobjects of the blocks. This is how I check which block specifically is being stepped off of or onto. bools One and Two simply are there to make it so the coroutines only get called once ie. do not keep trying to light the same block. I am passing true or false into the coroutine for lighting or dimming respectively and passing the gameobject of the block. Once again, this only shows if two blocks were in the system and only shows one coroutine since "F2" would just be a copy of coroutine "F1".

    Code (CSharp):
    1. private void OnTriggerStay2D(Collider2D other)
    2.     {
    3.         if (other.gameObject.tag == "Glow")
    4.         {
    5.             if (OneObj == other.gameObject && !One)
    6.             {
    7.                 One = true;
    8.                 StopCoroutine("F1"); //Stops dimming if it is dimming. Would not call if already lighting.
    9.                 StartCoroutine(F1(true, other.gameObject));
    10.             }
    11.             if (TwoObj == other.gameObject && !Two)
    12.             {
    13.                 Two = true;
    14.                 StopCoroutine("F2");
    15.                 StartCoroutine(F2(true, other.gameObject));
    16.             }
    17.         }
    18.  
    19.     }
    20.     private void OnTriggerExit2D(Collider2D other)
    21.     {
    22.         if (other.gameObject.tag == "Glow")
    23.         {
    24.             if (OneObj == other.gameObject && One)
    25.             {
    26.                 One = false;
    27.                 StopCoroutine("F1");
    28.                 StartCoroutine(F1(false, other.gameObject));
    29.             }
    30.             if (TwoObj == other.gameObject && Two)
    31.             {
    32.                 Two = false;
    33.                 StopCoroutine("F2");
    34.                 StartCoroutine(F2(false, other.gameObject));
    35.             }
    36.         }
    37.     }
    38.  
    39.  
    40.  
    41.     public IEnumerator F1(bool Lighting, GameObject Obj)
    42.     {
    43.         if (Lighting)
    44.         {
    45.             yield return new WaitForSecondsRealtime(0.5f);
    46.             UnityEngine.Light FirstLight = Obj.GetComponentsInChildren<UnityEngine.Light>()[0];
    47.             UnityEngine.Light SecondLight = Obj.GetComponentsInChildren<UnityEngine.Light>()[1];
    48.             UnityEngine.Light ThirdLight = Obj.GetComponentsInChildren<UnityEngine.Light>()[2];
    49.             float BlockLightI = FirstLight.intensity;
    50.             for (float i = 0f; i <= 3; i += 0.05f)
    51.             {
    52.                 if (One) //Although not needed, if coroutine somehow does not stop this will stop the loop.
    53.                 {
    54.                     float NewIntensity = Mathf.Lerp(BlockLightI, 10f, i / 3f);
    55.                     FirstLight.intensity = NewIntensity;
    56.                     SecondLight.intensity = NewIntensity;
    57.                     ThirdLight.intensity = NewIntensity;
    58.                     yield return null;
    59.                 }
    60.                 else
    61.                 {
    62.                     yield break;
    63.                 }
    64.             }
    65.         }
    66.         else //Not lighting so dimming. Is just what is above but backwards for the loop stuff.
    67.         {
    68.             UnityEngine.Light FirstLight = Obj.GetComponentsInChildren<UnityEngine.Light>()[0];
    69.             UnityEngine.Light SecondLight = Obj.GetComponentsInChildren<UnityEngine.Light>()[1];
    70.             UnityEngine.Light ThirdLight = Obj.GetComponentsInChildren<UnityEngine.Light>()[2];
    71.             float BlockLightI = FirstLight.intensity;
    72.             for (float i = 0f; i <= 3; i += 0.05f)
    73.             {
    74.                 if (!One) //Once again a non-needed check. Just ensuring a block is either lighting or dimming. Not two different instances trying to do both at the same time and conflicting.
    75.                 {
    76.                     float NewIntensity = Mathf.Lerp(BlockLightI, 0f, i / 3f);
    77.                     FirstLight.intensity = NewIntensity;
    78.                     SecondLight.intensity = NewIntensity;
    79.                     ThirdLight.intensity = NewIntensity;
    80.                     yield return null;
    81.                     if(FirstLight.intensity < 0.0001f)
    82.                     {
    83.                         FirstLight.intensity = 0f;
    84.                         SecondLight.intensity = 0f;
    85.                         ThirdLight.intensity = 0f;
    86.                         yield break;
    87.                     }
    88.                 }
    89.                 else
    90.                 {
    91.                     yield break;
    92.                 }
    93.             }
    94.         }
    95.     }
    I would have to expand this code all the way to "F38", 38 bools, and 38 gameobjects.

    I am not even sure of the performance costs as only a couple of these coroutines would be active at once at most. I tried to make a system of one or two coroutines handling all 38 but run into an issue of the for loop inside not knowing when to stop dimming, for instance, if a player steps back on a block while it is dimming. Every solution I have found leads to 38 identifiers in some way. Even so, SOMETHING either needs to be in the for loop to stop the coroutine if a player steps back on (But remember 38 blocks), OR there needs to be 38 coroutines that I can just call to stop. You see both solutions in play here ("If (One) and If (!One) + StopCoroutine in the triggers).

    My question, can I construct coroutines dynamically? The only way I see to get around using 38 is if I could change that "One" or "!One" bool based on whatever gameobject it is to Two or to Twenty etc and literally build a coroutine like a constructor almost. I could have maybe a massive nested loop in the coroutines to check this but it would also have to be in the loop anyway so that is not exactly optimal.
    Thank you.
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Sounds like you need exactly ONE coroutine and it needs to just accept a parameter for the block to manipulate.

    Also rather than numbered variables you should just be using an array.
     
    colinsauce00 and Bunny83 like this.
  3. colinsauce00

    colinsauce00

    Joined:
    Jul 13, 2021
    Posts:
    9
    I have not used an array yet because I just wanted it simplified for this but I will. How would I pass through a variable that is at all useful to the for loop? I pass in one or two etc and it automatically becomes local to whatever its called in the coroutine parameters. I could change "One" etc globally and it would not matter to the coroutine as nothing I can put in the for loop parameters can be global. Unless you mean dynamically check it inside the loop?
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,890
    38 coroutines for 38 blocks??? Madness.

    This can just be distilled into a single component you put on each individual block. Meaning set up a prefab and duplicate it as much as needed.
     
    colinsauce00 likes this.
  5. colinsauce00

    colinsauce00

    Joined:
    Jul 13, 2021
    Posts:
    9

    I fixed it by using just 38 gameobjects and one coroutine and checking if can loop inside the loop, would there not be a performance cost of 38 scripts regardless if theyre prefabbed?

    Edit: Ended up using prefab version because I don't have to assign a million gameobjects. Here is the code if anyone needs to do something similar and wants to use it:


    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class LightBlocks : MonoBehaviour
    7. {
    8.     bool CallOnce = false;
    9.     private void OnTriggerStay2D(Collider2D other)
    10.     {
    11.         if(!CallOnce)
    12.         {
    13.             CallOnce = true;
    14.             StopCoroutine("F1");
    15.             StartCoroutine(F1(true));
    16.         }
    17.     }
    18.     private void OnTriggerExit2D(Collider2D other)
    19.     {
    20.         if(CallOnce)
    21.         {
    22.             CallOnce = false;
    23.             StopCoroutine("F1");
    24.             StartCoroutine(F1(false));
    25.         }
    26.     }
    27.     public IEnumerator F1(bool WhichCase)
    28.     {
    29.         if (WhichCase)
    30.         {
    31.             yield return new WaitForSecondsRealtime(0.5f);
    32.             if (GetComponentInChildren<UnityEngine.Light>() != null)
    33.             {
    34.                 UnityEngine.Light FirstLight = GetComponentsInChildren<UnityEngine.Light>()[0];
    35.                 UnityEngine.Light SecondLight = GetComponentsInChildren<UnityEngine.Light>()[1];
    36.                 UnityEngine.Light ThirdLight = GetComponentsInChildren<UnityEngine.Light>()[2];
    37.                 float BlockLightI = FirstLight.intensity;
    38.                 for (float i = 0f; i <= 3; i += 0.05f)
    39.                 {
    40.                     if (CallOnce)
    41.                     {
    42.                         float NewIntensity = Mathf.Lerp(BlockLightI, 10f, i / 3f);
    43.                         FirstLight.intensity = NewIntensity;
    44.                         SecondLight.intensity = NewIntensity;
    45.                         ThirdLight.intensity = NewIntensity;
    46.                         yield return null;
    47.                     }
    48.                     else
    49.                     {
    50.                         yield break;
    51.                     }
    52.                 }
    53.             }
    54.         }
    55.         else
    56.         {
    57.             if (GetComponentInChildren<UnityEngine.Light>() != null)
    58.             {
    59.                 UnityEngine.Light FirstLight = GetComponentsInChildren<UnityEngine.Light>()[0];
    60.                 UnityEngine.Light SecondLight = GetComponentsInChildren<UnityEngine.Light>()[1];
    61.                 UnityEngine.Light ThirdLight = GetComponentsInChildren<UnityEngine.Light>()[2];
    62.                 float BlockLightI = FirstLight.intensity;
    63.                 for (float i = 0f; i <= 3; i += 0.05f)
    64.                 {
    65.                     if (!CallOnce)
    66.                     {
    67.                         float NewIntensity = Mathf.Lerp(BlockLightI, 0f, i / 3f);
    68.                         FirstLight.intensity = NewIntensity;
    69.                         SecondLight.intensity = NewIntensity;
    70.                         ThirdLight.intensity = NewIntensity;
    71.                         yield return null;
    72.                         if (FirstLight.intensity < 0.0001f)
    73.                         {
    74.                             FirstLight.intensity = 0f;
    75.                             SecondLight.intensity = 0f;
    76.                             ThirdLight.intensity = 0f;
    77.                             yield break;
    78.                         }
    79.                     }
    80.                     else
    81.                     {
    82.                         yield break;
    83.                     }
    84.                 }
    85.             }
    86.         }
    87.     }
    88. }
     
    Last edited: Jan 11, 2024
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,890
    Not particularly. We're not talking numbers that would make a significant impact. I would have the component disabled by default (so no Update calls), and have it enable itself when the player collides with it/enters a trigger. Then it can just grow brighter until a certain point, and when the player gets off it, it dims until it's off and disables itself.
     
    colinsauce00 likes this.