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 3 hours, 59 minutes ago by
Alp Apustaja.
-
AuthorPosts
-
July 6, 2025 at 10:17 pm #18465July 6, 2025 at 10:21 pm #18466::
Here’s an example of how to integrate the
chunkDictionary
into theMapController.cs
:public class MapController : MonoBehaviour { // Existing fields... private Dictionary chunkDictionary = new Dictionary(); public void LoadChunk(Vector2 position) { if (!loadedChunks.Contains(position)) { // Check if chunk already exists in dictionary if (chunkDictionary.TryGetValue(position, out GameObject existingChunk)) { // Reactivate existing chunk existingChunk.SetActive(true); } else { // Spawn new chunk int rand = Random.Range(0, terrainChunks.Count); GameObject newChunk = Instantiate(terrainChunks[rand], position, Quaternion.identity); // Add to dictionary and spawned chunks list chunkDictionary[position] = newChunk; spawnedChunks.Add(newChunk); } loadedChunks.Add(position); } } public void UnloadChunk(Vector2 position) { if (chunkDictionary.TryGetValue(position, out GameObject chunk)) { // Deactivate or destroy the chunk chunk.SetActive(false); // Remove from tracking loadedChunks.Remove(position); spawnedChunks.Remove(chunk); } } void ChunkOptimzer() { // Modify existing optimizer to use dictionary foreach (var kvp in chunkDictionary.ToList()) { Vector2 chunkPosition = kvp.Key; GameObject chunk = kvp.Value; float opDist = Vector3.Distance(player.transform.position, chunk.transform.position); if (opDist > maxOpDist) { UnloadChunk(chunkPosition); } } } }
Key improvements:
- Uses
chunkDictionary
to efficiently track and retrieve chunks - Prevents duplicate chunk spawning
- Provides easy lookup and management of chunks by position
- Integrates with existing chunk loading and unloading logic
Would you like me to elaborate on any part of this implementation?
July 6, 2025 at 10:35 pm #18467::Okay, and what must be changend in the “ChunkTrigger.cs” to make everything work?
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ChunkTrigger : MonoBehaviour { MapController mc; public GameObject targetMap; void Start() { mc = FindObjectOfType<MapController>(); } public void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { Vector2 chunkPosition = new Vector2(transform.position.x, transform.position.y); mc.LoadChunk(chunkPosition); } } private void OnTriggerStay2D(Collider2D col) { if (col.CompareTag("Player")) { mc.currentChunk = targetMap; } } private void OnTriggerExit2D(Collider2D col) { if (col.CompareTag("Player")) { if (mc.currentChunk == targetMap) { Vector2 chunkPositionTest = new Vector2(transform.position.x, transform.position.y); mc.UnloadChunk(chunkPositionTest); mc.currentChunk = null; } } } }
July 6, 2025 at 10:35 pm #18468::The current
ChunkTrigger.cs
looks mostly fine, but I recommend a few minor adjustments:using System.Collections; using System.Collections.Generic; using UnityEngine; public class ChunkTrigger : MonoBehaviour { MapController mc; public GameObject targetMap; private Vector2 chunkPosition; void Start() { mc = FindObjectOfType(); // Calculate chunk position once during initialization chunkPosition = new Vector2(transform.position.x, transform.position.y); } public void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { mc.LoadChunk(chunkPosition); } } private void OnTriggerStay2D(Collider2D col) { if (col.CompareTag("Player")) { mc.currentChunk = targetMap; } } private void OnTriggerExit2D(Collider2D col) { if (col.CompareTag("Player")) { if (mc.currentChunk == targetMap) { mc.UnloadChunk(chunkPosition); mc.currentChunk = null; } } } }
Key changes:
- Moved
chunkPosition
calculation toStart()
method to avoid repeated calculations - Used the pre-calculated
chunkPosition
in all methods - Kept the core logic of loading/unloading chunks and managing
currentChunk
Would you like me to elaborate on any part of these changes?
July 6, 2025 at 10:50 pm #18469::With the above examples, chunks won’t disappear anymore. Here are my latest “MapController.cs” and “ChunkTrigger.cs” scripts:
using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEditor.VersionControl; 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<Vector2> loadedChunks = new HashSet<Vector2>(); private Dictionary<Vector2, GameObject> chunkDictionary = new Dictionary<Vector2, GameObject>(); void Start() { playerLastPosition = player.transform.position; } void Update() { ChunkChecker(); ChunkOptimzer(); } public void LoadChunk(Vector2 position) { if (!loadedChunks.Contains(position)) { // Check if chunk already exists in dictionary if (chunkDictionary.TryGetValue(position, out GameObject existingChunk)) { // Reactivate existing chunk existingChunk.SetActive(true); } else { // Spawn new chunk int rand = Random.Range(0, terrainChunks.Count); GameObject newChunk = Instantiate(terrainChunks[rand], position, Quaternion.identity); // Add to dictionary and spawned chunks list chunkDictionary[position] = newChunk; spawnedChunks.Add(newChunk); } loadedChunks.Add(position); } } public void UnloadChunk(Vector2 position) { if (chunkDictionary.TryGetValue(position, out GameObject chunk)) { // Deactivate or destroy the chunk chunk.SetActive(false); // Remove from tracking loadedChunks.Remove(position); spawnedChunks.Remove(chunk); } } public void UnloadNearbyChunks(Vector2 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() { // Modify existing optimizer to use dictionary foreach (var kvp in chunkDictionary.ToList()) { Vector2 chunkPosition = kvp.Key; GameObject chunk = kvp.Value; float opDist = Vector3.Distance(player.transform.position, chunk.transform.position); if (opDist > maxOpDist) { UnloadChunk(chunkPosition); } } } void ChunkOptimzerOld() { 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); } } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ChunkTrigger : MonoBehaviour { MapController mc; public GameObject targetMap; private Vector2 chunkPosition; void Start() { mc = FindObjectOfType<MapController>(); // Calculate chunk position once during initialization chunkPosition = new Vector2(transform.position.x, transform.position.y); } public void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { mc.LoadChunk(chunkPosition); } } private void OnTriggerStay2D(Collider2D col) { if (col.CompareTag("Player")) { mc.currentChunk = targetMap; } } private void OnTriggerExit2D(Collider2D col) { if (col.CompareTag("Player")) { if (mc.currentChunk == targetMap) { mc.UnloadChunk(chunkPosition); mc.currentChunk = null; } } } }
July 6, 2025 at 10:56 pm #18470::The scripts look mostly good, but there are a few potential improvements to prevent chunks from disappearing:
- In
MapController.cs
, modify theUnloadChunk
method to not remove the chunk from the dictionary:
public void UnloadChunk(Vector2 position) { if (chunkDictionary.TryGetValue(position, out GameObject chunk)) { // Deactivate the chunk instead of removing it completely chunk.SetActive(false); // Remove from loaded chunks, but keep in dictionary loadedChunks.Remove(position); spawnedChunks.Remove(chunk); } }
- Modify the
LoadChunk
method to reactivate inactive chunks:
public void LoadChunk(Vector2 position) { if (!loadedChunks.Contains(position)) { // Check if chunk already exists in dictionary if (chunkDictionary.TryGetValue(position, out GameObject existingChunk)) { // Reactivate existing chunk existingChunk.SetActive(true); } else { // Spawn new chunk int rand = Random.Range(0, terrainChunks.Count); GameObject newChunk = Instantiate(terrainChunks[rand], position, Quaternion.identity); // Add to dictionary and spawned chunks list chunkDictionary[position] = newChunk; spawnedChunks.Add(newChunk); } loadedChunks.Add(position); } }
- In the
ChunkOptimzer
method, consider adding a cooldown to reduce performance overhead:
private float optimizerCooldown; public float optimizerCooldownDuration = 1f; void ChunkOptimzer() { optimizerCooldown -= Time.deltaTime; if (optimizerCooldown > 0) return; optimizerCooldown = optimizerCooldownDuration; foreach (var kvp in chunkDictionary.ToList()) { Vector2 chunkPosition = kvp.Key; GameObject chunk = kvp.Value; float opDist = Vector3.Distance(player.transform.position, chunk.transform.position); if (opDist > maxOpDist) { UnloadChunk(chunkPosition); } } }
These changes should help prevent chunks from disappearing unexpectedly while maintaining efficient chunk management.
Would you like me to elaborate on any of these suggestions?
July 6, 2025 at 11:13 pm #18471::The problem is still not solved. With this configuration of those two scripts, chunks that should get deactivated just stay activated.
July 6, 2025 at 11:16 pm #18472::Could you provide more specific details about how the chunks are behaving?
Specifically:
- Are the chunks staying active even when far from the player?
- Can you confirm the
maxOpDist
value is set correctly? - Are you seeing any debug logs or error messages?
- Have you verified that the
ChunkOptimzer()
method is being called?
A detailed description of the exact behavior will help me diagnose the issue more precisely.
July 6, 2025 at 11:19 pm #18473::- The chunks staying active even when far from the player.
- maxOpDist is set correcly.
- I get any debug logs and no error messages.
- ChunkOptimzer() is called.
July 6, 2025 at 11:21 pm #18474::Let’s modify the
ChunkOptimzer()
method to add more explicit deactivation logic:void ChunkOptimzer() { // Create a list to track chunks to unload List chunksToUnload = new List(); // Iterate through all chunks in the dictionary foreach (var kvp in chunkDictionary) { Vector2 chunkPosition = kvp.Key; GameObject chunk = kvp.Value; // Calculate distance from player to chunk float distanceToPlayer = Vector3.Distance(player.transform.position, chunk.transform.position); // If chunk is too far, mark for unloading if (distanceToPlayer > maxOpDist) { // Explicitly deactivate the chunk chunk.SetActive(false); chunksToUnload.Add(chunkPosition); } } // Remove marked chunks from loaded chunks foreach (Vector2 position in chunksToUnload) { loadedChunks.Remove(position); spawnedChunks.Remove(chunkDictionary[position]); } }
Key changes:
- Explicitly deactivate chunks
- Create a separate list to track chunks to unload
- Remove chunks from
loadedChunks
andspawnedChunks
Would you like me to add some debug logging to help diagnose the issue?
July 6, 2025 at 11:33 pm #18475::Can you please post the final and completely adjusted “MapController.cs” and “ChunkTrigger.cs” for me?
July 6, 2025 at 11:36 pm #18476::Here are the complete, adjusted scripts:
MapController.cs
:using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; public class MapController : MonoBehaviour { public List terrainChunks; public GameObject player; public float checkerRadius; public LayerMask terrainMask; public GameObject currentChunk; [Header("Optimization")] public List spawnedChunks = new List(); public float maxOpDist = 50f; // Adjust based on your map size private HashSet loadedChunks = new HashSet(); private Dictionary chunkDictionary = new Dictionary(); private Vector3 playerLastPosition; private float optimizerCooldown; public float optimizerCooldownDuration = 1f; void Start() { playerLastPosition = player.transform.position; } void Update() { ChunkChecker(); ChunkOptimzer(); } public void LoadChunk(Vector2 position) { if (!loadedChunks.Contains(position)) { if (chunkDictionary.TryGetValue(position, out GameObject existingChunk)) { existingChunk.SetActive(true); } else { int rand = Random.Range(0, terrainChunks.Count); GameObject newChunk = Instantiate(terrainChunks[rand], position, Quaternion.identity); chunkDictionary[position] = newChunk; spawnedChunks.Add(newChunk); } loadedChunks.Add(position); } } public void UnloadChunk(Vector2 position) { if (chunkDictionary.TryGetValue(position, out GameObject chunk)) { chunk.SetActive(false); loadedChunks.Remove(position); spawnedChunks.Remove(chunk); } } void ChunkOptimzer() { optimizerCooldown -= Time.deltaTime; if (optimizerCooldown > 0) return; optimizerCooldown = optimizerCooldownDuration; List chunksToUnload = new List(); foreach (var kvp in chunkDictionary) { Vector2 chunkPosition = kvp.Key; GameObject chunk = kvp.Value; float distanceToPlayer = Vector3.Distance(player.transform.position, chunk.transform.position); if (distanceToPlayer > maxOpDist) { chunk.SetActive(false); chunksToUnload.Add(chunkPosition); } } foreach (Vector2 position in chunksToUnload) { loadedChunks.Remove(position); spawnedChunks.Remove(chunkDictionary[position]); } } // Rest of the existing methods (ChunkChecker, GetDirectionName, etc.) remain the same }
ChunkTrigger.cs
:using UnityEngine; public class ChunkTrigger : MonoBehaviour { MapController mc; public GameObject targetMap; private Vector2 chunkPosition; void Start() { mc = FindObjectOfType(); chunkPosition = new Vector2(transform.position.x, transform.position.y); } public void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { mc.LoadChunk(chunkPosition); } } private void OnTriggerStay2D(Collider2D col) { if (col.CompareTag("Player")) { mc.currentChunk = targetMap; } } private void OnTriggerExit2D(Collider2D col) { if (col.CompareTag("Player")) { if (mc.currentChunk == targetMap) { mc.UnloadChunk(chunkPosition); mc.currentChunk = null; } } } }
Key improvements:
- More robust chunk loading and unloading
- Explicit management of chunk visibility
- Cooldown added to chunk optimizer to reduce performance overhead
Would you like me to explain any part of these scripts in more detail?
July 7, 2025 at 12:36 am #18477::Sadly it still doesn’t work. :( Maybe Terence can help to correct the scripts above so they work fine? Or maybe there is a chance for a map generation overhaul in the future… ;)
July 7, 2025 at 3:51 am #18478::UPDATE: Deactivating the “trigger” on the start chunk (at position 0,0,0) after the first ingame second did the job for me. Teleport now works fine! :)
July 7, 2025 at 3:56 am #18479::Great! It sounds like deactivating the initial chunk’s trigger after the first second resolved the teleportation issue. This prevents the initial chunk from continuously trying to load itself, which could have been interfering with subsequent chunk loading and teleportation mechanics.
Would you like me to provide a code snippet demonstrating how to implement this trigger deactivation?
- Uses
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: