Forum begins after the advertisement:


[Part 11] Chunk not loading correctly and enemies are multiplied when spawning

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [Part 11] Chunk not loading correctly and enemies are multiplied when spawning

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #16016
    ThatOneGuy
    Level 4
    Participant
    Helpful?
    Up
    0
    ::

    For the first issue, chunk loading diagonally is working fine but walking straight and the chunks are not loading correctly

    Video:

    View post on imgur.com

    Screenshot of the Trigger Inspector:

    View post on imgur.com

    For the second issue, for some reason when enemies in a wave are being spawned, they get multiplied by 2

    Code:

    <code>using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class EnemySpawner : MonoBehaviour
    {
        [System.Serializable]
        public class Wave
        {
            public string waveName;
            public List<EnemyGroup> enemyGroups; //list of groups of enemies to spawn in a wave
            public int waveQuota; //Total number of enemies to spawn in a wave
            public float spawnInterval; //interval at which to spawn enemies
            public int spawnCount; //number of enemies already spawned in a wave
        }
    
        [System.Serializable]
        public class EnemyGroup
        {
            public string enemyName;
            public int enemyCount; //Then number of enemiues to spawn in a wave
            public int spawnCount;// number of specific enemies type already spawned in a wave
            public GameObject enemyPrefab;
        }
    
        public List<Wave> waves; //A list of all the waves in the game
        public int currentWaveCount; //this is the index of the current wave (P.S. list always starts from 0)
    
        [Header("Spawner Attributes")]
        float spawnTimer; //Timer use to determine when to spawn the next enemy
        public int enemiesAlive; //number of enemies alive
        public int maxEnemiesAllowed; //maximum number of enemies allowed on the map
        public bool maxEnemiesReached = false; //Flag that indicates if the maximum number of enemies has been reached
        public float waveInterval; //inverval between each wave
        bool isWaveActive = false;
    
        [Header("Spawn Positions")]
        public List<Transform> relativeSpawnPoints; //List to store all the relative spawn points of enemies
    
        Transform player;
    
        // Start is called before the first frame update
        void Start()
        {
            player = FindObjectOfType<PlayerStats>().transform;
            CalculateWaveQuota();
        }
    
        // Update is called once per frame
        void Update()
        {
            if(currentWaveCount < waves.Count && waves[currentWaveCount].spawnCount == 0 && !isWaveActive) //Checks if the wave has ended and the next wave should start
            {
                StartCoroutine(BeginNextWave());
            }
    
            spawnTimer += Time.deltaTime;
    
            //Checks to see if its time to spawn the next enemy
            if(spawnTimer >= waves[currentWaveCount].spawnInterval)
            {
                spawnTimer = 0f;
                SpawnEnemies();
            }   
        }
    
        IEnumerator BeginNextWave()
        {
            isWaveActive = true;
    
            //Wave for 'waveInterval' seconds before starting the next wave
            yield return new WaitForSeconds(waveInterval);
    
            //checks if there are more waves to start after the current wave, move on to the next wave
            if(currentWaveCount < waves.Count - 1)
            {
                isWaveActive = false;
                currentWaveCount++;
                CalculateWaveQuota();
            }
        }
    
        void CalculateWaveQuota()
        {
            int currentWaveQuota = 0;
            foreach (var enemyGroup in waves[currentWaveCount].enemyGroups) //foreach loops that goes through all the enemy groups in the current wave count element of the waves array
            {
                currentWaveQuota += enemyGroup.enemyCount;//Add the enemy count of the current enemy groupd to the current wave quota
            }
    
            waves[currentWaveCount].waveQuota = currentWaveQuota; //sets the wave quota of the current wave count element in the wave array tot he current wave quota
            Debug.LogWarning(currentWaveQuota);
        }
    
        /// <summary>
        /// This method will stop spawning enemies if the amount of enemies on the map is maximum
        /// The method will only spawn enemies in a particular wave until it is time for the next wave's enemies to be spawned
        /// </summary>
        void SpawnEnemies()//method
        {
    
            if (waves[currentWaveCount].spawnCount < waves[currentWaveCount].waveQuota && !maxEnemiesReached) //checks if the minimum number of enemies in the current wave has been spawned
            {
                //Spawn each type of enemy until the quota is filled
                foreach (var enemyGroup in waves[currentWaveCount].enemyGroups) //loops each enemy group in the waves current wave count
                {
                    //checks if the minimum number of enemies of this type have been spawned
                    if (enemyGroup.spawnCount < enemyGroup.enemyCount) //checks if the minimum number of enemies of this type have been spawned by comparing the enemy group spawncount to enemy group enemy count
                    {
                        //Spawn the enemy at a random position close to the player
                        Instantiate(enemyGroup.enemyPrefab, player.position + relativeSpawnPoints[Random.Range(0, relativeSpawnPoints.Count)].position, Quaternion.identity);
    
                        //generates random x and y coordinates and storing them in a vector2
                        //adds the players x and y position to the randomised coordinates such that they are relative to ensure enemies spawn relative to the players movement
                        Vector2 spawnPosition = new Vector2(player.transform.position.x + Random.Range(-10f, 10f), player.transform.position.y + Random.Range(-10f, 10f));
                        Instantiate(enemyGroup.enemyPrefab, spawnPosition, Quaternion.identity);
    
                        enemyGroup.spawnCount++; //Increment to keep track enemy spawn
                        waves[currentWaveCount].spawnCount++; //Increment to keep track of wave count
                        enemiesAlive++; //Increment to keep count of enemies alive
    
                        //limits the number of enemies that can be spawned at once
                        if (enemiesAlive >= maxEnemiesAllowed)
                        {
                            maxEnemiesReached = true;
                            return;
                        }
                    } 
                }
            }
        }
    
        //Call this function when an enemy is killed
        public void OnEnemyKilled()
        {
            //Decrement the number of enemies alive
            enemiesAlive--;
    
            //Reset the maxEnemiesReached flag if the number of enemies alive has dropped below the maximum amount
            if (enemiesAlive < maxEnemiesAllowed)
            {
                maxEnemiesReached = false;
            }
        }
    }
    </code>
    #16020
    ThatOneGuy
    Level 4
    Participant
    Helpful?
    Up
    0
    ::

    Forgot to add my MapController script

    <code>using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class MapController : MonoBehaviour
    {
        //Store prefabs for the terrainchunks
        public List<GameObject> terrainChunks;
        //references the player
        public GameObject player;
        //checks the radius
        public float checkerRadius;
        //track which layer is the terrain and which is not the terrain
        public LayerMask terrainMask;
        //reference to access variables
        public GameObject currentChunk;
        Vector3 playerLastPosition;
    
        [Header("Optimization")]
        //stores the current chunks
        public List<GameObject> spawnedChunks;
        //last chunk that was spawned
        GameObject latestChunk;
        //Max optimization distance
        //used to set the max distance for each of the chunk from player
        public float maxOpDist; //Must be greater than the length and width of the tilemap
        //references the current distance for each chunks
        float opDist;
        float optimizerCooldown;
        public float optimizerCooldownDur;
    
        // Start is called before the first frame update
        void Start()
        {
            playerLastPosition = player.transform.position;
        }
    
        // Update is called once per frame
        void Update()
        {
            ChunkChecker();
            ChunkOptimizer();
        }
    
        void ChunkChecker()
        {
            if (!currentChunk)
            {
                return;
            }
    
            Vector3 moveDir = player.transform.position - playerLastPosition;
            playerLastPosition = player.transform.position;
    
            string directionName = GetDirectionName(moveDir);
    
            CheckAndSpawnChunk(directionName);
    
            //Check additional adjacent directions for diagonal chunks
            if (directionName.Contains("Up"))
            {
                CheckAndSpawnChunk("Up");
            }
            if (directionName.Contains("Down"))
            {
                CheckAndSpawnChunk("Down");
            }
            if (directionName.Contains("Right"))
            {
                CheckAndSpawnChunk("Right");
            }
            if (directionName.Contains("Left"))
            {
                CheckAndSpawnChunk("Left");
            }
        }
    
        void CheckAndSpawnChunk(string direction)
        {
            if(!Physics2D.OverlapCircle(currentChunk.transform.Find(direction).position, checkerRadius, terrainMask))
            {
                SpawnChunk(currentChunk.transform.Find(direction).position);
            }
        }
    
        string GetDirectionName(Vector3 direction)
        {
            direction = direction.normalized;
    
            if (Mathf.Abs(direction.x) > Mathf.Abs(direction.y))
            {
                //moving horizontally more than vertically
                if (direction.y > 0.5f)
                {
                    //Also moving upwards
                    return direction.x > 0 ? "Right Up" : "Left Up";
                }
                else if (direction.y < -0.5f)
                {
                    //also moving downwards
                    return direction.x > 0 ? "Right Down" : "Left Down";
                }
                else
                {
                    //moving straight horizontally
                    return direction.x > 0 ? "Right" : "Left";
                }
            }
            else
            {
                //moving vertically more than vertically
                if (direction.x > 0.5f)
                {
                    //also moving right
                    return direction.y > 0 ? "Right Up" : "Right Down";
                }
                else if (direction.x < -0.5f)
                {
                    //also moving left
                    return direction.y > 0 ? "Left Up" : "Left Down";
                }
                else
                {
                    //moving straight vertically
                    return direction.y > 0 ? "Up" : "Down";
                }
            }
        }
    
        void SpawnChunk(Vector3 spawnPosition)
        {
            int rand = Random.Range(0, terrainChunks.Count);
            latestChunk = Instantiate(terrainChunks[rand], spawnPosition, Quaternion.identity);
            spawnedChunks.Add(latestChunk);
        }
    
        void ChunkOptimizer()
        {
            optimizerCooldown -= Time.deltaTime;
    
            if(optimizerCooldown <= 0f)
            {
                optimizerCooldown = optimizerCooldownDur;
            }
            else
            {
                return;
            }
    
            //for loop
            foreach (GameObject chunk in spawnedChunks)
            {
                //distance of the chunk that is being checked
                opDist = Vector3.Distance(player.transform.position, chunk.transform.position);
                //checks if chunk dist is more than the max optimization dist and disable it if it is
                if (opDist > maxOpDist)
                {
                    chunk.SetActive(false);
                }
                else
                {
                    chunk.SetActive(true);
                }
            }
        }
    }
    </code>
    #16023
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    This is a known bug with the MapController. I’ll be improving on the map generation system soon. For now, here’s an easy (but slightly inefficient) fix. Change the highlighted portion of the ChunkChecker() function.

        void ChunkChecker()
        {
            if (!currentChunk)
            {
                return;
            }
    
            Vector3 moveDir = player.transform.position - playerLastPosition;
            playerLastPosition = player.transform.position;
    
            string directionName = GetDirectionName(moveDir);
    
            CheckAndSpawnChunk(directionName);
    
            // Check additional adjacent directions for diagonal chunks
            if (directionName.Contains("Up"))
            {
                CheckAndSpawnChunk("Up");
                CheckAndSpawnChunk("Left");
                CheckAndSpawnChunk("Right");
            } else if (directionName.Contains("Down"))
            {
                CheckAndSpawnChunk("Down");
                CheckAndSpawnChunk("Left");
                CheckAndSpawnChunk("Right");
            } else if (directionName.Contains("Right"))
            {
                CheckAndSpawnChunk("Right");
                CheckAndSpawnChunk("Down");
                CheckAndSpawnChunk("Up");
            } else if (directionName.Contains("Left"))
            {
                CheckAndSpawnChunk("Left");
                CheckAndSpawnChunk("Down");
                CheckAndSpawnChunk("Up");
            }
        }

    What we’re doing is instead of only checking the direction we are moving in, we are checking the diagonals as well.

    #16028
    ThatOneGuy
    Level 4
    Participant
    Helpful?
    Up
    0
    ::

    Got it. Thank you so much. About the second issue, I can’t seem to figure out the issue been trying to troubleshoot it but it still multiplies the enemy in the waves to 2

    #16033
    Terence
    Level 30
    Keymaster
    Helpful?
    Up
    0
    ::

    That’s because you call Instantiate twice in SpawnEnemy():

        void SpawnEnemies()//method
        {
    
            if (waves[currentWaveCount].spawnCount < waves[currentWaveCount].waveQuota && !maxEnemiesReached) //checks if the minimum number of enemies in the current wave has been spawned
            {
                //Spawn each type of enemy until the quota is filled
                foreach (var enemyGroup in waves[currentWaveCount].enemyGroups) //loops each enemy group in the waves current wave count
                {
                    //checks if the minimum number of enemies of this type have been spawned
                    if (enemyGroup.spawnCount < enemyGroup.enemyCount) //checks if the minimum number of enemies of this type have been spawned by comparing the enemy group spawncount to enemy group enemy count
                    {
                        //Spawn the enemy at a random position close to the player
                        Instantiate(enemyGroup.enemyPrefab, player.position + relativeSpawnPoints[Random.Range(0, relativeSpawnPoints.Count)].position, Quaternion.identity);
    
                        //generates random x and y coordinates and storing them in a vector2
                        //adds the players x and y position to the randomised coordinates such that they are relative to ensure enemies spawn relative to the players movement
                        Vector2 spawnPosition = new Vector2(player.transform.position.x + Random.Range(-10f, 10f), player.transform.position.y + Random.Range(-10f, 10f));
                        Instantiate(enemyGroup.enemyPrefab, spawnPosition, Quaternion.identity);
    
                        enemyGroup.spawnCount++; //Increment to keep track enemy spawn
                        waves[currentWaveCount].spawnCount++; //Increment to keep track of wave count
                        enemiesAlive++; //Increment to keep count of enemies alive
    
                        //limits the number of enemies that can be spawned at once
                        if (enemiesAlive >= maxEnemiesAllowed)
                        {
                            maxEnemiesReached = true;
                            return;
                        }
                    } 
                }
            }
        }
    #16040
    ThatOneGuy
    Level 4
    Participant
    Helpful?
    Up
    1
    ::

    That makes a lot of sense. I forgot to remove that when the enemy spawner was improved. Thank you so much.

    has upvoted this post.
Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: