|  |  |  | 
|---|
|  |  |  | using System; | 
|---|
|  |  |  | using System.Collections; | 
|---|
|  |  |  | using System.Collections.Generic; | 
|---|
|  |  |  | using System.Linq; | 
|---|
|  |  |  | using Unity.Collections; | 
|---|
|  |  |  | using Unity.Jobs; | 
|---|
|  |  |  | using Unity.VisualScripting; | 
|---|
|  |  |  | using UnityEngine; | 
|---|
|  |  |  | using UnityEngine.Tilemaps; | 
|---|
|  |  |  | 
|---|
|  |  |  | { | 
|---|
|  |  |  | public int? seed; | 
|---|
|  |  |  | public static int maxWidth = 256; | 
|---|
|  |  |  | public static int maxHeight = 384; | 
|---|
|  |  |  | public static int maxGroundHeight = 256; | 
|---|
|  |  |  | public static int maxDepth = 384; | 
|---|
|  |  |  | public static int groundDepth = 256; | 
|---|
|  |  |  | private float scale; | 
|---|
|  |  |  | private float offsetX; | 
|---|
|  |  |  | private float offsetY; | 
|---|
|  |  |  | Tilemap tilemap; | 
|---|
|  |  |  | public CustomRuleTile forestRuleTile; | 
|---|
|  |  |  | public TileBase borderTile; | 
|---|
|  |  |  | public List<Generateable> generateables; | 
|---|
|  |  |  | private List<Generateable> generateables; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //public List<TileBase> tiles; | 
|---|
|  |  |  | public const int CHUNK_SIZE = 16; // Size of each chunk | 
|---|
|  |  |  | public const int LOAD_DISTANCE = 2; // Number of chunks to load around player | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private Dictionary<Vector2Int, bool> loadedChunks = new Dictionary<Vector2Int, bool>(); | 
|---|
|  |  |  | private Transform playerTransform; // Reference to player/camera | 
|---|
|  |  |  | private Vector2Int lastLoadedChunk; | 
|---|
|  |  |  | private GameManager gameManager; | 
|---|
|  |  |  | private Dictionary<Vector2Int, HashSet<Vector3Int>> activeChunkTiles = new Dictionary<Vector2Int, HashSet<Vector3Int>>(); | 
|---|
|  |  |  | public TilePool tilePool; // Reference to the TilePool script | 
|---|
|  |  |  | private bool isGenerating = false; | 
|---|
|  |  |  | private Dictionary<Vector2Int, List<(Vector3Int position, CustomRuleTile tile)>> chunkTileData = new Dictionary<Vector2Int, List<(Vector3Int position, CustomRuleTile tile)>>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private void Awake() | 
|---|
|  |  |  | { | 
|---|
|  |  |  | gameManager = FindObjectOfType<GameManager>(); | 
|---|
|  |  |  | tilemap = GetComponent<Tilemap>(); | 
|---|
|  |  |  | playerTransform = GameObject.FindGameObjectWithTag("Player").transform; // Make sure your player has the "Player" tag | 
|---|
|  |  |  | #if DEBUG | 
|---|
|  |  |  | seed = 0123456789; | 
|---|
|  |  |  | #endif | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | SetSettingsFromSeed(seed.Value); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | transform.position = new Vector3((maxWidth / 2) * -1, (maxGroundHeight + 1) * -1, transform.position.z); | 
|---|
|  |  |  | // Position adjusted to center horizontally, but align top at y=0 | 
|---|
|  |  |  | transform.position = new Vector3((maxWidth / 2) * -1, -1, transform.position.z); | 
|---|
|  |  |  | tilePool.InitializePool(forestRuleTile); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | LoadGenerateablesFromResources(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private void Update() | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (playerTransform == null || isGenerating) return; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | Vector2Int currentChunk = GetChunkPosition(playerTransform.position); | 
|---|
|  |  |  | if (currentChunk != lastLoadedChunk) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Debug.Log($"Player moved to new chunk: {currentChunk}"); | 
|---|
|  |  |  | isGenerating = true; | 
|---|
|  |  |  | StopAllCoroutines(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | var chunksToUnload = loadedChunks.Keys | 
|---|
|  |  |  | .Where(chunk => Mathf.Abs(chunk.x - currentChunk.x) > LOAD_DISTANCE || | 
|---|
|  |  |  | Mathf.Abs(chunk.y - currentChunk.y) > LOAD_DISTANCE) | 
|---|
|  |  |  | .ToList(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | foreach (var chunk in chunksToUnload) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Debug.Log($"Unloading chunk: {chunk}"); | 
|---|
|  |  |  | UnloadChunk(chunk); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | StartCoroutine(UpdateLoadedChunksWithCompletion(currentChunk)); | 
|---|
|  |  |  | lastLoadedChunk = currentChunk; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private IEnumerator UpdateLoadedChunksWithCompletion(Vector2Int currentChunk) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | yield return UpdateLoadedChunks(currentChunk); | 
|---|
|  |  |  | isGenerating = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // When a tile is destroyed in a chunk, update GameManager's list | 
|---|
|  |  |  | private void AddDestroyedTile(Vector3Int tilePos) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (!gameManager.destroyedTiles.Contains(tilePos)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | gameManager.destroyedTiles.Add(tilePos); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private void SetTileWithPool(Vector3Int position, CustomRuleTile tileType) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | var chunk = new Vector2Int( | 
|---|
|  |  |  | Mathf.FloorToInt(position.x / (float)CHUNK_SIZE), | 
|---|
|  |  |  | Mathf.FloorToInt(position.y / (float)CHUNK_SIZE) | 
|---|
|  |  |  | ); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (!activeChunkTiles.ContainsKey(chunk)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | activeChunkTiles[chunk] = new HashSet<Vector3Int>(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | var tileData = tilePool.GetTileData(position, tileType); | 
|---|
|  |  |  | if (tileData != null) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.SetTile(position, tileData.tileType); | 
|---|
|  |  |  | activeChunkTiles[chunk].Add(position); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Refresh neighboring tiles to maintain proper rule tile connections | 
|---|
|  |  |  | for (int nx = -1; nx <= 1; nx++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int ny = -1; ny <= 1; ny++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (nx == 0 && ny == 0) continue; | 
|---|
|  |  |  | Vector3Int neighbor = new Vector3Int(position.x + nx, position.y + ny, position.z); | 
|---|
|  |  |  | if (tilemap.HasTile(neighbor)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.RefreshTile(neighbor); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private void RemoveTileWithPool(Vector3Int position) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.SetTile(position, null); | 
|---|
|  |  |  | tilePool.ReturnTileData(position); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // When checking if a tile is destroyed, use GameManager's list | 
|---|
|  |  |  | private bool IsTileDestroyed(Vector3Int tilePos) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | return gameManager.destroyedTiles.Contains(tilePos); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private IEnumerator UpdateLoadedChunks(Vector2Int currentChunk) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // Unload distant chunks | 
|---|
|  |  |  | List<Vector2Int> chunksToUnload = new List<Vector2Int>(); | 
|---|
|  |  |  | foreach (var chunk in loadedChunks.Keys) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (Mathf.Abs(chunk.x - currentChunk.x) > LOAD_DISTANCE || | 
|---|
|  |  |  | Mathf.Abs(chunk.y - currentChunk.y) > LOAD_DISTANCE) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | chunksToUnload.Add(chunk); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | foreach (var chunk in chunksToUnload) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | UnloadChunk(chunk); | 
|---|
|  |  |  | loadedChunks.Remove(chunk); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Load new chunks | 
|---|
|  |  |  | yield return LoadChunksAroundPosition(currentChunk, new List<Vector3Int>()); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private void UnloadChunk(Vector2Int chunk) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (!loadedChunks.ContainsKey(chunk)) return; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | int startX = chunk.x * CHUNK_SIZE; | 
|---|
|  |  |  | int startY = chunk.y * CHUNK_SIZE; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Clear rule cache for this chunk | 
|---|
|  |  |  | forestRuleTile.ClearRuleCache(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Remove all tiles in the chunk | 
|---|
|  |  |  | if (activeChunkTiles.TryGetValue(chunk, out var tiles)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | foreach (var pos in tiles) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | RemoveTileWithPool(pos); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | activeChunkTiles.Remove(chunk); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | loadedChunks.Remove(chunk); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private void LoadGenerateablesFromResources() | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // Clear existing siblings | 
|---|
|  |  |  | forestRuleTile.siblings.Clear(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Load all Item prefabs from the "Resources/Items" folder | 
|---|
|  |  |  | GameObject[] generateablePrefabs = Resources.LoadAll<GameObject>("Generateable"); | 
|---|
|  |  |  | generateables = new List<Generateable>(); | 
|---|
|  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | GenerateableDatabase.Instance.InitializeFromGenerateables(generateables); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (generateables.Count == 0) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Debug.LogWarning("No items found in Resources/Items folder"); | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public IEnumerator GenerateTiles(Action finishedCallback, List<Vector3Int> destroyedTiles) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // generate ground | 
|---|
|  |  |  | for (int x = 1; x < maxWidth; x++) | 
|---|
|  |  |  | // Get initial player chunk position | 
|---|
|  |  |  | Vector2Int currentChunk = GetChunkPosition(playerTransform.position); | 
|---|
|  |  |  | lastLoadedChunk = currentChunk; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Generate initial chunks around player | 
|---|
|  |  |  | yield return LoadChunksAroundPosition(currentChunk, destroyedTiles); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | finishedCallback(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private Vector2Int GetChunkPosition(Vector3 worldPosition) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // Adjust for tilemap offset | 
|---|
|  |  |  | float adjustedX = worldPosition.x - transform.position.x; | 
|---|
|  |  |  | float adjustedY = worldPosition.y - transform.position.y; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Calculate chunk position based on chunk boundaries | 
|---|
|  |  |  | // We don't add the CHUNK_SIZE/2 offset here anymore since we want to detect based on boundaries | 
|---|
|  |  |  | Vector2Int chunkPosition = new Vector2Int( | 
|---|
|  |  |  | Mathf.FloorToInt(adjustedX / CHUNK_SIZE), | 
|---|
|  |  |  | Mathf.FloorToInt(adjustedY / CHUNK_SIZE) | 
|---|
|  |  |  | ); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return chunkPosition; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private IEnumerator LoadChunksAroundPosition(Vector2Int centerChunk, List<Vector3Int> destroyedTiles) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int x = -LOAD_DISTANCE; x <= LOAD_DISTANCE; x++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int y = 1; y < maxGroundHeight; y++) | 
|---|
|  |  |  | for (int y = -LOAD_DISTANCE; y <= LOAD_DISTANCE; y++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | float xPerlin = ((float)x / maxWidth) * scale + offsetX; | 
|---|
|  |  |  | float yPerlin = ((float)y / maxHeight) * scale + offsetY; | 
|---|
|  |  |  | float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (perlinNoise <= 0.7f) | 
|---|
|  |  |  | Vector2Int chunkPos = new Vector2Int(centerChunk.x + x, centerChunk.y + y); | 
|---|
|  |  |  | if (!loadedChunks.ContainsKey(chunkPos)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Vector3Int tileSpawnCoord = new Vector3Int(x, y); | 
|---|
|  |  |  | if (!destroyedTiles.Contains(tileSpawnCoord)) | 
|---|
|  |  |  | if (chunkTileData.ContainsKey(chunkPos)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.SetTile(tileSpawnCoord, forestRuleTile); | 
|---|
|  |  |  | Debug.Log($"Reloading chunk from saved data: {chunkPos}"); | 
|---|
|  |  |  | // Reload chunk from saved data | 
|---|
|  |  |  | var tilesToUpdate = chunkTileData[chunkPos]; | 
|---|
|  |  |  | const int BATCH_SIZE = 32; | 
|---|
|  |  |  | for (int i = 0; i < tilesToUpdate.Count; i += BATCH_SIZE) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | int batchCount = Math.Min(BATCH_SIZE, tilesToUpdate.Count - i); | 
|---|
|  |  |  | for (int j = 0; j < batchCount; j++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | var (pos, tile) = tilesToUpdate[i + j]; | 
|---|
|  |  |  | SetTileWithPool(pos, tile); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | else | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Debug.Log($"Generating new chunk: {chunkPos}"); | 
|---|
|  |  |  | // Generate new chunk | 
|---|
|  |  |  | yield return GenerateChunk(chunkPos, destroyedTiles); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | loadedChunks[chunkPos] = true; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Update UI every 8 lines | 
|---|
|  |  |  | if ((x % 8) == 0) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private IEnumerator GenerateChunk(Vector2Int chunk, List<Vector3Int> destroyedTiles) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | int startX = chunk.x * CHUNK_SIZE; | 
|---|
|  |  |  | int startY = chunk.y * CHUNK_SIZE; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (generateables != null) | 
|---|
|  |  |  | // Clear rule cache for this chunk's area | 
|---|
|  |  |  | forestRuleTile.ClearRuleCache(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | NativeArray<bool> terrainMap = default; | 
|---|
|  |  |  | NativeArray<float> noiseMap = default; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | try | 
|---|
|  |  |  | { | 
|---|
|  |  |  | foreach (Generateable generateable in generateables) | 
|---|
|  |  |  | terrainMap = new NativeArray<bool>(CHUNK_SIZE * CHUNK_SIZE, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); | 
|---|
|  |  |  | noiseMap = new NativeArray<float>(CHUNK_SIZE * CHUNK_SIZE, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | var job = new TerrainGenerationJob | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int x = 0; x < maxWidth; x++) | 
|---|
|  |  |  | ChunkStartX = startX, | 
|---|
|  |  |  | ChunkStartY = startY, | 
|---|
|  |  |  | ChunkSize = CHUNK_SIZE, | 
|---|
|  |  |  | Scale = scale, | 
|---|
|  |  |  | OffsetX = offsetX, | 
|---|
|  |  |  | OffsetY = offsetY, | 
|---|
|  |  |  | MaxWidth = maxWidth, | 
|---|
|  |  |  | MaxDepth = maxDepth, | 
|---|
|  |  |  | TerrainMap = terrainMap, | 
|---|
|  |  |  | NoiseMap = noiseMap | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Schedule and complete job immediately to prevent frame lifetime issues | 
|---|
|  |  |  | job.Schedule(CHUNK_SIZE * CHUNK_SIZE, 32).Complete(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Copy data from job before yielding | 
|---|
|  |  |  | var tilesToUpdate = new List<(Vector3Int pos, CustomRuleTile tile)>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | for (int i = 0; i < terrainMap.Length; i++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (terrainMap[i]) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int y = generateable.minSpawnHeight; y < generateable.maxSpawnHeight; y++) | 
|---|
|  |  |  | int x = startX + (i % CHUNK_SIZE); | 
|---|
|  |  |  | int y = startY + (i / CHUNK_SIZE); | 
|---|
|  |  |  | Vector3Int tilePos = new Vector3Int(x, y); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (!IsTileDestroyed(tilePos)) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilesToUpdate.Add((tilePos, forestRuleTile)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Save tile data to dictionary | 
|---|
|  |  |  | chunkTileData[chunk] = tilesToUpdate; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Dispose native arrays before yielding | 
|---|
|  |  |  | terrainMap.Dispose(); | 
|---|
|  |  |  | noiseMap.Dispose(); | 
|---|
|  |  |  | terrainMap = default; | 
|---|
|  |  |  | noiseMap = default; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Now we can safely yield and process tiles | 
|---|
|  |  |  | const int BATCH_SIZE = 32; | 
|---|
|  |  |  | for (int i = 0; i < tilesToUpdate.Count; i += BATCH_SIZE) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | int batchCount = Math.Min(BATCH_SIZE, tilesToUpdate.Count - i); | 
|---|
|  |  |  | for (int j = 0; j < batchCount; j++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | var (pos, tile) = tilesToUpdate[i + j]; | 
|---|
|  |  |  | SetTileWithPool(pos, tile); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Generate ores | 
|---|
|  |  |  | if (generateables != null) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | yield return GenerateOresInChunk(chunk, destroyedTiles); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Generate borders if needed | 
|---|
|  |  |  | if (startX == 0 || startX + CHUNK_SIZE >= maxWidth || | 
|---|
|  |  |  | startY == 0 || startY - CHUNK_SIZE <= -groundDepth) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | yield return GenerateBorders(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | finally | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // Ensure arrays are always disposed | 
|---|
|  |  |  | if (terrainMap.IsCreated) terrainMap.Dispose(); | 
|---|
|  |  |  | if (noiseMap.IsCreated) noiseMap.Dispose(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //private IEnumerator CreateGroundLayer(List<Vector3Int> destroyedTiles) | 
|---|
|  |  |  | //{ | 
|---|
|  |  |  | //    for (int x = 1; x < maxWidth; x++) | 
|---|
|  |  |  | //    { | 
|---|
|  |  |  | //        for (int y = -1; y > -groundDepth; y--) | 
|---|
|  |  |  | //        { | 
|---|
|  |  |  | //            float xPerlin = ((float)x / maxWidth) * scale + offsetX; | 
|---|
|  |  |  | //            float yPerlin = ((float)Mathf.Abs(y) / maxDepth) * scale + offsetY; | 
|---|
|  |  |  | //            float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //            if (perlinNoise <= 0.7f) | 
|---|
|  |  |  | //            { | 
|---|
|  |  |  | //                Vector3Int tileSpawnCoord = new Vector3Int(x, y); | 
|---|
|  |  |  | //                if (!destroyedTiles.Contains(tileSpawnCoord)) | 
|---|
|  |  |  | //                { | 
|---|
|  |  |  | //                    tilemap.SetTile(tileSpawnCoord, forestRuleTile); | 
|---|
|  |  |  | //                } | 
|---|
|  |  |  | //            } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //        } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //        // Update UI every 8 lines | 
|---|
|  |  |  | //        if ((x % 8) == 0) | 
|---|
|  |  |  | //        { | 
|---|
|  |  |  | //            yield return null; | 
|---|
|  |  |  | //        } | 
|---|
|  |  |  | //    } | 
|---|
|  |  |  | //} | 
|---|
|  |  |  | private IEnumerator GenerateOresInChunk(Vector2Int chunk, List<Vector3Int> destroyedTiles) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | int startX = chunk.x * CHUNK_SIZE; | 
|---|
|  |  |  | int startY = chunk.y * CHUNK_SIZE; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | foreach (Generateable generateable in generateables) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // Convert spawn heights to negative values if they aren't already | 
|---|
|  |  |  | int maxY = -Mathf.Abs(generateable.maxSpawnHeight); | 
|---|
|  |  |  | int minY = -Mathf.Abs(generateable.minSpawnHeight); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Only process this chunk if it's within the ore's spawn height range | 
|---|
|  |  |  | if (startY < maxY && startY > minY) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int x = startX; x < startX + CHUNK_SIZE; x++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (x >= maxWidth) continue; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | for (int y = Mathf.Max(startY - CHUNK_SIZE, minY); y < Mathf.Min(startY, maxY); y++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | float xPerlin = ((float)x / maxWidth) * (float)generateable.clusterWeight + offsetX; | 
|---|
|  |  |  | float yPerlin = ((float)y / maxHeight) * (float)generateable.clusterWeight + offsetY; | 
|---|
|  |  |  | float yPerlin = ((float)Mathf.Abs(y) / maxDepth) * (float)generateable.clusterWeight + offsetY; | 
|---|
|  |  |  | float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (perlinNoise <= (1f / (float)generateable.weight)) | 
|---|
|  |  |  | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.SetTile(tileSpawnCoord, generateable.tile); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | //tilemap.SetTile(tileSpawnCoord, generateable.tile); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Update UI every 8 lines | 
|---|
|  |  |  | if ((x % 8) == 0) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // Update every few rows to maintain performance | 
|---|
|  |  |  | if (generateables.Count > 3) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // generate borders | 
|---|
|  |  |  | //private IEnumerator GenerateOreClusters(List<Vector3Int> destroyedTiles) | 
|---|
|  |  |  | //{ | 
|---|
|  |  |  | //    foreach (Generateable generateable in generateables) | 
|---|
|  |  |  | //    { | 
|---|
|  |  |  | //        // Convert spawn heights to negative values if they aren't already | 
|---|
|  |  |  | //        int maxY = -Mathf.Abs(generateable.maxSpawnHeight); | 
|---|
|  |  |  | //        int minY = -Mathf.Abs(generateable.minSpawnHeight); | 
|---|
|  |  |  | //        Debug.Log($"Generating {generateable.name} between Y: {minY} - {maxY}"); | 
|---|
|  |  |  | //        for (int x = 0; x < maxWidth; x++) | 
|---|
|  |  |  | //        { | 
|---|
|  |  |  | //            for (int y = maxY; y > minY; y--) | 
|---|
|  |  |  | //            { | 
|---|
|  |  |  | //                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)) | 
|---|
|  |  |  | //                { | 
|---|
|  |  |  | //                    Vector3Int tileSpawnCoord = new Vector3Int(x, y); | 
|---|
|  |  |  | //                    if (!destroyedTiles.Contains(tileSpawnCoord) && tilemap.HasTile(tileSpawnCoord)) | 
|---|
|  |  |  | //                    { | 
|---|
|  |  |  | //                        // Check potential cluster size before placing | 
|---|
|  |  |  | //                        int clusterSize = CountPotentialClusterSize(x, y, generateable.weight, generateable.clusterWeight); | 
|---|
|  |  |  | //                        if (clusterSize >= generateable.minClusterSize) | 
|---|
|  |  |  | //                        { | 
|---|
|  |  |  | //                            tilemap.SetTile(tileSpawnCoord, generateable.tile); | 
|---|
|  |  |  | //                        } | 
|---|
|  |  |  | //                        //tilemap.SetTile(tileSpawnCoord, generateable.tile); | 
|---|
|  |  |  | //                    } | 
|---|
|  |  |  | //                } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //            } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //            // Update UI every 8 lines | 
|---|
|  |  |  | //            if ((x % 8) == 0) | 
|---|
|  |  |  | //            { | 
|---|
|  |  |  | //                yield return null; | 
|---|
|  |  |  | //            } | 
|---|
|  |  |  | //        } | 
|---|
|  |  |  | //    } | 
|---|
|  |  |  | //} | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private IEnumerator GenerateBorders() | 
|---|
|  |  |  | { | 
|---|
|  |  |  | // Vertical borders (going up from underground to sky) | 
|---|
|  |  |  | for (int x = 0; x <= maxWidth; x += maxWidth) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int y = 0; y <= maxHeight; y++) | 
|---|
|  |  |  | for (int y = -groundDepth; y <= maxDepth - groundDepth; y++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.SetTile(new Vector3Int(x, y), borderTile); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | for (int y = 0; y <= maxHeight; y += maxHeight) | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Horizontal borders (at bottom and sky level) | 
|---|
|  |  |  | for (int y = -groundDepth; y <= maxDepth - groundDepth; y += maxDepth) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int x = 1; x <= maxWidth; x++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | tilemap.SetTile(new Vector3Int(x, y), borderTile); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | yield return null; | 
|---|
|  |  |  | finishedCallback(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private int CountPotentialClusterSize(int startX, int startY, int weight, int clusterWeight) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | int size = 0; | 
|---|
|  |  |  | 
|---|
|  |  |  | if (checked_positions.Contains(neighbor)) continue; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | float xPerlin = ((float)neighbor.x / maxWidth) * clusterWeight + offsetX; | 
|---|
|  |  |  | float yPerlin = ((float)neighbor.y / maxHeight) * clusterWeight + offsetY; | 
|---|
|  |  |  | float yPerlin = ((float)Mathf.Abs(neighbor.y) / maxDepth) * clusterWeight + offsetY; | 
|---|
|  |  |  | float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (perlinNoise <= (1f / (float)weight)) | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return size; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private void OnDrawGizmos() | 
|---|
|  |  |  | { | 
|---|
|  |  |  | if (!Application.isPlaying) return; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Draw current chunk boundaries | 
|---|
|  |  |  | if (playerTransform != null) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Vector2Int currentChunk = GetChunkPosition(playerTransform.position); | 
|---|
|  |  |  | Gizmos.color = Color.yellow; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Draw player position for debugging | 
|---|
|  |  |  | Gizmos.color = Color.red; | 
|---|
|  |  |  | Gizmos.DrawSphere(playerTransform.position, 0.5f); | 
|---|
|  |  |  | Gizmos.color = Color.yellow; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | for (int x = -LOAD_DISTANCE; x <= LOAD_DISTANCE; x++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | for (int y = -LOAD_DISTANCE; y <= LOAD_DISTANCE; y++) | 
|---|
|  |  |  | { | 
|---|
|  |  |  | Vector2Int chunk = new Vector2Int(currentChunk.x + x, currentChunk.y + y); | 
|---|
|  |  |  | Vector3 worldPos = new Vector3( | 
|---|
|  |  |  | chunk.x * CHUNK_SIZE + transform.position.x, | 
|---|
|  |  |  | chunk.y * CHUNK_SIZE + transform.position.y, | 
|---|
|  |  |  | 0 | 
|---|
|  |  |  | ); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Draw chunk boundary | 
|---|
|  |  |  | Gizmos.DrawWireCube( | 
|---|
|  |  |  | worldPos + new Vector3(CHUNK_SIZE / 2f, -CHUNK_SIZE / 2f, 0), | 
|---|
|  |  |  | new Vector3(CHUNK_SIZE, CHUNK_SIZE, 0) | 
|---|
|  |  |  | ); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // Draw chunk coordinates and boundaries | 
|---|
|  |  |  | UnityEditor.Handles.Label( | 
|---|
|  |  |  | worldPos + new Vector3(CHUNK_SIZE / 2f, -CHUNK_SIZE / 2f, 0), | 
|---|
|  |  |  | $"({chunk.x}, {chunk.y})\n" + | 
|---|
|  |  |  | $"X: {worldPos.x} to {worldPos.x + CHUNK_SIZE}\n" + | 
|---|
|  |  |  | $"Y: {worldPos.y} to {worldPos.y - CHUNK_SIZE}" | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|