using System.Collections.Generic; using UnityEngine; namespace Assets.Scripts { public class World : MonoBehaviour { public int seed; public Transform player; public Vector3 spawn; public BiomeAttributes biome; public Material material; public BlockType[] blockTypes; private readonly Chunk[,] _chunks = new Chunk[VoxelData.WorldSizeInChunks, VoxelData.WorldSizeInChunks]; private readonly List _activeChunks = new List(); private ChunkCoord _playersLastChunk; private void Start() { Random.InitState(seed); spawn = new Vector3(VoxelData.WorldSizeInBlocks / 2f, VoxelData.ChunkHeight / 2f, VoxelData.WorldSizeInBlocks / 2f); GenerateWorld(); _playersLastChunk = GetChunkCoord(player.position); } private void Update() { ChunkCoord playerChunk = GetChunkCoord(player.position); if (!playerChunk.Equals(_playersLastChunk)) { _playersLastChunk = playerChunk; CheckViewDistance(); } } private void GenerateWorld() { for (int x = VoxelData.WorldSizeInChunks / 2 - VoxelData.ViewDistance; x < VoxelData.WorldSizeInChunks / 2 + VoxelData.ViewDistance; x++) { for (int z = VoxelData.WorldSizeInChunks / 2 - VoxelData.ViewDistance; z < VoxelData.WorldSizeInChunks / 2 + VoxelData.ViewDistance; z++) { CreateNewChunk(x, z); } } player.position = spawn; } public ChunkCoord GetChunkCoord(Vector3 absPosition) { int x = Mathf.FloorToInt(absPosition.x / VoxelData.ChunkWidth); int z = Mathf.FloorToInt(absPosition.z / VoxelData.ChunkWidth); return new ChunkCoord(x, z); } private void CheckViewDistance() { ChunkCoord coord = GetChunkCoord(player.position); ChunkCoord[] previouslyActiveChunks = _activeChunks.ToArray(); _activeChunks.Clear(); for (int x = coord.x - VoxelData.ViewDistance; x < coord.x + VoxelData.ViewDistance; x++) { for (int z = coord.z - VoxelData.ViewDistance; z < coord.z + VoxelData.ViewDistance; z++) { if (!IsChunkInWorld(new ChunkCoord(x, z))) continue; Chunk chunk = _chunks[x, z]; if (chunk == null) CreateNewChunk(x, z); else if (!chunk.IsActive) { chunk.IsActive = true; } _activeChunks.Add(new ChunkCoord(x, z)); } } foreach (ChunkCoord chunk in previouslyActiveChunks) { if (!_activeChunks.Contains(chunk)) _chunks[chunk.x, chunk.z].IsActive = false; } } private void CreateNewChunk(int x, int z) { _chunks[x, z] = new Chunk(new ChunkCoord(x, z), this); _activeChunks.Add(new ChunkCoord(x, z)); } public bool IsChunkInWorld(ChunkCoord coord) { return (coord.x > 0 && coord.x < VoxelData.WorldSizeInChunks - 1 && coord.z > 0 && coord.z < VoxelData.WorldSizeInChunks - 1); } public bool IsBlockInWorld(Vector3 pos) { return (pos.x >= 0 && pos.x < VoxelData.WorldSizeInBlocks && pos.y >= 0 && pos.y < VoxelData.ChunkHeight && pos.z >= 0 && pos.z < VoxelData.WorldSizeInBlocks); } public byte GenerateBlock(Vector3 position) { Vector3Int pos = new Vector3Int(Mathf.FloorToInt(position.x), Mathf.FloorToInt(position.y), Mathf.FloorToInt(position.z)); if (!IsBlockInWorld(position)) return 0; if (pos.y == 0) return 1; //Generate Block based on 2D Perlin Noise int terrainHeight = Mathf.FloorToInt(Noise.Get2DNoise(new Vector2(pos.x, pos.z), 0, biome.terrainScale) * biome.terrainHeight + biome.solidGroundHeight); int blockValue = 0; if (pos.y < terrainHeight) blockValue = 2; if (pos.y < terrainHeight && pos.y > terrainHeight - 4) blockValue = 5; if (pos.y == terrainHeight) blockValue = 3; if (blockValue == 2) { foreach (var lode in biome.lodes) { if ((pos.y > lode.minHeight && pos.y < lode.maxHeight) && Noise.Get3DNoise(pos, lode.offset, lode.scale, lode.threshold)) blockValue = lode.blockID; } } return (byte)blockValue; } } [System.Serializable] public class BlockType { public string blockName; public bool isSolid; [Header("Texture Values")] public int backFaceTexture; public int frontFaceTexture; public int topFaceTexture; public int bottomFaceTexture; public int leftFaceTexture; public int rightFaceTexture; //Back - Front - Top - Bottom - Left - Right public int GetTextureID(int faceIndex) { switch (faceIndex) { case 0: return backFaceTexture; case 1: return frontFaceTexture; case 2: return topFaceTexture; case 3: return bottomFaceTexture; case 4: return leftFaceTexture; case 5: return rightFaceTexture; default: return 0; } } } }