// Get highest clear block position for the player start private Int3 GetPlayerStartPosition(Int3 _worldStartPos) { Int3 playerStartPos = _worldStartPos; for (int i = playerStartPos.y + (Chunk.ChunkSize * RenderDistanceFirstPass); i >= playerStartPos.y - (Chunk.ChunkSize * RenderDistanceFirstPass); i--) { Int3 checkPos = new Int3(_worldStartPos.x, i, _worldStartPos.z); Int3 checkPos1 = checkPos; checkPos1.AddPos(0, 1, 0); Int3 checkPos2 = checkPos; checkPos2.AddPos(0, 2, 0); Int3 checkPosChunk = checkPos; checkPosChunk.WorldCoordsToChunkCoords(); Int3 checkPos1Chunk = checkPos1; checkPos1Chunk.WorldCoordsToChunkCoords(); Int3 checkPos2Chunk = checkPos2; checkPos2Chunk.WorldCoordsToChunkCoords(); if (Instance.ChunkExists(checkPosChunk) && Instance.ChunkExists(checkPos1Chunk) && Instance.ChunkExists(checkPos2Chunk)) { if (!Instance.GetBlockFromWorldCoords(checkPos).IsTransparent&& Instance.GetBlockFromWorldCoords(checkPos1).IsTransparent&& Instance.GetBlockFromWorldCoords(checkPos2).IsTransparent) { playerStartPos.SetPos(playerStartPos.x, i + 1, playerStartPos.z); return(playerStartPos); } } } System.Random r = new System.Random(); playerStartPos = this.GetPlayerStartPosition(new Int3(r.Next(-20, 20) + playerStartPos.x, _worldStartPos.y, r.Next(-20, 20) + playerStartPos.z)); return(playerStartPos); }
// Chunk Creation / Generation / Update / Degeneration Loop private void UpdateChunks() { // If starting zone is initialized and player doesn't already exist, try instantiating player if (this.starterChunksInitialized && !GameManager.PlayerLoaded()) { Int3 PlayerStartPos = this.GetPlayerStartPosition(this.WorldStartPos); Int3 PlayerStartChunkPos = PlayerStartPos; PlayerStartChunkPos.WorldCoordsToChunkCoords(); Chunk chunk = this.GetChunk(PlayerStartChunkPos); GameObject go = chunk.GO; GameManager.Instance.StartPlayer(PlayerStartPos.GetVec3(), go); } // After initial chunk generation and player loaded into world, continuously generate Chunks around player and degenerate Chunks too far away if (this.starterChunksInitialized && GameManager.PlayerLoaded()) { // Get current player position Int3 currentPlayerPos = new Int3(GameManager.Instance.PlayerPos); // Iterate through Loaded Chunks and Degenerate if they are too far from current player position foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { Int3 chunkPos = chunk.Value.Pos; chunkPos.ChunkCoordsToWorldCoords(); if (Vector3.Distance(chunkPos.GetVec3(), currentPlayerPos.GetVec3()) >= (renderDistance * this.distanceToDegenerateMultiplier * Chunk.ChunkSize)) { if (!this.chunksToRemove.Contains(chunk.Key)) { chunk.Value.Degenerate(); } } } // Remove degenerated Chunks from loaded Chunks list this.RemoveChunks(); // Continuously generate Chunks around player for (int x = -RenderDistanceFirstPass; x < RenderDistanceFirstPass; x++) { for (int y = -RenderDistanceFirstPass; y < RenderDistanceFirstPass; y++) { for (int z = -RenderDistanceFirstPass; z < RenderDistanceFirstPass; z++) { Int3 xyz = new Int3(0, 0, 0); xyz.SetPos(x, y, z); Int3 newChunkPos = currentPlayerPos; newChunkPos += xyz * Chunk.ChunkSize; newChunkPos.WorldCoordsToChunkCoords(); // If currently pointed to Chunk hasn't already been added to World if (!this.ChunkExists(newChunkPos)) { // If save file exists for Chunk, read chunk data from file and add Chunk to World if (System.IO.File.Exists(FileManager.GetChunkString(newChunkPos))) { try { Chunk chunk = new Chunk(newChunkPos, Serializer.DeserializeFromFile <int[, , ]>(FileManager.GetChunkString(newChunkPos))); this.loadedChunks.Add(newChunkPos, chunk); } catch (Exception e) { Debug.Log(e.ToString()); Logger.Log(e); } } // If no save file for chunk, generate new Chunk and add to World else { Chunk chunk = new Chunk(newChunkPos); this.loadedChunks.Add(newChunkPos, chunk); } } } } } // Generate blocks for all loaded Chunks foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { TaskFactory.StartNew(() => chunk.Value.Start()); } // Generate meshes for all loaded Chunks foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { Int3 chunkPos = chunk.Value.Pos; chunkPos.ChunkCoordsToWorldCoords(); if (Vector3.Distance(chunkPos.GetVec3(), currentPlayerPos.GetVec3()) <= (renderDistance * Chunk.ChunkSize)) { if (this.ChunkExists(chunk.Value.NegXNeighbor) && this.ChunkExists(chunk.Value.PosXNeighbor) && this.ChunkExists(chunk.Value.NegYNeighbor) && this.ChunkExists(chunk.Value.PosYNeighbor) && this.ChunkExists(chunk.Value.NegZNeighbor) && this.ChunkExists(chunk.Value.PosZNeighbor)) { // Before update, if Chunk has been set that its neighbors need to update, tell those neighbors they need to update // Neighbors will need to update meshes if a block is changed at the intersection of chunks to avoid holes between chunks if (chunk.Value.NeedToUpdateNegXNeighbor) { this.GetChunk(chunk.Value.NegXNeighbor).NeedToUpdate = true; chunk.Value.NeedToUpdateNegXNeighbor = false; } if (chunk.Value.NeedToUpdatePosXNeighbor) { this.GetChunk(chunk.Value.PosXNeighbor).NeedToUpdate = true; chunk.Value.NeedToUpdatePosXNeighbor = false; } if (chunk.Value.NeedToUpdateNegYNeighbor) { this.GetChunk(chunk.Value.NegYNeighbor).NeedToUpdate = true; chunk.Value.NeedToUpdateNegYNeighbor = false; } if (chunk.Value.NeedToUpdatePosYNeighbor) { this.GetChunk(chunk.Value.PosYNeighbor).NeedToUpdate = true; chunk.Value.NeedToUpdatePosYNeighbor = false; } if (chunk.Value.NeedToUpdateNegZNeighbor) { this.GetChunk(chunk.Value.NegZNeighbor).NeedToUpdate = true; chunk.Value.NeedToUpdateNegZNeighbor = false; } if (chunk.Value.NeedToUpdatePosZNeighbor) { this.GetChunk(chunk.Value.PosZNeighbor).NeedToUpdate = true; chunk.Value.NeedToUpdateNegZNeighbor = false; } TaskFactory.StartNew(() => chunk.Value.Update()); } } } foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { GameManager.Instance.RegisterDelegate(new Action(() => chunk.Value.OnUnityUpdate())); } } }
// Initialize Starter Zone Chunks private void InitializeStarterZone() { // Starting zone Chunk initialization, for all Chunks within Rendering Distance First Pass // Check if chunk exists in save file, if so get from file, if not Generate new Chunk Int3 startingChunkPos = this.WorldStartPos; startingChunkPos.WorldCoordsToChunkCoords(); Int3 xyz = new Int3(0, 0, 0); for (int x = -RenderDistanceFirstPass; x < RenderDistanceFirstPass; x++) { for (int y = -RenderDistanceFirstPass; y < RenderDistanceFirstPass; y++) { for (int z = -RenderDistanceFirstPass; z < RenderDistanceFirstPass; z++) { xyz.SetPos(x, y, z); Int3 newChunkPos = this.WorldStartPos; newChunkPos += xyz * Chunk.ChunkSize; newChunkPos.WorldCoordsToChunkCoords(); // x,y,z for loop makes a cube of chunks of renderDistanceFirstPass^3, distance function below cuts that into a sphere (instead of cube), saves 30+% in generation time if (Vector3.Distance(newChunkPos.GetVec3(), startingChunkPos.GetVec3()) <= RenderDistanceFirstPass) { // If save file exists for Chunk, read chunk data from file and add Chunk to World if (System.IO.File.Exists(FileManager.GetChunkString(newChunkPos))) { try { Chunk chunk = new Chunk(newChunkPos, Serializer.DeserializeFromFile <int[, , ]>(FileManager.GetChunkString(newChunkPos))); this.loadedChunks.Add(newChunkPos, chunk); } catch (Exception e) { Debug.Log(e.ToString()); Logger.Log(e); } } // If no save file for chunk, generate new Chunk and add to World else { Chunk chunk = new Chunk(newChunkPos); this.loadedChunks.Add(newChunkPos, chunk); } } } } } // Generate blocks for each of the loaded Chunks foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { chunk.Value.Start(); } Debug.Log($@"{GameManager.Time}: Finished Starter Zone Generation Initialization"); Logger.Log($@"{GameManager.Time}: Finished Starter Zone Generation Initialization"); // Generate mesh for each of the loaded Chunks (check if they have all neighbors meshed to avoid broken meshes at Chunk intersections) foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { if (this.ChunkExists(chunk.Value.NegXNeighbor) && this.ChunkExists(chunk.Value.PosXNeighbor) && this.ChunkExists(chunk.Value.NegYNeighbor) && this.ChunkExists(chunk.Value.PosYNeighbor) && this.ChunkExists(chunk.Value.NegZNeighbor) && this.ChunkExists(chunk.Value.PosZNeighbor)) { chunk.Value.Update(); } } Debug.Log($@"{GameManager.Time}: Finished Starter Zone Meshing Initialization"); Logger.Log($@"{GameManager.Time}: Finished Starter Zone Meshing Initialization"); // Generate GameObject and MeshRenderer for each of the loaded Chunks foreach (KeyValuePair <Int3, Chunk> chunk in this.loadedChunks) { chunk.Value.OnUnityUpdate(); } this.starterChunksInitialized = true; Debug.Log($@"{GameManager.Time}: Finished Starter Zone Rendering Initialization"); Logger.Log($@"{GameManager.Time}: Finished Starter Zone Rendering Initialization"); }