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
- This topic has 25 replies, 3 voices, and was last updated 54 minutes ago by
Alp Apustaja.
-
AuthorPosts
-
July 6, 2025 at 3:38 pm #18435::
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. July 6, 2025 at 3:45 pm #18436::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
andChunkTrigger.cs
:-
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. -
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. -
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 theUnloadNearbyChunks
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.
July 6, 2025 at 3:52 pm #18437::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 }
July 6, 2025 at 3:55 pm #18438::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:
- Iterates through all spawned chunks
- Destroys chunks that are beyond the maximum optimization distance
- Removes destroyed chunks from the
spawnedChunks
list - 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. July 6, 2025 at 3:59 pm #18439::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.”
July 6, 2025 at 4:00 pm #18440::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 theusing 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. July 6, 2025 at 4:04 pm #18441::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); } } } }
July 6, 2025 at 4:05 pm #18442::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.
July 6, 2025 at 4:11 pm #18443::This makes no sense, both lines above are the same.
private HashSet loadedChunks = new HashSet();
July 6, 2025 at 4:15 pm #18444::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?
July 6, 2025 at 4:15 pm #18445::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:
- Removes the player to the new position
- Destroys all existing chunks
- Clears the spawned chunks list
- Resets the current chunk
- Optionally respawns the initial chunk at the origin
Would you like me to elaborate on any part of this implementation?
July 6, 2025 at 4:17 pm #18446::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.
July 6, 2025 at 4:19 pm #18447July 6, 2025 at 4:20 pm #18448July 6, 2025 at 4:20 pm #18449::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?
-
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: