| | |
| | | using System.Collections.Generic; |
| | | using Unity.Collections; |
| | | using Unity.Jobs; |
| | | using Unity.VisualScripting; |
| | | using UnityEngine; |
| | | using UnityEngine.Tilemaps; |
| | | |
| | |
| | | |
| | | public const int CHUNK_SIZE = 16; // Size of each chunk |
| | | public int LOAD_DISTANCE = 2; // Number of chunks to load around player |
| | | private const int CACHE_CLEAR_DISTANCE = 8; // Distance in chunks before clearing cache (should be > LOAD_DISTANCE) |
| | | private Vector2Int lastCacheClearPosition; // Track position where cache was last cleared |
| | | |
| | | private Dictionary<Vector2Int, bool> loadedChunks = new Dictionary<Vector2Int, bool>(); |
| | | private Dictionary<Vector2Int, TileBase[]> chunkCache = new Dictionary<Vector2Int, TileBase[]>(); |
| | | private Transform playerTransform; // Reference to player/camera |
| | | private Vector2Int lastLoadedChunk; |
| | | private GameManager gameManager; |
| | |
| | | { |
| | | StartCoroutine(UpdateLoadedChunks(currentChunk)); |
| | | lastLoadedChunk = currentChunk; |
| | | // Check if we need to clear the cache |
| | | ClearDistantChunks(currentChunk); |
| | | } |
| | | } |
| | | |
| | |
| | | Vector2Int chunkPos = new Vector2Int(centerChunk.x + x, centerChunk.y + y); |
| | | if (!loadedChunks.ContainsKey(chunkPos)) |
| | | { |
| | | yield return GenerateChunk(chunkPos, destroyedTiles); |
| | | if (chunkCache.ContainsKey(chunkPos)) |
| | | { |
| | | // Load the chunk from the cache |
| | | yield return LoadChunkFromCache(chunkPos); |
| | | } |
| | | else |
| | | { |
| | | // Generate the chunk |
| | | yield return GenerateChunk(chunkPos, destroyedTiles); |
| | | } |
| | | loadedChunks[chunkPos] = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private IEnumerator LoadChunkFromCache(Vector2Int chunk) |
| | | { |
| | | int startX = chunk.x * CHUNK_SIZE; |
| | | int startY = (chunk.y * CHUNK_SIZE) - 1; |
| | | |
| | | 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); |
| | | |
| | | if (!IsTileDestroyed(tilePos)) |
| | | { |
| | | tilemap.SetTile(tilePos, cachedTiles[i]); |
| | | } |
| | | } |
| | | } |
| | | yield return null; |
| | | } |
| | | private void ClearDistantChunks(Vector2Int currentChunk) |
| | | { |
| | | // If this is the first time, initialize the last clear position |
| | | if (lastCacheClearPosition == default) |
| | | { |
| | | lastCacheClearPosition = currentChunk; |
| | | return; |
| | | } |
| | | |
| | | // Calculate distance moved since last cache clear |
| | | int distanceMoved = Mathf.Max( |
| | | Mathf.Abs(currentChunk.x - lastCacheClearPosition.x), |
| | | Mathf.Abs(currentChunk.y - lastCacheClearPosition.y) |
| | | ); |
| | | |
| | | // If we've moved far enough, clear distant chunks from cache |
| | | if (distanceMoved >= CACHE_CLEAR_DISTANCE) |
| | | { |
| | | List<Vector2Int> chunksToRemove = new List<Vector2Int>(); |
| | | |
| | | foreach (var chunk in chunkCache.Keys) |
| | | { |
| | | int distanceToPlayer = Mathf.Max( |
| | | Mathf.Abs(chunk.x - currentChunk.x), |
| | | Mathf.Abs(chunk.y - currentChunk.y) |
| | | ); |
| | | |
| | | // Remove chunks that are far from current position |
| | | if (distanceToPlayer > LOAD_DISTANCE * 2) |
| | | { |
| | | chunksToRemove.Add(chunk); |
| | | } |
| | | } |
| | | |
| | | // Remove the distant chunks from cache |
| | | foreach (var chunk in chunksToRemove) |
| | | { |
| | | chunkCache.Remove(chunk); |
| | | } |
| | | |
| | | lastCacheClearPosition = currentChunk; |
| | | Debug.Log($"Cleared {chunksToRemove.Count} chunks from cache. Current cache size: {chunkCache.Count}"); |
| | | } |
| | | } |
| | | private IEnumerator GenerateChunk(Vector2Int chunk, List<Vector3Int> destroyedTiles) |
| | | { |
| | | int startX = chunk.x * CHUNK_SIZE; |
| | | int startY = (chunk.y * CHUNK_SIZE) - 1; |
| | | // Check if the chunk is already in the cache |
| | | LoadChunkFromCache(chunk); |
| | | |
| | | // Create job data |
| | | var groundJob = new GenerateGroundJob |
| | |
| | | |
| | | // 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]) |
| | |
| | | if (!IsTileDestroyed(tilePos)) |
| | | { |
| | | tilemap.SetTile(tilePos, forestRuleTile); |
| | | newChunkTiles[i] = forestRuleTile; |
| | | } |
| | | else |
| | | { |
| | | newChunkTiles[i] = null; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | newChunkTiles[i] = null; |
| | | } |
| | | } |
| | | |
| | | // Save the generated chunk to the cache |
| | | chunkCache[chunk] = newChunkTiles; |
| | | |
| | | // Clean up native array |
| | | groundJob.groundTiles.Dispose(); |
| | |
| | | { |
| | | float xPerlin = ((float)x / maxWidth) * (float)generateable.clusterWeight + offsetX; |
| | | float yPerlin = ((float)Mathf.Abs(y) / maxDepth) * (float)generateable.clusterWeight + offsetY; |
| | | |
| | | float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); |
| | | |
| | | if (perlinNoise <= (1f / (float)generateable.weight)) |
| | |
| | | { |
| | | perlinNoiseCache.Dispose(); |
| | | } |
| | | chunkCache.Clear(); |
| | | } |
| | | #if UNITY_EDITOR |
| | | private void OnDrawGizmos() |