Assets/Scenes/GameplayScene.unity | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Cache/TilePool.cs | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Cache/TilePool.cs.meta | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/CustomRuleTile.cs | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/GenerateTileMap.cs | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Jobs.meta | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Jobs/OreGnerationJob.cs | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Jobs/OreGnerationJob.cs.meta | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Jobs/TerrainGenerationJob.cs | ●●●●● patch | view | raw | blame | history | |
Assets/Scripts/Jobs/TerrainGenerationJob.cs.meta | ●●●●● patch | view | raw | blame | history |
Assets/Scenes/GameplayScene.unity
@@ -4521,11 +4521,6 @@ m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1725312356} m_CullTransparentMesh: 1 --- !u!1 &1776306376 stripped GameObject: m_CorrespondingSourceObject: {fileID: 8598998496262044661, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} m_PrefabInstance: {fileID: 75655679957548990} m_PrefabAsset: {fileID: 0} --- !u!1 &1794216239 GameObject: m_ObjectHideFlags: 0 @@ -4566,7 +4561,7 @@ m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1794216239} m_Enabled: 1 m_Enabled: 0 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 @@ -5142,6 +5137,7 @@ m_EditorClassIdentifier: forestRuleTile: {fileID: 11400000, guid: 3999614e192b37546a6b710bf5ceb30c, type: 2} borderTile: {fileID: 11400000, guid: dcef846474e534b45ab3b175559c19a2, type: 2} tilePool: {fileID: 2140657251} --- !u!1 &1955008394 GameObject: m_ObjectHideFlags: 0 @@ -5386,6 +5382,50 @@ hasTopBuddy: 0 hasBottomBuddy: 0 reverseScale: 1 --- !u!1 &2140657250 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 2140657252} - component: {fileID: 2140657251} m_Layer: 0 m_Name: TilePool m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &2140657251 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2140657250} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: ca130bf5edb364941807e577a7d1b7d6, type: 3} m_Name: m_EditorClassIdentifier: --- !u!4 &2140657252 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2140657250} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 84.880615, y: -3.9463425, z: -2.3778288} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &18669457987763773 MonoBehaviour: m_ObjectHideFlags: 0 @@ -5439,6 +5479,18 @@ serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: - target: {fileID: 851313892602111808, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: player value: objectReference: {fileID: 254538002} - target: {fileID: 851313892602111808, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: fogTilemap value: objectReference: {fileID: 1794216242} - target: {fileID: 851313892602111808, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: mainCamera value: objectReference: {fileID: 519420031} - target: {fileID: 5654596948088327753, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_LocalPosition.x value: 721.6197 @@ -5484,17 +5536,9 @@ value: objectReference: {fileID: 1409843025} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: tileMap value: objectReference: {fileID: 1919262559} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: tilemap value: objectReference: {fileID: 1919262559} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: fogOfWar value: objectReference: {fileID: 75655679957548994} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: playerUI value: @@ -5503,10 +5547,6 @@ propertyPath: fogTilemap value: objectReference: {fileID: 1794216242} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: gameCanvas value: objectReference: {fileID: 90387968388784992} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: pauseMenuUI value: @@ -5519,30 +5559,6 @@ propertyPath: worldSpaceUI value: objectReference: {fileID: 97771189} - target: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: lootTextPrefab value: objectReference: {fileID: 456827189715955869, guid: 9014e3792e948bf4e81fdfdc7f38c5d9, type: 3} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.size value: 15 objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_ActionId value: b425611e-206f-4a50-938e-ad263b5330a1 objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_ActionName value: UI/Inventory[/Keyboard/b,/Keyboard/i] objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.size value: 1 objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.data[0].m_Mode value: 0 objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[13].m_PersistentCalls.m_Calls.Array.data[0].m_Target value: @@ -5551,81 +5567,26 @@ propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.data[0].m_Target value: objectReference: {fileID: 7398061984209721034} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.data[0].m_CallState value: 2 objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[13].m_PersistentCalls.m_Calls.Array.data[0].m_MethodName value: OnEscapedPressed objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.data[0].m_MethodName value: OnInventoryButtonPressed objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName value: InventoryDisplay, Assembly-CSharp objectReference: {fileID: 0} - target: {fileID: 7748736149887392517, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_ActionEvents.Array.data[14].m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName value: UnityEngine.Object, UnityEngine objectReference: {fileID: 0} - target: {fileID: 8598998496262044661, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_Name value: GameManager objectReference: {fileID: 0} - target: {fileID: 8598998496262044661, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} propertyPath: m_IsActive value: 1 objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] m_AddedComponents: - targetCorrespondingSourceObject: {fileID: 8598998496262044661, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} insertIndex: -1 addedObject: {fileID: 75655679957548994} m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} --- !u!114 &75655679957548991 stripped MonoBehaviour: m_CorrespondingSourceObject: {fileID: 6908574976455911116, guid: 7296d9a2424531f4ba42c0c75e9c48a0, type: 3} m_PrefabInstance: {fileID: 75655679957548990} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1776306376} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: ee189283a861cae42b728253b8229316, type: 3} m_Name: m_EditorClassIdentifier: --- !u!114 &75655679957548994 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1776306376} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e98672417f6c56e4d8020c2453ced645, type: 3} m_Name: m_EditorClassIdentifier: fogTilemap: {fileID: 1794216242} fogTile: {fileID: 11400000, guid: 54e9042346ddcc347aa31573844df14b, type: 2} player: {fileID: 254538002} viewRadius: 5 mainCamera: {fileID: 519420031} fogLevels: - tile: {fileID: 11400000, guid: ee11684c9f570a741917f425e4ff9bf1, type: 2} opacity: 20 - tile: {fileID: 11400000, guid: cfdca8d69259ece46ac1c967fcfcc447, type: 2} opacity: 40 - tile: {fileID: 11400000, guid: e70bfd59f79cdce449d7a6a67b4a1722, type: 2} opacity: 60 - tile: {fileID: 11400000, guid: 546f1dbae64d6ce49b8474fc131c86b9, type: 2} opacity: 80 - tile: {fileID: 11400000, guid: 54e9042346ddcc347aa31573844df14b, type: 2} opacity: 100 --- !u!223 &90387968388784992 Canvas: m_ObjectHideFlags: 0 @@ -6091,6 +6052,10 @@ objectReference: {fileID: 0} - target: {fileID: 3345854317100013954, guid: c220ec455fce341408d66d880b464cad, type: 3} propertyPath: m_Name value: Player objectReference: {fileID: 0} - target: {fileID: 3345854317100013954, guid: c220ec455fce341408d66d880b464cad, type: 3} propertyPath: m_TagString value: Player objectReference: {fileID: 0} m_RemovedComponents: [] @@ -14343,3 +14308,4 @@ - {fileID: 318061306} - {fileID: 1190838621} - {fileID: 1162250618} - {fileID: 2140657252} Assets/Scripts/Cache/TilePool.cs
New file @@ -0,0 +1,79 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Tilemaps; public class TilePool : MonoBehaviour { private Dictionary<Vector3Int, TileData> pooledTileData = new Dictionary<Vector3Int, TileData>(); private int poolSize = 10000; // Adjust pool size as needed private Queue<TileData> availableTileData = new Queue<TileData>(); private HashSet<Vector3Int> activeTilePositions = new HashSet<Vector3Int>(); // Cache for rule tile data private Dictionary<CustomRuleTile, TileBase[]> tileDataCache = new Dictionary<CustomRuleTile, TileBase[]>(); [System.Serializable] public class TileData { public Vector3Int position; public CustomRuleTile tileType; public bool isActive; } public void InitializePool(CustomRuleTile defaultTile) { pooledTileData.Clear(); availableTileData.Clear(); activeTilePositions.Clear(); // Pre-calculate tile data for the default tile if (!tileDataCache.ContainsKey(defaultTile)) { var tileData = new TileBase[9]; // Cache neighboring tile configurations tileDataCache[defaultTile] = tileData; } for (int i = 0; i < poolSize; i++) { availableTileData.Enqueue(new TileData { tileType = defaultTile, isActive = false }); } } public TileData GetTileData(Vector3Int position, CustomRuleTile tileType) { if (!activeTilePositions.Contains(position)) { if (availableTileData.Count > 0) { var tileData = availableTileData.Dequeue(); tileData.position = position; tileData.tileType = tileType; tileData.isActive = true; pooledTileData[position] = tileData; activeTilePositions.Add(position); return tileData; } } return pooledTileData.GetValueOrDefault(position); } public void ReturnTileData(Vector3Int position) { if (pooledTileData.TryGetValue(position, out TileData tileData)) { tileData.isActive = false; availableTileData.Enqueue(tileData); pooledTileData.Remove(position); } } public bool HasTileAt(Vector3Int position) { return pooledTileData.ContainsKey(position) && pooledTileData[position].isActive; } } Assets/Scripts/Cache/TilePool.cs.meta
New file @@ -0,0 +1,11 @@ fileFormatVersion: 2 guid: ca130bf5edb364941807e577a7d1b7d6 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: Assets/Scripts/CustomRuleTile.cs
@@ -10,25 +10,65 @@ { public List<TileBase> siblings = new List<TileBase>(); private Dictionary<int, bool> ruleMatchCache = new Dictionary<int, bool>(); public class Neighbor : RuleTile.TilingRule.Neighbor { public const int Sibling = 3; } public override bool RuleMatch(int neighbor, TileBase tile) { // Create a unique key for caching based on neighbor and tile int cacheKey = neighbor; if (tile != null) { cacheKey = cacheKey * 23 + tile.GetInstanceID(); // Use prime number to reduce collisions } // Check if we have a cached result if (ruleMatchCache.TryGetValue(cacheKey, out bool cachedResult)) { return cachedResult; } // If not cached, compute the result bool result; // Handle null tiles if (tile == null) return neighbor == RuleTile.TilingRule.Neighbor.NotThis; // Always allow connections to siblings or self, regardless of surrounding tiles if (tile == this || siblings.Contains(tile)) return neighbor == RuleTile.TilingRule.Neighbor.This; { result = neighbor == RuleTile.TilingRule.Neighbor.NotThis; } // Always allow connections to siblings or self else if (tile == this || siblings.Contains(tile)) { result = neighbor == RuleTile.TilingRule.Neighbor.This; } // For Sibling type explicitly if (neighbor == Neighbor.Sibling) return siblings.Contains(tile); else if (neighbor == Neighbor.Sibling) { result = siblings.Contains(tile); } // For any other case, use base behavior return base.RuleMatch(neighbor, tile); else { result = base.RuleMatch(neighbor, tile); } // Cache the result ruleMatchCache[cacheKey] = result; return result; } // Add method to clear cache when needed (e.g., when rules change or on chunk unload) public void ClearRuleCache() { ruleMatchCache.Clear(); } // Override RefreshTile to clear cache for that specific tile position public override void RefreshTile(Vector3Int position, ITilemap tilemap) { ClearRuleCache(); // Clear cache before refresh to ensure proper updates base.RefreshTile(position, tilemap); } } Assets/Scripts/GenerateTileMap.cs
@@ -1,6 +1,9 @@ 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; @@ -52,6 +55,10 @@ 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 void Awake() { @@ -70,20 +77,38 @@ // 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) return; Debug.Log($"Player Position: {playerTransform.position}"); if (playerTransform == null || isGenerating) return; Vector2Int currentChunk = GetChunkPosition(playerTransform.position); Debug.Log($"Current Chunk: {currentChunk}"); if (currentChunk != lastLoadedChunk) { StartCoroutine(UpdateLoadedChunks(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) { 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) @@ -92,6 +117,45 @@ { 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 @@ -123,16 +187,25 @@ private void UnloadChunk(Vector2Int chunk) { if (!loadedChunks.ContainsKey(chunk)) return; int startX = chunk.x * CHUNK_SIZE; int startY = chunk.y * CHUNK_SIZE; for (int x = startX; x < startX + CHUNK_SIZE; x++) // Clear rule cache for this chunk forestRuleTile.ClearRuleCache(); // Remove all tiles in the chunk if (activeChunkTiles.TryGetValue(chunk, out var tiles)) { for (int y = startY; y > startY - CHUNK_SIZE; y--) foreach (var pos in tiles) { tilemap.SetTile(new Vector3Int(x, y), null); RemoveTileWithPool(pos); } activeChunkTiles.Remove(chunk); } loadedChunks.Remove(chunk); } private void LoadGenerateablesFromResources() { @@ -232,42 +305,91 @@ private IEnumerator GenerateChunk(Vector2Int chunk, List<Vector3Int> destroyedTiles) { int startX = chunk.x * CHUNK_SIZE; int startY = (chunk.y * CHUNK_SIZE); int startY = chunk.y * CHUNK_SIZE; // Generate ground in chunk for (int x = startX; x < startX + CHUNK_SIZE; x++) // Clear rule cache for this chunk's area forestRuleTile.ClearRuleCache(); NativeArray<bool> terrainMap = default; NativeArray<float> noiseMap = default; try { if (x < 1 || x >= maxWidth) continue; terrainMap = new NativeArray<bool>(CHUNK_SIZE * CHUNK_SIZE, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); noiseMap = new NativeArray<float>(CHUNK_SIZE * CHUNK_SIZE, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); for (int y = startY; y > startY - CHUNK_SIZE; y--) var job = new TerrainGenerationJob { if (y < -groundDepth || y >= 0) continue; Vector3Int tilePos = new Vector3Int(x, y); if (IsTileDestroyed(tilePos)) continue; ChunkStartX = startX, ChunkStartY = startY, ChunkSize = CHUNK_SIZE, Scale = scale, OffsetX = offsetX, OffsetY = offsetY, MaxWidth = maxWidth, MaxDepth = maxDepth, TerrainMap = terrainMap, NoiseMap = noiseMap }; float xPerlin = ((float)x / maxWidth) * scale + offsetX; float yPerlin = ((float)Mathf.Abs(y) / maxDepth) * scale + offsetY; float perlinNoise = Mathf.PerlinNoise(xPerlin, yPerlin); // Schedule and complete job immediately to prevent frame lifetime issues job.Schedule(CHUNK_SIZE * CHUNK_SIZE, 32).Complete(); if (perlinNoise <= 0.7f) // 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]) { tilemap.SetTile(tilePos, forestRuleTile); 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)); } } } } // Generate ores in chunk if (generateables != null) { yield return GenerateOresInChunk(chunk, destroyedTiles); // 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(); } } // Don't generate borders for each chunk // Only generate borders when at world edges if (startX == 0 || startX + CHUNK_SIZE >= maxWidth || startY == 0 || startY - CHUNK_SIZE <= -groundDepth) finally { yield return GenerateBorders(); // Ensure arrays are always disposed if (terrainMap.IsCreated) terrainMap.Dispose(); if (noiseMap.IsCreated) noiseMap.Dispose(); } } Assets/Scripts/Jobs.meta
New file @@ -0,0 +1,8 @@ fileFormatVersion: 2 guid: c530f47158920ad4890aa10e7869b090 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: Assets/Scripts/Jobs/OreGnerationJob.cs
New file @@ -0,0 +1,33 @@ using System.Collections; using System.Collections.Generic; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; public struct OreGnerationJob : IJobParallelFor { [ReadOnly] public int ChunkStartX; [ReadOnly] public int ChunkStartY; [ReadOnly] public int ChunkSize; [ReadOnly] public int Weight; [ReadOnly] public int ClusterWeight; [ReadOnly] public float OffsetX; [ReadOnly] public float OffsetY; [ReadOnly] public int MaxWidth; [ReadOnly] public int MaxDepth; public NativeArray<bool> OreMap; public void Execute(int index) { int x = ChunkStartX + (index % ChunkSize); int y = ChunkStartY - (index / ChunkSize); float xPerlin = ((float)x / MaxWidth) * ClusterWeight + OffsetX; float yPerlin = ((float)Mathf.Abs(y) / MaxDepth) * ClusterWeight + OffsetY; float perlinNoise = noise.snoise(new float2(xPerlin, yPerlin)); OreMap[index] = perlinNoise <= (1f / (float)Weight); } } Assets/Scripts/Jobs/OreGnerationJob.cs.meta
New file @@ -0,0 +1,11 @@ fileFormatVersion: 2 guid: 25bcaf00876f10b44ade0b57e8159cbe MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: Assets/Scripts/Jobs/TerrainGenerationJob.cs
New file @@ -0,0 +1,42 @@ using System; using System.Collections; using System.Collections.Generic; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; public struct TerrainGenerationJob : IJobParallelFor { [ReadOnly] public int ChunkStartX; [ReadOnly] public int ChunkStartY; [ReadOnly] public int ChunkSize; [ReadOnly] public float Scale; [ReadOnly] public float OffsetX; [ReadOnly] public float OffsetY; [ReadOnly] public int MaxWidth; [ReadOnly] public int MaxDepth; [WriteOnly] public NativeArray<bool> TerrainMap; [WriteOnly] public NativeArray<float> NoiseMap; public void Execute(int index) { int x = ChunkStartX + (index % ChunkSize); int y = ChunkStartY - (index / ChunkSize); if (x < 1 || x >= MaxWidth || y < -MaxDepth || y >= 0) { TerrainMap[index] = false; NoiseMap[index] = 0f; return; } float xPerlin = ((float)x / MaxWidth) * Scale + OffsetX; float yPerlin = ((float)Mathf.Abs(y) / MaxDepth) * Scale + OffsetY; float perlinNoise = noise.snoise(new float2(xPerlin, yPerlin)); // Using Unity.Mathematics noise NoiseMap[index] = perlinNoise; TerrainMap[index] = perlinNoise <= 0.7f; } } Assets/Scripts/Jobs/TerrainGenerationJob.cs.meta
New file @@ -0,0 +1,11 @@ fileFormatVersion: 2 guid: 165d406cda8a781498fac74f1348fd81 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: