/// <summary> /// <para>Notify the chunk loading. If is a parallel execution, is added to the ChunksLoaded list in order to be build at BuildParallelyLoadedChunks method.</para> /// <para>If the chunk is for map, it is added to ChunksForMap list.</para> /// <para>Otherwise, the chunk is build.</para> /// <para>IMPORTANT: If is a parallel execution, this method can't instanciate any child of GameObject.</para> /// </summary> /// <param name="chunk">Chunk instance.</param> /// <param name="isParellel">If the chunk is loading in parallel.</param> /// <param name="isForMap">If the chunk is loading for the terrain mapping.</param> protected virtual void ChunkLoaded(Chunk chunk, bool isParallel, bool isForMap) { if (isParallel) { Monitor.Enter(ChunksLoadingLock); } if (isForMap) { ChunksLoadingForMap.Remove(chunk.Position); ChunksForMap.Add(chunk.Position, chunk); } else if (isParallel) { ChunksLoading.Remove(chunk.Position); ChunksLoaded.Add(chunk.Position, chunk); } if (isParallel) { Monitor.Exit(ChunksLoadingLock); } else if (!isForMap) { BuildChunk(chunk); CurrentChunks.Add(chunk.Position, chunk); } }
/// <summary> /// Equivalent to DynamicChunksUpdate but for terrain mapping. Loads the chunks required for the map and unloads the unrequired ones. /// </summary> /// <param name="bottomLeftPos">Bottom left corner of the map.</param> /// <param name="topRightPos">Top right corner of the map.</param> /// <param name="inMaxUpdateTime">Function from the mapping class that checks if the corresponding MaxUpdateTime is exceeded.</param> public virtual void UpdateChunksForMap(Vector2Int bottomLeftPos, Vector2Int topRightPos, System.Func <bool> inMaxUpdateTime) { Vector2Int bottomLeftChunkPos = TerrainPosToChunk(bottomLeftPos); Vector2Int topRightChunkPos = TerrainPosToChunk(topRightPos); Vector2Int mapSize = topRightChunkPos - bottomLeftChunkPos; // Destroy the don't required chunks List <Vector2Int> chunksToDestroy = new List <Vector2Int>(); foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksForMap) { if (entry.Key.x < bottomLeftChunkPos.x || entry.Key.x > topRightChunkPos.x || entry.Key.y < bottomLeftChunkPos.y || entry.Key.y > topRightChunkPos.y) { entry.Value.Destroy(); chunksToDestroy.Add(entry.Key); } } foreach (Vector2Int pos in chunksToDestroy) { ChunksForMap.Remove(pos); } // Load the chunks required for map Vector2Int chunkPos = new Vector2Int(); for (int x = 0; x <= mapSize.x; x++) { for (int y = 0; y <= mapSize.y; y++) { chunkPos.x = bottomLeftChunkPos.x + x; chunkPos.y = bottomLeftChunkPos.y + y; // If no loaded neither loading if (!CurrentChunks.ContainsKey(chunkPos) && !ChunksLoaded.ContainsKey(chunkPos) && !ChunksLoading.ContainsKey(chunkPos) && !ChunksForMap.ContainsKey(chunkPos) && !ChunksLoadingForMap.ContainsKey(chunkPos)) { LoadChunk(chunkPos, ParallelChunkLoading, true); } } } }
/// <summary> /// Builds the chunks loaded parallely. For each chunk it uses the InMaxUpdateTime method, breaking the loop if the MaxUpdateTime is exceeded. /// </summary> protected virtual void BuildParallelyLoadedChunks() { if (ChunksLoaded.Count > 0) { lock ( ChunksLoadingLock ) { Chunk chunk; List <Vector2Int> chunksBuilt = new List <Vector2Int>(ChunksLoaded.Count); float loopStartTime = Time.realtimeSinceStartup; float numIterations = 0; float averageIterationTime = 0; foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksLoaded) { if (InMaxUpdateTime(averageIterationTime)) { chunk = entry.Value; if (IsChunkRequired(chunk.Position)) { BuildChunk(chunk); CurrentChunks.Add(chunk.Position, chunk); } else { chunk.Destroy(); } chunksBuilt.Add(entry.Key); numIterations++; averageIterationTime = (Time.realtimeSinceStartup - loopStartTime) / numIterations; } else { break; } } // Delete chunks already built foreach (Vector2Int key in chunksBuilt) { ChunksLoaded.Remove(key); } } } }
/// <summary> /// <para>Checks the reference current chunk, that always has to be build.</para> /// <para>If is loading, waits for it to load and then build it.</para> /// <para>If it isn't loaded, loads it.</para> /// </summary> protected virtual void CheckReferenceCurrentChunk() { Monitor.Enter(ChunksLoadingLock); if (!CurrentChunks.ContainsKey(ReferenceChunkPos) && !ChunksLoaded.ContainsKey(ReferenceChunkPos)) { bool isLoading = ChunksLoading.TryGetValue(ReferenceChunkPos, out Chunk referenceChunk); Monitor.Exit(ChunksLoadingLock); if (isLoading) { referenceChunk.ParallelTask.Wait(); // Wait parallel loading to end BuildParallelyLoadedChunks(); // Built the chunk (and the others if are required) } else { LoadChunk(ReferenceChunkPos, true); // Ignore parallel because is impossible continue playing without this chunk } } else { Monitor.Exit(ChunksLoadingLock); } }
public virtual Chunk GetChunk(Vector2Int terrainPos) { Vector2Int chunkPos = TerrainPosToChunk(terrainPos); return(CurrentChunks.ContainsKey(chunkPos) ? CurrentChunks[chunkPos] : null); }
/// <summary> /// <para>Manages the chunks required and the current chunks using the reference position.</para> /// <para>If a chunk is required and not loaded, it loads it (or starts the load if ParallelChunkLoading is enabled).</para> /// <para>If a chunk is not required, destroy it.</para> /// <para>Moreover, when is loading the chunks required, it uses the InMaxUpdateTime method, breaking the loop if the MaxUpdateTime is exceeded.</para> /// </summary> protected virtual void DynamicChunksUpdate() { Dictionary <Vector2Int, object> chunkRequired = DynamicChunksRequired(); // Use a dictionary for faster searchs // Check chunks already created List <Vector2Int> chunksToDestroy = new List <Vector2Int>(); foreach (KeyValuePair <Vector2Int, Chunk> entry in CurrentChunks) { // If already created, don't reload if (chunkRequired.ContainsKey(entry.Key)) { chunkRequired.Remove(entry.Key); } // If don't required, unload else { chunksToDestroy.Add(entry.Key); entry.Value.Destroy(); } } // Remove chunks don't required foreach (Vector2Int chunkPos in chunksToDestroy) { CurrentChunks.Remove(chunkPos); } if (InMaxUpdateTime()) { lock ( ChunksLoadingLock ) { // Ignore chunks that are loading foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksLoading) { if (chunkRequired.ContainsKey(entry.Key)) { chunkRequired.Remove(entry.Key); } } // Ignore chunks that are already loaded foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksLoaded) { if (chunkRequired.ContainsKey(entry.Key)) { chunkRequired.Remove(entry.Key); } } // Load the other chunks float loopStartTime = Time.realtimeSinceStartup; float numIterations = 0; float averageIterationTime = 0; foreach (KeyValuePair <Vector2Int, object> entry in chunkRequired) { if (InMaxUpdateTime(averageIterationTime)) { LoadChunk(entry.Key); numIterations++; averageIterationTime = (Time.realtimeSinceStartup - loopStartTime) / numIterations; } else { break; } } } } }
/// <summary> /// Destroys all the chunks of the terrain, including all GameObjects and ParallelTasks. /// </summary> public virtual void DestroyTerrain() { IsGenerated = false; foreach (Transform child in transform) { Destroy(child.gameObject); } if (CurrentChunks != null) { foreach (KeyValuePair <Vector2Int, Chunk> entry in CurrentChunks) { entry.Value.Destroy(); } CurrentChunks.Clear(); } if (ChunksForMap != null) { foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksForMap) { entry.Value.Destroy(); } ChunksForMap.Clear(); } lock ( ChunksLoadingLock ) { if (ChunksLoading != null) { foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksLoading) { entry.Value.Destroy(); } ChunksLoading.Clear(); } if (ChunksLoaded != null) { foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksLoaded) { entry.Value.Destroy(); } ChunksLoaded.Clear(); } if (ChunksLoadingForMap != null) { foreach (KeyValuePair <Vector2Int, Chunk> entry in ChunksLoadingForMap) { entry.Value.Destroy(); } ChunksLoadingForMap.Clear(); } } }