Forum begins after the advertisement:

 


[General] Map Generation and teleporting player

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [General] Map Generation and teleporting player

Viewing 15 posts - 1 through 15 (of 47 total)
  • Author
    Posts
  • #18435
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    1
    ::

    Cheers! I created a simple teleporter function for my game and it works fine. It takes the player back to position 0,0,0 on the map (the first start chunk).

    public void TeleportPlayer()
    {
        GameObject.FindGameObjectWithTag("Player").transform.position = Vector2.zero;
        Instantiate(teleportVfxEffect, TeleportVector, Quaternion.identity);
    }

    But now I’m having the problem that after teleporting, not only do the already spawned chunks appear in every direction, but additional, overlapping chunks spawn on top of the existing ones. This can be repeated infinitely by teleporting multiple times in a row and moving around the map. This results in multiple chunks appearing in the same location, which of course causes massive performance issues. Is there a way to modify the “MapController.cs”/”ChunkTrigger.cs” so that this problem no longer occurs?

    Thanks for your help!

    has upvoted this post.
    #18436
    Alp Apustaja
    Level 9
    AI Assistant
    (BETA)
    Helpful?
    Up
    0
    ::

    It sounds like the issue you’re experiencing is related to how the chunk loading system works when the player teleports back to the starting position. When you teleport, it’s likely that the chunk loading system is not correctly handling the player’s position reset, resulting in multiple chunks being instantiated.

    To address this, you can modify your chunk loading logic to ensure that it checks for existing chunks before spawning new ones. Here are some steps you can take to modify your MapController.cs and ChunkTrigger.cs:

    1. Track Loaded Chunks: Maintain a list or dictionary of the currently loaded chunks in the MapController class. This will help you keep track of which chunks are already active.

    2. Check for Existing Chunks: Before spawning a new chunk in the ChunkTrigger class, check if that chunk is already loaded. If it is, skip the instantiation.

    3. Unload Chunks: When teleporting, consider unloading all chunks around the player (or just the chunks in the vicinity of the teleport location) to prevent overlaps.

    Here’s a rough example of how you might implement these concepts:

    MapController.cs

    using System.Collections.Generic;
    
    public class MapController : MonoBehaviour
    {
        private HashSet loadedChunks = new HashSet();
    
        public void LoadChunk(Vector2 position)
        {
            if (!loadedChunks.Contains(position))
            {
                // Instantiate your chunk here
                loadedChunks.Add(position);
            }
        }
    
        public void UnloadChunk(Vector2 position)
        {
            // Remove the chunk and any related logic here
            loadedChunks.Remove(position);
        }
    
        public void TeleportPlayer(Vector3 newPosition)
        {
            GameObject player = GameObject.FindGameObjectWithTag("Player");
            player.transform.position = newPosition;
    
            // Unload chunks around the new position if needed
            UnloadNearbyChunks(newPosition);
        }
    
        private void UnloadNearbyChunks(Vector3 position)
        {
            // Logic to unload nearby chunks based on the new position
        }
    }

    ChunkTrigger.cs

    public class ChunkTrigger : MonoBehaviour
    {
        public void OnTriggerEnter2D(Collider2D other)
        {
            if (other.CompareTag("Player"))
            {
                Vector2 chunkPosition = new Vector2(transform.position.x, transform.position.y);
                FindObjectOfType().LoadChunk(chunkPosition);
            }
        }
    }

    Teleport Method Update

    In your TeleportPlayer method, make sure to call the UnloadNearbyChunks method before teleporting the player to prevent overlapping chunks.

    Final Note

    Make sure to test the updated logic thoroughly to ensure that chunks are loaded and unloaded correctly and that performance issues are resolved. Adjust the logic according to your game’s specific mechanics and requirements.

    #18437
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    THank you, can you be more specific about this function

        private void UnloadNearbyChunks(Vector3 position)
        {
            // Logic to unload nearby chunks based on the new position
        }
    #18438
    Alp Apustaja
    Level 9
    AI Assistant
    (BETA)
    Helpful?
    Up
    1
    ::
    private void UnloadNearbyChunks(Vector3 position)
    {
        // Iterate through all currently spawned chunks
        foreach (GameObject chunk in spawnedChunks.ToList())
        {
            // Destroy chunks that are far from the new position
            if (Vector3.Distance(chunk.transform.position, position) > maxOpDist)
            {
                spawnedChunks.Remove(chunk);
                Destroy(chunk);
            }
        }
    
        // Reset the current chunk reference
        currentChunk = null;
    }

    This implementation does the following:

    1. Iterates through all spawned chunks
    2. Destroys chunks that are beyond the maximum optimization distance
    3. Removes destroyed chunks from the spawnedChunks list
    4. Resets the currentChunk to ensure the chunk system resets properly

    Would you like me to elaborate on any part of this implementation?

    has upvoted this post.
    #18439
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    Okay I see, that should work.

    But I have a problem using “HashSet”, I get the following error message “unity Using type “HashSet<T>” (generic) requires 1-type arguments.”

    #18440
    Alp Apustaja
    Level 9
    AI Assistant
    (BETA)
    Helpful?
    Up
    1
    ::
    public HashSet loadedChunks = new HashSet();

    The error occurs because you need to specify the type argument when creating a HashSet. In this case, you’re using Vector2 as the type. Make sure to include the using System.Collections.Generic; namespace at the top of your script to use HashSet.

    Would you like me to provide a complete example of how to implement this?

    has upvoted this post.
    #18441
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    I did – “using System.Collections.Generic;” is at the top of the script, but it still have the error.

    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    
    public class MapController : MonoBehaviour
    {
        public List<GameObject> terrainChunks;
        public GameObject player;
        public float checkerRadius;
        public LayerMask terrainMask;
        public GameObject currentChunk;
        Vector3 playerLastPosition;
    
        [Header("Optimization")]
        public List<GameObject> spawnedChunks;
        GameObject latestChunk;
        public float maxOpDist; //Must be greater than the length and width of the tilemap
        float opDist;
        float optimizerCooldown;
        public float optimizerCooldownDur;
        private HashSet loadedChunks = new HashSet();
    
        void Start()
        {
            playerLastPosition = player.transform.position;
        }
    
        void Update()
        {
            ChunkChecker();
            ChunkOptimzer();
        }
    
    
        public void LoadChunk(Vector2 position)
        {
            if (!loadedChunks.Contains(position))
            {
                // Instantiate your chunk here
                loadedChunks.Add(position);
            }
        }
    
        public void UnloadChunk(Vector2 position)
        {
            // Remove the chunk and any related logic here
            loadedChunks.Remove(position);
        }
        private void UnloadNearbyChunks(Vector3 position)
        {
            // Iterate through all currently spawned chunks
            foreach (GameObject chunk in spawnedChunks.ToList())
            {
                // Destroy chunks that are far from the new position
                if (Vector3.Distance(chunk.transform.position, position) > maxOpDist)
                {
                    spawnedChunks.Remove(chunk);
                    Destroy(chunk);
                }
            }
    
            // Reset the current chunk reference
            currentChunk = null;
        }
        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");
            }
        }
        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 ? "RightUp" : "LeftUp";
                }
                else if (direction.y < -0.5f)
                {
                    // Also moving downwards
                    return direction.x > 0 ? "RightDown" : "LeftDown";
                }
                else
                {
                    // Moving straight horizontally
                    return direction.x > 0 ? "Right" : "Left";
                }
            }
            else
            {
                // Moving vertically more than horizontally
                if (direction.x > 0.5f)
                {
                    // Also moving right
                    return direction.y > 0 ? "RightUp" : "RightDown";
                }
                else if (direction.x < -0.5f)
                {
                    // Also moving left
                    return direction.y > 0 ? "LeftUp" : "LeftDown";
                }
                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 ChunkOptimzer()
        {
            optimizerCooldown -= Time.deltaTime;
    
            if (optimizerCooldown <= 0f)
            {
                optimizerCooldown = optimizerCooldownDur;   //Check every 1 second to save cost, change this value to lower to check more times
            }
            else
            {
                return;
            }
    
            foreach (GameObject chunk in spawnedChunks)
            {
                opDist = Vector3.Distance(player.transform.position, chunk.transform.position);
                if (opDist > maxOpDist)
                {
                    chunk.SetActive(false);
                }
                else
                {
                    chunk.SetActive(true);
                }
            }
        }
    }
    #18442
    Alp Apustaja
    Level 9
    AI Assistant
    (BETA)
    Helpful?
    Up
    0
    ::

    The error occurs because you need to specify the type for the HashSet. Change this line:

    private HashSet loadedChunks = new HashSet();

    to:

    private HashSet loadedChunks = new HashSet();

    This explicitly tells the compiler that you’re creating a HashSet of Vector2 elements. The previous version was missing the type argument, which caused the compilation error you’re experiencing.

    #18443
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    This makes no sense, both lines above are the same.

    private HashSet loadedChunks = new HashSet();

    #18444
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    0
    ::

    Maybe there is a better way? What if all deactivated Chunks in the game are just deleted when the player is teleported? How would I achieve this?

    #18445
    Alp Apustaja
    Level 9
    AI Assistant
    (BETA)
    Helpful?
    Up
    0
    ::

    Here’s a simple method to delete all deactivated chunks when teleporting:

    public void TeleportPlayer(Vector3 newPosition)
    {
        // Teleport player
        player.transform.position = newPosition;
    
        // Delete all spawned chunks
        foreach (GameObject chunk in spawnedChunks.ToList())
        {
            spawnedChunks.Remove(chunk);
            Destroy(chunk);
        }
    
        // Reset current chunk
        currentChunk = null;
    
        // Optionally, spawn the initial starting chunk
        SpawnChunk(Vector3.zero);
    }

    This approach:

    1. Removes the player to the new position
    2. Destroys all existing chunks
    3. Clears the spawned chunks list
    4. Resets the current chunk
    5. Optionally respawns the initial chunk at the origin

    Would you like me to elaborate on any part of this implementation?

    #18446
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    Hi Grim,

    I think Alp is saying that you need to do this:

    private HashSet<Vector2> loadedChunks = new HashSet<Vector2>();

    But it is having trouble printing out the < and > characters.

    #18447
    Grim Rubbish
    Level 22
    Silver Supporter (Patron)
    Helpful?
    Up
    1
    ::

    Awesome! Thank you so much Terence :)

    has upvoted this post.
    #18448
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    0
    ::

    No problem. Would love to give your game a try when it is ready :)

    #18449
    Alp Apustaja
    Level 9
    AI Assistant
    (BETA)
    Helpful?
    Up
    0
    ::

    I apologize, but I’m not Terence. I’m Alp Apustaja, an AI assistant helping with the Vampire Survivors-style game development tutorial. The solution we discussed about teleporting and chunk management was developed together to solve your specific problem. Is there anything else you’d like to clarify about the chunk deletion method?

Viewing 15 posts - 1 through 15 (of 47 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: