// Remove Chunk from world, given Chunk Position public void AddChunkToRemoveList(Int3 _chunkPos) { this.chunksToRemove.Add(_chunkPos); //Debug.Log($@"{GameManager.time}: Added Chunk to remove list: C_{chunkPos.x}_{chunkPos.y}_{chunkPos.z}"); //Logger.Log($@"{GameManager.time}: Added Chunk to remove list: C_{chunkPos.x}_{chunkPos.y}_{chunkPos.z}"); }
// 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())); } } }
// Create Mesh for Cube, given Chunk Internal Coords as int(x,y,z), Block 3D Array blocks, Block block, UVMaps as Vector2 Arrays, and Chunk Coords as int(PosX, PosY, PosZ) // Draw Grass Block public static MeshData DrawCube(int _x, int _y, int _z, Block[,,] _blocks, Block _block, Block.BlockFaceStyle _style, Vector2[] _uvMap0, Vector2[] _uvMap1, Vector2[] _uvMap2, Vector2[] _uvMap3, Vector2[] _uvMap4, Vector2[] _uvMap5, Int3 _chunkPos) { MeshData data = new MeshData(); Int3 pos = new Int3(_x, _y, _z); pos.ChunkInternalCoordsToWorldCoords(_chunkPos); // Bottom Face bool bottomVis = CheckVis(_x, _y, _z, pos, _blocks, Side.Bottom); bool topVis = CheckVis(_x, _y, _z, pos, _blocks, Side.Top); bool frontVis = CheckVis(_x, _y, _z, pos, _blocks, Side.Front); bool backVis = CheckVis(_x, _y, _z, pos, _blocks, Side.Back); bool leftVis = CheckVis(_x, _y, _z, pos, _blocks, Side.Left); bool rightVis = CheckVis(_x, _y, _z, pos, _blocks, Side.Right); if (bottomVis) { if (_style == Block.BlockFaceStyle.GrassStyle) { data.Merge(new MeshData( bottomFaceVerts, bottomFaceDrawOrder, new Vector2[] { _uvMap3[0], _uvMap3[1], _uvMap3[2], _uvMap3[3] })); } else if (_style == Block.BlockFaceStyle.LogStyle) { data.Merge(new MeshData( bottomFaceVerts, bottomFaceDrawOrder, new Vector2[] { _uvMap0[0], _uvMap0[1], _uvMap0[2], _uvMap0[3] })); } else if (_style == Block.BlockFaceStyle.UniversalStyle) { data.Merge(new MeshData( bottomFaceVerts, bottomFaceDrawOrder, new Vector2[] { _uvMap0[0], _uvMap0[1], _uvMap0[2], _uvMap0[3] })); } } // Top Face if (topVis) { if (_style == Block.BlockFaceStyle.GrassStyle) { data.Merge(new MeshData( topFaceVerts, topFaceDrawOrder, new Vector2[] { _uvMap0[0], _uvMap0[1], _uvMap0[2], _uvMap0[3] })); } else if (_style == Block.BlockFaceStyle.LogStyle) { data.Merge(new MeshData( topFaceVerts, topFaceDrawOrder, new Vector2[] { _uvMap0[0], _uvMap0[1], _uvMap0[2], _uvMap0[3] })); } else if (_style == Block.BlockFaceStyle.UniversalStyle) { data.Merge(new MeshData( topFaceVerts, topFaceDrawOrder, new Vector2[] { _uvMap0[0], _uvMap0[1], _uvMap0[2], _uvMap0[3] })); } } // Front Face if (frontVis) { if (_style == Block.BlockFaceStyle.GrassStyle) { if (topVis) { data.Merge(new MeshData( frontFaceVerts, frontFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else { data.Merge(new MeshData( frontFaceVerts, frontFaceDrawOrder, new Vector2[] { _uvMap3[2], _uvMap3[0], _uvMap3[3], _uvMap3[1] })); } } else if (_style == Block.BlockFaceStyle.LogStyle) { data.Merge(new MeshData( frontFaceVerts, frontFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else if (_style == Block.BlockFaceStyle.UniversalStyle) { data.Merge(new MeshData( frontFaceVerts, frontFaceDrawOrder, new Vector2[] { _uvMap0[2], _uvMap0[0], _uvMap0[3], _uvMap0[1] })); } } // Back Face if (backVis) { if (_style == Block.BlockFaceStyle.GrassStyle) { if (topVis) { data.Merge(new MeshData( backFaceVerts, backFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else { data.Merge(new MeshData( backFaceVerts, backFaceDrawOrder, new Vector2[] { _uvMap3[2], _uvMap3[0], _uvMap3[3], _uvMap3[1] })); } } else if (_style == Block.BlockFaceStyle.LogStyle) { data.Merge(new MeshData( backFaceVerts, backFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else if (_style == Block.BlockFaceStyle.UniversalStyle) { data.Merge(new MeshData( backFaceVerts, backFaceDrawOrder, new Vector2[] { _uvMap0[2], _uvMap0[0], _uvMap0[3], _uvMap0[1] })); } } // Left Face if (leftVis) { if (_style == Block.BlockFaceStyle.GrassStyle) { if (topVis) { data.Merge(new MeshData( leftFaceVerts, leftFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else { data.Merge(new MeshData( leftFaceVerts, leftFaceDrawOrder, new Vector2[] { _uvMap3[2], _uvMap3[0], _uvMap3[3], _uvMap3[1] })); } } else if (_style == Block.BlockFaceStyle.LogStyle) { data.Merge(new MeshData( leftFaceVerts, leftFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else if (_style == Block.BlockFaceStyle.UniversalStyle) { data.Merge(new MeshData( leftFaceVerts, leftFaceDrawOrder, new Vector2[] { _uvMap0[2], _uvMap0[0], _uvMap0[3], _uvMap0[1] })); } } // Right Face if (rightVis) { if (_style == Block.BlockFaceStyle.GrassStyle) { if (topVis) { data.Merge(new MeshData( rightFaceVerts, rightFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else { data.Merge(new MeshData( rightFaceVerts, rightFaceDrawOrder, new Vector2[] { _uvMap3[2], _uvMap3[0], _uvMap3[3], _uvMap3[1] })); } } else if (_style == Block.BlockFaceStyle.LogStyle) { data.Merge(new MeshData( rightFaceVerts, rightFaceDrawOrder, new Vector2[] { _uvMap1[2], _uvMap1[0], _uvMap1[3], _uvMap1[1] })); } else if (_style == Block.BlockFaceStyle.UniversalStyle) { data.Merge(new MeshData( rightFaceVerts, rightFaceDrawOrder, new Vector2[] { _uvMap0[2], _uvMap0[0], _uvMap0[3], _uvMap0[1] })); } } data.AddPos(new Vector3(_x - 0.5f, _y - 0.5f, _z - 0.5f)); return(data); }
// 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"); }
// Checks visibility of given face private static bool CheckVis(int _x, int _y, int _z, Int3 _position, Block[,,] _blocks, Side _side) { if (_side == Side.Bottom) { if (_y > 0) { Block block = _blocks[_x, _y - 1, _z]; return(block.IsTransparent || block.IsSemiTransparent); } else if (_y == 0) { Int3 pos = _position; pos.AddPos(0, -1, 0); Block block = World.Instance.GetBlockFromWorldCoords(pos); return(block.IsTransparent || block.IsSemiTransparent); } return(false); } else if (_side == Side.Top) { if (_y < Chunk.ChunkSize - 1) { Block block = _blocks[_x, _y + 1, _z]; return(block.IsTransparent || block.IsSemiTransparent); } else if (_y == Chunk.ChunkSize - 1) { Int3 pos = _position; pos.AddPos(0, 1, 0); Block block = World.Instance.GetBlockFromWorldCoords(pos); return(block.IsTransparent || block.IsSemiTransparent); } return(false); } else if (_side == Side.Front) { if (_x > 0) { Block block = _blocks[_x - 1, _y, _z]; return(block.IsTransparent || block.IsSemiTransparent); } else if (_x == 0) { Int3 pos = _position; pos.AddPos(-1, 0, 0); Block block = World.Instance.GetBlockFromWorldCoords(pos); return(block.IsTransparent || block.IsSemiTransparent); } return(false); } else if (_side == Side.Back) { if (_x < Chunk.ChunkSize - 1) { Block block = _blocks[_x + 1, _y, _z]; return(block.IsTransparent || block.IsSemiTransparent); } else if (_x == Chunk.ChunkSize - 1) { Int3 pos = _position; pos.AddPos(1, 0, 0); Block block = World.Instance.GetBlockFromWorldCoords(pos); return(block.IsTransparent || block.IsSemiTransparent); } return(false); } else if (_side == Side.Left) { if (_z > 0) { Block block = _blocks[_x, _y, _z - 1]; return(block.IsTransparent || block.IsSemiTransparent); } else if (_z == 0) { Int3 pos = _position; pos.AddPos(0, 0, -1); Block block = World.Instance.GetBlockFromWorldCoords(pos); return(block.IsTransparent || block.IsSemiTransparent); } return(false); } else // Side.Right { if (_z < Chunk.ChunkSize - 1) { Block block = _blocks[_x, _y, _z + 1]; return(block.IsTransparent || block.IsSemiTransparent); } else if (_z == Chunk.ChunkSize - 1) { Int3 pos = _position; pos.AddPos(0, 0, 1); Block block = World.Instance.GetBlockFromWorldCoords(pos); return(block.IsTransparent || block.IsSemiTransparent); } return(false); } }