Search Unity

Question Is making my game gradually harder really this hard to do?

Discussion in 'Scripting' started by Loading7088, May 22, 2023.

  1. Loading7088

    Loading7088

    Joined:
    May 5, 2023
    Posts:
    9
    Hiya, so I have a simple script that spawns platforms at a set interval. But I need it to start slow (like its slow now) and gradually increase over timer (Kinda like subway surfers). This, as I have found, seems to be almost impossible to find a tutorial or a past thread about this. Currently, I have this script to spawn

    Code (csharp):
    1.  
    2. if (timer < spawnrate)
    3.             {
    4.  
    5.                 timer = timer + Time.deltaTime;
    6.  
    7.  
    8.             }
    9.             else
    10.             {
    11.  
    12.                 int platformIndex = Random.Range(0, Platforms.Length);
    13.                 Instantiate(Platforms[platformIndex], new Vector3(15, -0.3f, 0), Platforms[platformIndex].transform.rotation);
    14.                 timer = 0;
    15.             }
    16.  
    17.  

    But I need it to make the spawnrate and timer floats smaller by x value so that it gruadually increases. So I added a increment value for both. How would I do this, or is there a better way to implement my spawn code?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,752
    With the above structure all you need is to gradually reduce
    spawnrate
    every time you spawn something.

    You do NOT want an arithmetic decrease because then it could go negative and this would start spawning something every single time it was run.

    Instead reduce it geometrically.

    For instance you could multiply it by a fraction every time you spawn.

    Start spawnrate at whatever you want it (say 2 seconds), then on line 11 above:

    Code (csharp):
    1. spawnrate = spawnrate * 0.95f;
    That will approach but essentially never reach zero until you underflow.

    You could control for that by never letting it get below a floor amount:

    Code (csharp):
    1. if (spawnrate < floorSpawnRate)
    2. {
    3.   spawnrate = floorSpawnRate;
    4. }
    NOTE:
    spawnrate
    is a misleading variable name because it's not a rate. A rate is quantity over time (generally). What you have there is a
    spawninterval
    , so the
    spawnrate
    is technically
    1.0f / spawninterval
     
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    PraetorBlue, Yoreki, Bunny83 and 2 others like this.
  4. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    You could also use Animation Curves, and that's what I would do, because it's much easier to just create and mentally visualize a curve than trying to come up with a mathematical formula that could be very complicated. Check this CodeMonkey's tutorial (starting at 15:18) which does exactly what you want (increasing difficulty enemy spawn):

     
    orionsyndrome and Chubzdoomer like this.
  5. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Why are you suggesting this, like coroutines are somehow superior in every regard?

    They are actually to be avoided whenever possible. Coroutines are WD-40 of Unity. You got squeaky door, sure, you got rusty screw, sure, but when you start spraying your CPU fans with it you might as well start gargling it before going to bed.

    There are many hidden costs incurred by this 'cleanliness'. It is not recommended way to do most things, unless, as I explained in another thread, you are absolutely sure it will never grow or become complicated in such a way to interact with your state logic or status of other coroutines. So think of a pretty big tree, if you can identify your feature as a leaf in that tree, you may use a coroutine. And even then I tend to avoid it.

    It is especially irresponsible to suggest such potentially dangerous convolutions to beginners, many don't understand what enumerators are! Approximately 30% of all problems in this forum is due to newbies copy pasting a tangled mess of uncontrollable coroutines. I personally find them incredibly unintuitive and prone to catastrophic errors because you got one extra-dimensional pocket to think about -- and it's a time-based dimension -- which is almost as bad as having code that's multithreaded without any safety or syncing measure whatsoever.

    All that because of cleanliness. Sure hide all your code from sight. Put it in a drawer somewhere, never to be seen. That's not what clean code is about.
     
    Yoreki and Nad_B like this.
  7. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    The increasing difficulty is part of the game flow (main feature, managing part). It's not the same as an in-game event.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,752
    I want this notice to pop up automatically from the Unity editor anytime they detect me making some crazy computation to make a particular curve.

    Code (csharp):
    1. spawnrate = 100 / (10 + enemyCount... //* BEEP BEEP! You could also use an Animation Curve...
     
    Yoreki, orionsyndrome and Nad_B like this.
  9. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,799
    IMO keeping the Update function clean is not a good enough reason to use coroutines.
     
    Yoreki, orionsyndrome and koirat like this.
  10. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    Yep, Animations Curves are awesome, can't live without them.
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,752
    Everybody screeches "keep the update loop CLEAN!" until they have a bug.

    Then when they go to place a breakpoint in their code and everything is blasted and scattered all over kingdom come, they have no idea where to put the breakpoint and no idea what might or might not be running.

    I try to code with this in mind:

    "When my code has a problem, where will I put the breakpoint?"

    This alone discourages lots of silly attempts to be clever.
     
    orionsyndrome and Nad_B like this.
  12. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    Code (CSharp):
    1. private void Update()
    2. {
    3.     SpawnEnemiesIfNecessary();
    4. }

    Here, I just made the Update method clean without needing a coroutine.
     
    orionsyndrome and AcidArrow like this.
  13. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    I want to boil an egg, so I set a timer and go to do other tasks. Before that, I told my wife what to do when it rings (boil more). So I don't have to stop my chores to check the clock each time I blink (Update my view).
     
  14. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    Yes you do, you just want to hide away from it. It's still synchronous programming, you're just delegating your task management to native code and waiting for enumerators to silently scramble everything in the "background".

    It's like code folding, the stupidest invention that ever crossed someone's mind. Some people write programs like they're punished to do so.

    We're supposed to engineer solutions, and coroutines fit into this category of a sweet lazy hack, which is excellent for quickly mashing something up, but god help you if you rely that much on them and end up having to refactor that bs.

    Legit timing concerns are solved with callbacks and events, and proper system boilerplate. You don't lay a brick house on dirt. Down with the lazy, messy coroutines that come back to haunt you forever. I don't mind if you want to spice up your haxx life a little, but telling everyone to jump off the cliff is not a great general advice.
     
    AngryProgrammer and Yoreki like this.
  15. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    You're mixing coroutines and events (pub/sub). Coroutines also run each frame, just like the Update method, it's just abstracted away in their own state machine.

    From Unity's "Order of Execution for Event Functions" page:
    upload_2023-5-22_18-4-43.png
     

    Attached Files:

    AngryProgrammer likes this.
  16. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,205
    Coroutines just shift the complexity from one function to another. Use InvokeRepeating.
    Code (csharp):
    1. public float spawnRate = 1.0f;
    2.  
    3. private void Start()
    4. {
    5.     InvokeRepeating(nameof(SpawnPlatform), 0, spawnRate);
    6. }
    7.  
    8. private void SpawnPlatform()
    9. {
    10.     int platformIndex = Random.Range(0, Platforms.Length);
    11.     Instantiate(Platforms[platformIndex], new Vector3(15, -0.3f, 0), Platforms[platformIndex].transform.rotation);
    12. }
     
    AngryProgrammer likes this.
  17. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    With that proof, you drain my mana out. You convince me of my mistake.
     
    Nad_B likes this.
  18. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,116
    On the flipside you get something that consistently evaluates between Update and LateUpdate.