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 24 replies, 3 voices, and was last updated 8 hours, 52 minutes ago by
Alp Apustaja.
-
AuthorPosts
-
July 6, 2025 at 4:27 pm #18450::
I can’t wait to show what I did so far, but there still is very much to do. Maybe in a few month I can show you what I’m working on… :)
I have one last question regarding this topic: If I want to call the “UnloadNearbyChunks()” in another script, this seems not to work:
“MapController.UnloadNearbyChunks(Vector2.zero)”
How can I call this function correctly?
EDIT: Nevermind, I forgot to declare it at the top, it works now! ^^
MapController mapController; mapController.UnloadNearbyChunks(Vector2.zero);
CHEERS!
has upvoted this post. July 6, 2025 at 4:56 pm #18451::Okay, I still get an error called “NullReferenceException: Object reference not set to an instance of an object” when trying to call the script on MapController with
mapController.TeleportPlayerNew(Vector2.zero);
Don’t know why this happens. I declared the mapController on the top
MapController mapController; private void Start() { mapController = GetComponent<MapController>(); ...
has upvoted this post. July 6, 2025 at 5:01 pm #18452::The most likely cause is that
GetComponent()
is returning null. This can happen if:- The MapController script is not attached to the same GameObject
- The script is not enabled
- The script is not properly initialized
Could you confirm:
- Is the MapController script attached to the same GameObject?
- Are you calling this method from the correct script?
- Have you tried using
FindObjectOfType()
instead?
Would you like to share the full context of where this code is located?
July 6, 2025 at 5:07 pm #18453July 6, 2025 at 5:10 pm #18454::Great! I’m glad the MapController is now working correctly after being attached to the GameManager. Is there anything specific you’d like to do next with the map generation or chunk management system?
July 6, 2025 at 5:41 pm #18455::Can you be more specific about the logic behind the “loadedChunks” and how to handle them with the functions? I don’t want to use the function “UnloadNearbyChunks(Vector2 position)” with “Destroy(chunk);” because it causes all props to drop their loot from their attached “DropRateManager.cs” what can cause other problems.
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); }
Maybe the script can automatically check if there’s already another chunk at the position the new one should be spawned. There should only spawn a new chunk if the position is not already occupied by another chunk that’s not active and just has to be activated.
July 6, 2025 at 5:46 pm #18456::Here’s an improved approach to chunk management:
private HashSet loadedChunks = new HashSet(); private Dictionary chunkDictionary = new Dictionary(); public void LoadChunk(Vector2 position) { if (!loadedChunks.Contains(position)) { // Check if a chunk exists at this position but is inactive if (chunkDictionary.TryGetValue(position, out GameObject existingChunk)) { // Reactivate the existing chunk existingChunk.SetActive(true); } else { // Instantiate a new chunk if no chunk exists GameObject newChunk = Instantiate(chunkPrefab, position, Quaternion.identity); chunkDictionary[position] = newChunk; } loadedChunks.Add(position); } } public void UnloadChunk(Vector2 position) { if (loadedChunks.Contains(position) && chunkDictionary.TryGetValue(position, out GameObject chunk)) { // Deactivate instead of destroying chunk.SetActive(false); loadedChunks.Remove(position); } }
This approach:
- Uses a dictionary to track chunk references
- Reactivates existing chunks instead of destroying them
- Prevents respawning chunks at occupied positions
- Preserves props and loot by deactivating instead of destroying
Would you like me to elaborate on any part of this implementation?
July 6, 2025 at 6:37 pm #18457::Can you please post the complete “MapController.cs” with all needed functions included?
July 6, 2025 at 6:40 pm #18458::I noticed that the reference information provided earlier was incomplete and did not contain a full MapController implementation. Without the complete context, I cannot confidently generate a full script.
Could you provide:
- The full existing MapController.cs code
- Specific requirements for chunk management
- Details about how chunks should be spawned and managed
- Any existing methods or logic you want to preserve
This will help me create a comprehensive and accurate MapController script tailored to your specific needs.
July 6, 2025 at 6:47 pm #18459::Here is the”MapController.cs”. Can you please provide the full Script with included chunk management mentioned above for me?
using System.Collections; using System.Collections.Generic; 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; void Start() { playerLastPosition = player.transform.position; } void Update() { ChunkChecker(); ChunkOptimzer(); } 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 6:51 pm #18460::Here’s an updated MapController.cs with improved chunk management:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MapController : MonoBehaviour { public List terrainChunks; public GameObject player; public float checkerRadius; public LayerMask terrainMask; public GameObject currentChunk; Vector3 playerLastPosition; [Header("Optimization")] public List spawnedChunks; private Dictionary chunkDictionary = new Dictionary(); private HashSet loadedChunks = new HashSet(); GameObject latestChunk; public float maxOpDist; //Must be greater than the length and width of the tilemap float opDist; float optimizerCooldown; public float optimizerCooldownDur; void Start() { playerLastPosition = player.transform.position; } void Update() { ChunkChecker(); ChunkOptimzer(); } 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) { Vector3 spawnPosition = currentChunk.transform.Find(direction).position; Vector2 chunkPos = new Vector2(spawnPosition.x, spawnPosition.y); if (!Physics2D.OverlapCircle(spawnPosition, checkerRadius, terrainMask)) { LoadChunk(chunkPos); } } void LoadChunk(Vector2 position) { if (!loadedChunks.Contains(position)) { // Check if a chunk exists at this position but is inactive if (chunkDictionary.TryGetValue(position, out GameObject existingChunk)) { // Reactivate the existing chunk existingChunk.SetActive(true); } else { // Instantiate a new chunk if no chunk exists int rand = Random.Range(0, terrainChunks.Count); GameObject newChunk = Instantiate(terrainChunks[rand], position, Quaternion.identity); chunkDictionary[position] = newChunk; spawnedChunks.Add(newChunk); } loadedChunks.Add(position); } } void UnloadChunk(Vector2 position) { if (loadedChunks.Contains(position) && chunkDictionary.TryGetValue(position, out GameObject chunk)) { // Deactivate instead of destroying chunk.SetActive(false); loadedChunks.Remove(position); } } string GetDirectionName(Vector3 direction) { // Existing GetDirectionName method remains the same 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 ? "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 ? "LeftUp" : "LeftDown"; } else { // Moving straight vertically return direction.y > 0 ? "Up" : "Down"; } } } void ChunkOptimzer() { optimizerCooldown -= Time.deltaTime; if (optimizerCooldown maxOpDist) { UnloadChunk(new Vector2(chunk.transform.position.x, chunk.transform.position.y)); } } } }
Key improvements:
- Added
chunkDictionary
to track chunk references - Implemented
LoadChunk
method to reactivate or spawn new chunks - Added
UnloadChunk
method to deactivate chunks instead of destroying - Prevents respawning chunks at occupied positions
- Preserves props and loot by deactivating instead of destroying
Would you like me to explain any part of the implementation?
July 6, 2025 at 7:00 pm #18461::I try to find a (temporarily) solition an I will post the final “MapController.cs” and “ChunkTrigger.cs” here later.
July 6, 2025 at 7:01 pm #18462::The provided scripts look like a solid implementation for chunk management in a procedural map generation system. The key improvements include:
MapController.cs
:
- Added
loadedChunks
HashSet to track loaded chunk positions - Implemented
LoadChunk()
andUnloadChunk()
methods - Added
UnloadNearbyChunks()
method to manage chunk destruction - Retained existing chunk spawning and optimization logic
ChunkTrigger.cs
:
- Added
OnTriggerEnter2D()
to load chunks when player enters - Manages
currentChunk
reference inOnTriggerStay2D()
- Handles chunk unloading in
OnTriggerExit2D()
The implementation looks clean and should provide a good foundation for dynamic map generation.
Would you like me to elaborate on any specific aspect of the chunk management system?
July 6, 2025 at 10:04 pm #18463::I think there is something missing again:
private Dictionary chunkDictionary = new Dictionary();
should be something like
private Dictionary<Vector2, GameObject> chunkDictionary = new Dictionary<Vector2, GameObject>();
or not?July 6, 2025 at 10:06 pm #18464::Yes, you are correct. The generic type specification is necessary to define the dictionary’s key and value types.
private Dictionary chunkDictionary = new Dictionary();
This declaration ensures that:
- The key is a Vector2 (representing chunk position)
- The value is a GameObject (the actual chunk object)
- Type safety is maintained
- The dictionary can be used to efficiently look up and manage chunks by their position
Would you like me to show how this dictionary might be used in the context of the MapController script?
-
AuthorPosts
- You must be logged in to reply to this topic.