1
\$\begingroup\$

Hey guys so here's an outline of my problem using a basic 3-step approach

The Problem Defined Waves 1-22 should spawn in order with the correct paths and enemy prefabs. However, Waves 1-4 only spawn and repeat on a loop. https://www.youtube.com/watch?v=p0HZ_qo4SxU

Possible Causes I think the issue possibly lies within my enemyspawner.cs script but it could also potentially lie within my level.cs and waveconfig.cs scripts. Oddly enough this seems to also be an issue with the original source code as well. I made several copies of the prefabs, gave them unique paths and waves and changed the new enemy prefabs to different colors to test it and the same result happens. https://www.youtube.com/watch?v=8zr7CVjty6k

The needed solution I need all 22 waves to re-spawn in order correctly as only having 4 waves repeat is very repetitive and boring.

Here is my specific code for enemyspawner:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemySpawner : MonoBehaviour {

    [SerializeField] List<WaveConfig> waveConfigs;
    [SerializeField] int startingWave = 0;
    [SerializeField] bool looping = false;

    IEnumerator Start()
    {
        do
        {
            yield return StartCoroutine(SpawnAllWaves());
        }
        while (looping);
    }

    private IEnumerator SpawnAllWaves()
    {
        for (int waveIndex = startingWave; waveIndex < waveConfigs.Count; waveIndex++)
        {
            var currentWave = waveConfigs[waveIndex];
            yield return StartCoroutine(SpawnAllEnemiesInWave(currentWave));
        }
    }

    private IEnumerator SpawnAllEnemiesInWave(WaveConfig waveConfig)
    {
        for (int enemyCount = 0; enemyCount < waveConfig.GetNumberOfEnemies(); enemyCount++)
        {
            var newEnemy = Instantiate(
                waveConfig.GetEnemyPrefab(),
                waveConfig.GetWaypoints()[0].transform.position,
                Quaternion.identity);
            newEnemy.GetComponent<EnemyPathing>().SetWaveConfig(waveConfig);
            yield return new WaitForSeconds(waveConfig.GetTimeBetweenSpawns());
        }
    }
}

enter image description here

Here is my specific code for waveconfig:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Enemy Wave Config")]
public class WaveConfig : ScriptableObject
{

    [SerializeField] GameObject enemyPrefab;
    [SerializeField] GameObject pathPrefab;
    [SerializeField] float timeBetweenSpawns = 0.5f;
    [SerializeField] float spawnRandomFactor = 0.3f;
    [SerializeField] int numberOfEnemies = 5;
    [SerializeField] float moveSpeed = 2f;

    public GameObject GetEnemyPrefab() { return enemyPrefab; }

    public List<Transform> GetWaypoints()
    {
        var waveWaypoints = new List<Transform>();
        foreach (Transform child in pathPrefab.transform)
        {
            waveWaypoints.Add(child);
        }
        return waveWaypoints;
    }

    public float GetTimeBetweenSpawns() { return timeBetweenSpawns; }

    public float GetSpawnRandomFactor() { return spawnRandomFactor; }

    public int GetNumberOfEnemies() { return numberOfEnemies; }

    public float GetMoveSpeed() { return moveSpeed; }

}

Here is my specific code for Level:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Level : MonoBehaviour
{

    [SerializeField] float delayInSeconds = 2f;

    public void LoadStartMenu()
    {
        SceneManager.LoadScene(0);
    }

    public void LoadGame()
    {
        SceneManager.LoadScene("Game");
        FindObjectOfType<GameSession>().ResetGame();
    }

    public void LoadGameOver()
    {
        StartCoroutine(WaitAndLoad());
    }

    IEnumerator WaitAndLoad()
    {
        yield return new WaitForSeconds(delayInSeconds);
        SceneManager.LoadScene("Game Over");
    }

    public void QuitGame()
    {
        Application.Quit();
    }

}
\$\endgroup\$
  • 1
    \$\begingroup\$ You don't need to yield return StartCoroutine(SpawnAllEnemiesInWave(currentWave));, you can just yield return SpawnAllEnemiesInWave(currentWave);: the function is already returning an IEnumerator, which is the type of object expected by a yield instruction. \$\endgroup\$ – Draco18s Feb 6 at 4:32
1
\$\begingroup\$

The following has nothing to do with the question, but I couldn't format the code in a comment.

You should look into using properties for your fields. They greatly reduce your complexity and are a good shortcut. As an example:

[SerializeField] float timeBetweenSpawns = 0.5f;
public float GetTimeBetweenSpawns() { return timeBetweenSpawns; }

is identical to

public float TimeBetweenSpawns {get; private set;}
// This creates a property on the object that can be accessed publicly
// but only set within its class

This prevents you from having to write out manual getters and setters for common methods, and prevents you from having to do bad design, like telling unity to serialize a private field.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks again for this mate! It really helped me clean up my code! \$\endgroup\$ – user116531 Feb 13 at 23:29
  • \$\begingroup\$ it's something that is very common to see in people who start writing code in java, and switch to C# \$\endgroup\$ – Marshall Tigerus Feb 14 at 14:49
1
\$\begingroup\$

Hey guys so the issue happened to be that my enemy spawner prefab became corrupted somehow and after I replaced it with my saved source code, everything is working correctly, and oddly enough the master code from the course I built the game from had the same issue but if you are following along to the course just make sure to have all your needed prefabs in the game scene.

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.