Search Unity

Resolved Anyway to Optimize this code?

Discussion in 'Scripting' started by vaqquixx, Dec 2, 2022.

  1. vaqquixx

    vaqquixx

    Joined:
    Jul 29, 2018
    Posts:
    18
    I'm creating a Tower Defense game, and thus need a Wave Spawner script, I'm still inexperienced in Unity, and c#. I made this script, which allows me to control the flow of the wave 100% however I recognize that this potentially isn't the best way to do things, I tried my best to comment it to help anyone willing to help me, understand the code better, I'd really appreciate any type of input. :)

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public class EnemyStructure
    7. {
    8.     //Enemy clusters
    9.     [HideInInspector] public string Name;
    10.     [Header("Required")]
    11.     public GameObject enemyType;
    12.     public int amount = 1;
    13.     public float timeToNextEnemy;
    14.     [Header("MultiEnemy")]
    15.     public float multiEnemySpacing;
    16.     public bool ignoreSpacingTime;
    17. }
    18. [System.Serializable]
    19. public class Waves
    20. {
    21.     //Waves holding the enemy clusters
    22.     [HideInInspector] public string Name;
    23.     public EnemyStructure[] enemiesInWave;
    24. }
    25. public class WaveSpawner : MonoBehaviour
    26. {
    27.     [Header("References")]
    28.     [SerializeField] Transform enemyHolder;
    29.  
    30.     [Header("Properties")]
    31.     [SerializeField] float waveCooldownTime;
    32.     [SerializeField] bool autoStartWaves;
    33.  
    34.     [Header("Waves")]
    35.     [SerializeField] int currentWave;
    36.     [SerializeField] bool spawning;
    37.     [SerializeField] Waves[] totalWaves;
    38.  
    39.     List<bool> waitQueue = new List<bool>();
    40.     int waitQueueIndex;
    41.  
    42.     float waveCountdown;
    43.     Transform spawnPoint;
    44.     private void OnValidate()
    45.     {
    46.         LableArrays();
    47.     }
    48.     void LableArrays()
    49.     {
    50.         //Loops through all Waves and Enemies In Waves, and lables them in the Inspector
    51.         for (int i = 0; i < totalWaves.Length; i++)
    52.         {
    53.             totalWaves[i].Name = "Wave " + (i + 1);
    54.             for (int x = 0; x < totalWaves[i].enemiesInWave.Length; x++)
    55.             {
    56.                 totalWaves[i].enemiesInWave[x].Name = "Enemy(ies) " + (x + 1);
    57.             }
    58.         }
    59.     }
    60.     void Start()
    61.     {
    62.         //Start function, what do you expect? We are setting variables
    63.         spawning = false;
    64.         waveCountdown = waveCooldownTime;
    65.         currentWave = 0;
    66.         spawnPoint = Waypoints.waypointsStatic[0];
    67.         waitQueueIndex = -1;
    68.     }
    69.  
    70.     void Update()
    71.     {
    72.         //If no enemies exist in the map, and the spawner is done spawning
    73.         if(!spawning && enemyHolder.childCount == 0)
    74.         {
    75.             //If waves start automatically, countdown, then start wave
    76.             if (autoStartWaves)
    77.             {
    78.                 waveCountdown -= Time.deltaTime;
    79.                 if (waveCountdown <= 0)
    80.                 {
    81.                     TryStartNextWave();
    82.                 }
    83.             }
    84.         }
    85.         //Start next wave if we press "P"
    86.         if (Input.GetKeyUp(KeyCode.P))
    87.         {
    88.             TryStartNextWave();
    89.         }
    90.     }
    91.     IEnumerator SpawnWave()
    92.     {
    93.         //Setting variables we need
    94.         int waveIndex = currentWave;
    95.         spawning = true;
    96.         waveCountdown = waveCooldownTime;
    97.         //Here as well
    98.         bool clusterWait = false;
    99.         waitQueueIndex++;
    100.         int queueNumber = waitQueueIndex;
    101.  
    102.         waitQueue.Add(clusterWait);
    103.         //Loop through as many enemies/enemy clusters there are in the wave
    104.         for (int i = 0; i < totalWaves[waveIndex-1].enemiesInWave.Length; i++)
    105.         {
    106.             //List of bools to act as local variables for whether or not to
    107.             //wait for enemy clusters to spawn
    108.             clusterWait = !totalWaves[waveIndex - 1].enemiesInWave[i].ignoreSpacingTime;
    109.             waitQueue[queueNumber] = clusterWait;
    110.  
    111.             //Spawn in the enemy(ies) in the cluster
    112.             StartCoroutine(EnemyCluster(waveIndex, i, clusterWait, queueNumber));
    113.  
    114.             //Wait if we want all enemies to spawn in the current cluster before the next cluster
    115.             while (waitQueue[queueNumber])
    116.             {
    117.                 yield return null;
    118.             }
    119.  
    120.             //Wait for the time to next enemy cluster
    121.             float timeToNextEnemyCluster = totalWaves[waveIndex - 1].enemiesInWave[i].timeToNextEnemy;
    122.             if (timeToNextEnemyCluster > 0)
    123.             {
    124.                 yield return new WaitForSeconds(timeToNextEnemyCluster);
    125.             }
    126.         }
    127.         //Tell game we're done spawning in enemies for this wave
    128.         if(currentWave == waveIndex)
    129.         {
    130.             spawning = false;
    131.         }
    132.     }
    133.     IEnumerator EnemyCluster(int waveIndex, int spawnSpot, bool resetWait, int queueNumber)
    134.     {
    135.         //Loop through all enemies to spawn in the enemy cluster
    136.         for (int x = 0; x < totalWaves[waveIndex - 1].enemiesInWave[spawnSpot].amount; x++)
    137.         {
    138.             //Spawn in an enemy
    139.             SpawnEnemy(totalWaves[waveIndex - 1].enemiesInWave[spawnSpot].enemyType);
    140.  
    141.             //If enemy spacing exists, wait
    142.             float spacingTime = totalWaves[waveIndex - 1].enemiesInWave[spawnSpot].multiEnemySpacing;
    143.             if (spacingTime > 0)
    144.             {
    145.                 yield return new WaitForSeconds(spacingTime);
    146.             }
    147.         }
    148.         //If we decided to wait for all enemies in cluster to spawn before the next one, reset the queue
    149.         if (resetWait)
    150.         {
    151.             waitQueue[queueNumber] = false;
    152.         }
    153.     }
    154.     void SpawnEnemy(GameObject enemy)
    155.     {
    156.         //Creates enemy at spawnpoint
    157.         GameObject spawnedEnemy = Instantiate(enemy, spawnPoint.position, Quaternion.identity);
    158.         //Set enemies parent to the enemy holder for tracking
    159.         spawnedEnemy.transform.SetParent(enemyHolder);
    160.     }
    161.  
    162.     public void TryStartNextWave()
    163.     {
    164.         //Increment wave number and start next wave
    165.         if (currentWave < totalWaves.Length)
    166.         {
    167.             currentWave++;
    168.             StartCoroutine(SpawnWave());
    169.         }
    170.         else
    171.         {
    172.             Win();
    173.         }
    174.     }
    175.  
    176.     void Win()
    177.     {
    178.         print("Player Won!");
    179.     }
    180. }
    181.  
     
    Last edited: Dec 5, 2022
  2. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    1,164
    Before you change this code why don’t you make a separate code to check whether or not you want the functionality this code provides. For example you have 100% control over the flow wave and you are suggesting you do not want that level of control? Like balloons tower defence they can come clustered or linearly and queue.

    if by optimal you want a smaller code structure I would assume most basic it could get is like this —

    MakeWave()- add to list number of enemies
    (if cluster less than or equal to zero)
    EjectUnit(int whichindex) probably 0
    cluster = How_long_a_tick_is
    Wave Remove at whichindex

    Send him on his path or plug him into his path iterate path.

    if you had something as simple as that you would be fine. Ensure that you do not want the functionality you may no longer have before decimating the code.

    also
    The Inumerators may be your problem. But maybe somebody else can look at this.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,689
    Check out Step #2 below... it is critical to understanding any code, including your own.

    Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

    How to do tutorials properly, two (2) simple steps to success:

    Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That's how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

    Fortunately this is the easiest part to get right: Be a robot. Don't make any mistakes.
    BE PERFECT IN EVERYTHING YOU DO HERE!!


    If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

    Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

    Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

    Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there's an error, you will NEVER be the first guy to find it.

    Beyond that, Step 3, 4, 5 and 6 become easy because you already understand!
     
  4. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    699
    You have a issue line 165.

    Code (csharp):
    1.  
    2.         if (currentWave < totalWaves.Length)
    3.         {
    4.             currentWave++;
    5.             StartCoroutine(SpawnWave());
    6.         }
    7.  
    with this code you will reach currentWave == totalWaves.Length and then totalWaves[currentWaves] does not exist.

    if (currentWave < (totalWaves.Length-1))

    should do the trick.

    I see that you have fixed it by using waveIndex - 1 everytime but in my opinion this is unnecessary and source for bugs.

    Your waveIndex should start at 0 and finish at totalWaves.Length - 1, otherwise I'm not sure what your code does when waveIndex == 0.
     
  5. vaqquixx

    vaqquixx

    Joined:
    Jul 29, 2018
    Posts:
    18
    Nope, my code works, if there are 6 waves, totalWaves.Length will be == 6, therefore if waveIndex == 6 then we will not call a new wave and instead win.
     
  6. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    Just glancing, in function enemyCluster you're using looking at
    totalWaves[waveIndex - 1].enemiesInWave[spawnSpot]
    . At the top of the function you may as well grab that:
    EnemyStructure ee = totalWaves[waveIndex - 1].enemiesInWave[spawnSpot];
    then just use
    ee
    everywhere. As well as shorter, it makes it more obvious that you're using the same thing in those 3 places -- and also reduces the chance for error if done this way for new code. Or, this is a more programmery idea, don't even pass it waveIndex and spawnSpot -- pass it the EnemyStructure instead.

    Then a minor thing, in spawnWave you're got
    if (timeToNextEnemyCluster > 0)
    protecting a WaitForSeconds. I wouldn't bother, it can be confusing since check for zero feels like it _can't_ be zero or bad things happen.
     
    vaqquixx likes this.
  7. vaqquixx

    vaqquixx

    Joined:
    Jul 29, 2018
    Posts:
    18
    I didn't follow a tutorial for this, I made this code myself, I think you may have understood what I meant, as I forgot a comma.