From a576c4a6edc69f703422ab289ee46b05011b9275 Mon Sep 17 00:00:00 2001 From: miepzerino <o.skotnik@gmail.com> Date: Sun, 06 Apr 2025 17:46:38 +0000 Subject: [PATCH] #48 added cache to map savefile --- Assets/Scripts/Managers/GameManager.cs | 9 ++ Assets/Scripts/GenerateTileMap.cs | 202 ++++++++++++++++++++++++++++++++++++------------- Assets/Scripts/Saving/SaveData.cs | 19 ++++ Assets/Scripts/UI/PauseMenu.cs | 2 4 files changed, 175 insertions(+), 57 deletions(-) diff --git a/Assets/Scripts/GenerateTileMap.cs b/Assets/Scripts/GenerateTileMap.cs index 4bf8585..8870c8e 100644 --- a/Assets/Scripts/GenerateTileMap.cs +++ b/Assets/Scripts/GenerateTileMap.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using Unity.Collections; using Unity.Jobs; using Unity.VisualScripting; @@ -10,9 +11,9 @@ public class GenerateTileMap : MonoBehaviour { public int? seed; - public static int maxWidth = 256; + public static int maxWidth = 255; public static int maxDepth = 384; - public static int groundDepth = 256; + public static int groundDepth = 255; private float scale; private float offsetX; private float offsetY; @@ -61,20 +62,32 @@ //Debug.Log($"Current Chunk: {currentChunk}"); if (currentChunk != lastLoadedChunk) { - StartCoroutine(UpdateLoadedChunks(currentChunk)); + StartCoroutine(UpdateLoadedChunks(currentChunk, gameManager.destroyedTiles)); lastLoadedChunk = currentChunk; // Check if we need to clear the cache ClearDistantChunks(currentChunk); } } - - // When checking if a tile is destroyed, use GameManager's list - private bool IsTileDestroyed(Vector3Int tilePos) + public Dictionary<Vector2Int, TileBase[]> GetSaveData() { - return gameManager.destroyedTiles.Contains(tilePos); + return chunkCache; } - private IEnumerator UpdateLoadedChunks(Vector2Int currentChunk) + public void LoadChunkDataFromSave(List<SerializedChunkData> serializedChunks) + { + chunkCache.Clear(); + foreach (var chunk in serializedChunks) + { + TileBase[] tiles = chunk.tileNames.Select(name => + name == "null" ? null : + name == forestRuleTile.name ? forestRuleTile : + forestRuleTile.siblings.FirstOrDefault(s => s.name == name) + ).ToArray(); + + chunkCache[chunk.position] = tiles; + } + } + private IEnumerator UpdateLoadedChunks(Vector2Int currentChunk, List<Vector3Int> destroyedTiles) { // Unload distant chunks List<Vector2Int> chunksToUnload = new List<Vector2Int>(); @@ -93,21 +106,13 @@ } // Load new chunks - yield return LoadChunksAroundPosition(currentChunk, new List<Vector3Int>()); + yield return LoadChunksAroundPosition(currentChunk, destroyedTiles); } private void UnloadChunk(Vector2Int chunk) { - int startX = chunk.x * CHUNK_SIZE; - int startY = (chunk.y * CHUNK_SIZE)-1; - - for (int x = startX; x < startX + CHUNK_SIZE; x++) - { - for (int y = startY; y > startY - CHUNK_SIZE; y--) - { - tilemap.SetTile(new Vector3Int(x, y), null); - } - } + var emptyTiles = new TileBase[CHUNK_SIZE * CHUNK_SIZE]; + BatchSetTiles(chunk, emptyTiles); } private void LoadGenerateablesFromResources() { @@ -192,7 +197,7 @@ finishedCallback(); } - private Vector2Int GetChunkPosition(Vector3 worldPosition) + public Vector2Int GetChunkPosition(Vector3 worldPosition) { // Adjust for tilemap offset float adjustedX = worldPosition.x - transform.position.x; @@ -202,6 +207,23 @@ Mathf.FloorToInt(adjustedX / CHUNK_SIZE), Mathf.FloorToInt(adjustedY / CHUNK_SIZE) ); + } + public void UpdateChunkCache(Vector3Int cellCoord, Vector3 cellWorldPosition) + { + // Update chunk cache + Vector2Int chunkPos = GetChunkPosition(cellWorldPosition); + if (chunkCache.TryGetValue(chunkPos, out TileBase[] cachedTiles)) + { + int localX = cellCoord.x - (chunkPos.x * GenerateTileMap.CHUNK_SIZE); + int localY = (chunkPos.y * GenerateTileMap.CHUNK_SIZE) - cellCoord.y - 1; + int index = localY * GenerateTileMap.CHUNK_SIZE + localX; + + if (index >= 0 && index < cachedTiles.Length) + { + cachedTiles[index] = null; + chunkCache[chunkPos] = cachedTiles; + } + } } private IEnumerator LoadChunksAroundPosition(Vector2Int centerChunk, List<Vector3Int> destroyedTiles) { @@ -235,19 +257,40 @@ if (chunkCache.TryGetValue(chunk, out TileBase[] cachedTiles)) { - for (int i = 0; i < cachedTiles.Length; i++) - { - int x = startX + (i % CHUNK_SIZE); - int y = startY - (i / CHUNK_SIZE); - Vector3Int tilePos = new Vector3Int(x, y); + //for (int i = 0; i < cachedTiles.Length; i++) + //{ + // int x = startX + (i % CHUNK_SIZE); + // int y = startY - (i / CHUNK_SIZE); + // Vector3Int tilePos = new Vector3Int(x, y); - if (!IsTileDestroyed(tilePos)) - { - tilemap.SetTile(tilePos, cachedTiles[i]); - } - } + // if (!IsTileDestroyed(tilePos)) + // { + // tilemap.SetTile(tilePos, cachedTiles[i]); + // } + //} + BatchSetTiles(chunk, cachedTiles, gameManager.destroyedTiles); } yield return null; + } + private void BatchSetTiles(Vector2Int chunk, TileBase[] tiles, List<Vector3Int> destroyedTiles = null) + { + int startX = chunk.x * CHUNK_SIZE; + int startY = (chunk.y * CHUNK_SIZE) - 1; + + var positions = new Vector3Int[CHUNK_SIZE * CHUNK_SIZE]; + var tileArray = new TileBase[CHUNK_SIZE * CHUNK_SIZE]; + + for (int i = 0; i < tiles.Length; i++) + { + int x = startX + (i % CHUNK_SIZE); + int y = startY - (i / CHUNK_SIZE); + Vector3Int pos = new Vector3Int(x, y); + + positions[i] = pos; + tileArray[i] = destroyedTiles != null && destroyedTiles.Contains(pos) ? null : tiles[i]; + } + + tilemap.SetTiles(positions, tileArray); } private void ClearDistantChunks(Vector2Int currentChunk) { @@ -324,29 +367,36 @@ // Apply the results groundJobHandle.Complete(); TileBase[] newChunkTiles = new TileBase[CHUNK_SIZE * CHUNK_SIZE]; + //for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++) + //{ + // if (groundJob.groundTiles[i]) + // { + // int x = startX + (i % CHUNK_SIZE); + // int y = startY - (i / CHUNK_SIZE); + // Vector3Int tilePos = new Vector3Int(x, y); + + // if (!IsTileDestroyed(tilePos)) + // { + // tilemap.SetTile(tilePos, forestRuleTile); + // newChunkTiles[i] = forestRuleTile; + // } + // else + // { + // newChunkTiles[i] = null; + // } + // } + // else + // { + // newChunkTiles[i] = null; + // } + //} for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++) { - if (groundJob.groundTiles[i]) - { - int x = startX + (i % CHUNK_SIZE); - int y = startY - (i / CHUNK_SIZE); - Vector3Int tilePos = new Vector3Int(x, y); - - if (!IsTileDestroyed(tilePos)) - { - tilemap.SetTile(tilePos, forestRuleTile); - newChunkTiles[i] = forestRuleTile; - } - else - { - newChunkTiles[i] = null; - } - } - else - { - newChunkTiles[i] = null; - } + newChunkTiles[i] = groundJob.groundTiles[i] ? forestRuleTile : null; } + + // Batch update tiles + BatchSetTiles(chunk, newChunkTiles, destroyedTiles); // Save the generated chunk to the cache chunkCache[chunk] = newChunkTiles; @@ -372,6 +422,8 @@ { int startX = chunk.x * CHUNK_SIZE; int startY = chunk.y * CHUNK_SIZE; + // Get the cached tiles for this chunk + TileBase[] cachedTiles = chunkCache[chunk]; foreach (Generateable generateable in generateables) { @@ -409,6 +461,14 @@ if (clusterSize >= generateable.minClusterSize) { tilemap.SetTile(tileSpawnCoord, generateable.tile); + // Update cache + int localX = x - startX; + int localY = startY - y - 1; + int index = localY * CHUNK_SIZE + localX; + if (index >= 0 && index < cachedTiles.Length) + { + cachedTiles[index] = generateable.tile; + } } } } @@ -422,25 +482,57 @@ } } } + // Update the cache with the modified tiles + chunkCache[chunk] = cachedTiles; } private IEnumerator GenerateBorders() { - // Vertical borders (going up from underground to sky) + // Vertical borders for (int x = 0; x <= maxWidth; x += maxWidth) { for (int y = -groundDepth; y <= maxDepth - groundDepth; y++) { - tilemap.SetTile(new Vector3Int(x, y), borderTile); + Vector3Int borderPos = new Vector3Int(x, y); + tilemap.SetTile(borderPos, borderTile); + + // Update cache for affected chunk + Vector2Int chunkPos = GetChunkPosition(new Vector3(x + transform.position.x, y + transform.position.y)); + if (chunkCache.TryGetValue(chunkPos, out TileBase[] cachedTiles)) + { + int localX = x - (chunkPos.x * CHUNK_SIZE); + int localY = chunkPos.y * CHUNK_SIZE - y - 1; + int index = localY * CHUNK_SIZE + localX; + if (index >= 0 && index < cachedTiles.Length) + { + cachedTiles[index] = borderTile; + chunkCache[chunkPos] = cachedTiles; + } + } } } yield return null; - // Horizontal borders (at bottom and sky level) + // Horizontal borders for (int y = -groundDepth; y <= maxDepth - groundDepth; y += maxDepth) { - for (int x = 1; x <= maxWidth; x++) + for (int x = 1; x < maxWidth; x++) { - tilemap.SetTile(new Vector3Int(x, y), borderTile); + Vector3Int borderPos = new Vector3Int(x, y); + tilemap.SetTile(borderPos, borderTile); + + // Update cache for affected chunk + Vector2Int chunkPos = GetChunkPosition(new Vector3(x + transform.position.x, y + transform.position.y)); + if (chunkCache.TryGetValue(chunkPos, out TileBase[] cachedTiles)) + { + int localX = x - (chunkPos.x * CHUNK_SIZE); + int localY = chunkPos.y * CHUNK_SIZE - y - 1; + int index = localY * CHUNK_SIZE + localX; + if (index >= 0 && index < cachedTiles.Length) + { + cachedTiles[index] = borderTile; + chunkCache[chunkPos] = cachedTiles; + } + } } } yield return null; diff --git a/Assets/Scripts/Managers/GameManager.cs b/Assets/Scripts/Managers/GameManager.cs index 7f543e9..cf3a5ab 100644 --- a/Assets/Scripts/Managers/GameManager.cs +++ b/Assets/Scripts/Managers/GameManager.cs @@ -76,6 +76,7 @@ { //fogOfWar.LoadFromSaveData(mapState.fogOfWarData); generateTileMap.SetSettingsFromSeed(mapState.seed); + if (mapState.destroyedTiles != null && mapState.destroyedTiles.Count > 0) { // TODO rework load map (it's fucky wucky currently as I had to make an extra class for it to jsonify the tiles correctly, as unity does not like lists of arrays or 2d arrays) @@ -83,6 +84,11 @@ { destroyedTiles.Add(tile.tileCoord.ConvertToVector3Int()); } //destroyedTiles.AddRange(mapState.destroyedTiles.Select(tile => { return tile.tileCoord; }).ToList().ConvertToVector3Int()); + } + // Load chunk cache + if (mapState.chunkData != null) + { + generateTileMap.LoadChunkDataFromSave(mapState.chunkData); } } } @@ -198,8 +204,10 @@ CharacterLootObtained(tileGameObject.transform.position, item, dropAmount); } } + // Update tilemap tilemap.SetTile(cellCoord, null); destroyedTiles.Add(cellCoord); + generateTileMap.UpdateChunkCache(cellCoord, cellWorldPosition); CharacterEvents.characterDrillingToPosition.Invoke(cellWorldPosition, drillDirection); } else @@ -208,6 +216,7 @@ } } } + #endregion diff --git a/Assets/Scripts/Saving/SaveData.cs b/Assets/Scripts/Saving/SaveData.cs index cb1ee37..939850c 100644 --- a/Assets/Scripts/Saving/SaveData.cs +++ b/Assets/Scripts/Saving/SaveData.cs @@ -2,7 +2,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using UnityEngine; +using UnityEngine.Tilemaps; #region player data [Serializable] @@ -52,7 +54,8 @@ public int seed; public List<DestroyedTile> destroyedTiles; public FogOfWarData fogOfWarData; - public SaveDataMap(List<Vector3Int> destroyedTiles, int seed, FogOfWarData fogOfWarData) + public List<SerializedChunkData> chunkData; // New field + public SaveDataMap(List<Vector3Int> destroyedTiles, int seed, FogOfWarData fogOfWarData, Dictionary<Vector2Int, TileBase[]> chunkCache) { this.seed = seed; this.destroyedTiles = new List<DestroyedTile>(); @@ -61,6 +64,8 @@ { this.destroyedTiles.Add(new DestroyedTile(item)); } + // Convert chunk cache to serializable format + this.chunkData = chunkCache.Select(kvp => new SerializedChunkData(kvp.Key, kvp.Value)).ToList(); } } @@ -87,4 +92,16 @@ { public List<FogTileData> discoveredTiles = new List<FogTileData>(); } +[System.Serializable] +public class SerializedChunkData +{ + public Vector2Int position; + public string[] tileNames; // Store tile names instead of TileBase references + + public SerializedChunkData(Vector2Int pos, TileBase[] tiles) + { + position = pos; + tileNames = tiles.Select(t => t != null ? t.name : "null").ToArray(); + } +} #endregion diff --git a/Assets/Scripts/UI/PauseMenu.cs b/Assets/Scripts/UI/PauseMenu.cs index 93337d0..3a4d141 100644 --- a/Assets/Scripts/UI/PauseMenu.cs +++ b/Assets/Scripts/UI/PauseMenu.cs @@ -65,7 +65,7 @@ Inventory playerInventory = GameObject.Find("Player").GetComponent<Inventory>(); SaveSystem.SavePlayer(new SaveDataPlayer(playerController, playerInventory)); GameManager gameManager= GameObject.Find("GameManager").GetComponent<GameManager>(); - SaveSystem.SaveMapState(new SaveDataMap(gameManager.destroyedTiles, gameManager.generateTileMap.seed.Value, gameManager.GetComponent<FogOfWar>().GetSaveData())); + SaveSystem.SaveMapState(new SaveDataMap(gameManager.destroyedTiles, gameManager.generateTileMap.seed.Value, gameManager.GetComponent<FogOfWar>().GetSaveData(),gameManager.generateTileMap.GetSaveData())); animator.SetTrigger("GameSaved"); } -- Gitblit v1.9.3