using System; using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; using UnityEngine; namespace Terrain { public class Chunk { public readonly Vector2 Position; public readonly World World; private readonly Dictionary _blocks = new Dictionary(); private readonly Dictionary _obsticals = new Dictionary(); private readonly GameObject _object; private readonly MeshFilter _filter; private readonly Bounds _chunkWorldBounds; private readonly Bounds _chunkViewBounds; private readonly GameObject _subObject; private readonly MeshFilter _subFilter; public bool Visible; public Chunk(Vector2 position, World world) { Position = position; World = world; _object = new GameObject(); _object.SetActive(false); _filter = _object.AddComponent(); MeshRenderer renderer = _object.AddComponent(); renderer.material = world.GetTerrainTextureManager().Material; renderer.sortingLayerName = "Terrain"; _object.transform.SetParent(world.transform); _object.transform.position = new Vector3(position.x * World.ChunkSize, position.y * World.ChunkSize); _object.name = $"Chunk [{position.x}:{position.y}]"; _chunkWorldBounds = new Bounds(_object.transform.position + new Vector3(5, 5), new Vector3(10, 10)); _chunkViewBounds = new Bounds(_chunkWorldBounds.center, _chunkWorldBounds.size + new Vector3(5, 5)); _subObject = new GameObject(); _subObject.SetActive(false); _subObject.transform.SetParent(_object.transform); _subObject.transform.position = _object.transform.position; _subObject.name = "Animatable Blocks"; _subFilter = _subObject.AddComponent(); MeshRenderer subRenderer = _subObject.AddComponent(); subRenderer.material = world.GetTerrainTextureManager().Material; subRenderer.sortingLayerName = "Terrain"; GenerateChunk(); } private void GenerateChunk() { Mesh mesh = new Mesh(); int vertexIndex = 0; List vertices = new List(); List triangles = new List(); List uvs = new List(); Mesh aMesh = new Mesh(); int aVertexIndex = 0; List aVertices = new List(); List aTriangles = new List(); List aUVs = new List(); for (int x = 0; x < World.ChunkSize; x++) { for (int y = World.ChunkSize; y > 0; y--) { Vector3 pos = new Vector2(x, y); Block block = GenerateBlock(pos + _object.transform.position); RectangleF texture = block.GetTile(World.GetTerrainTextureManager()); if (block.Animatable) { aVertices.Add(pos); aVertices.Add(pos + new Vector3(0, 1)); aVertices.Add(pos + new Vector3(1, 0)); aVertices.Add(pos + new Vector3(1, 1)); aUVs.Add(new Vector2(texture.X, texture.Y)); aUVs.Add(new Vector2(texture.X, texture.Y + texture.Height)); aUVs.Add(new Vector2(texture.X + texture.Width, texture.Y)); aUVs.Add(new Vector2(texture.X + texture.Width, texture.Y + texture.Height)); aTriangles.Add(aVertexIndex); aTriangles.Add(aVertexIndex + 1); aTriangles.Add(aVertexIndex + 2); aTriangles.Add(aVertexIndex + 2); aTriangles.Add(aVertexIndex + 1); aTriangles.Add(aVertexIndex + 3); aVertexIndex += 4; } else { vertices.Add(pos); vertices.Add(pos + new Vector3(0, 1)); vertices.Add(pos + new Vector3(1, 0)); vertices.Add(pos + new Vector3(1, 1)); uvs.Add(new Vector2(texture.X, texture.Y)); uvs.Add(new Vector2(texture.X, texture.Y + texture.Height)); uvs.Add(new Vector2(texture.X + texture.Width, texture.Y)); uvs.Add(new Vector2(texture.X + texture.Width, texture.Y + texture.Height)); triangles.Add(vertexIndex); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 2); triangles.Add(vertexIndex + 2); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 3); vertexIndex += 4; } } } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.uv = uvs.ToArray(); mesh.RecalculateNormals(); _filter.mesh = mesh; aMesh.vertices = aVertices.ToArray(); aMesh.triangles = aTriangles.ToArray(); aMesh.uv = aUVs.ToArray(); aMesh.RecalculateNormals(); _subFilter.mesh = aMesh; } private Block GenerateBlock(Vector2 position) { int blockId = World.GenerateBlock(position, this); _blocks.Add(position, blockId); Block block = Block.GetBlock(blockId); return block; } public void Update() { SetVisible(World.player.CanSee(_chunkViewBounds)); } public void FixedUpdate() { } public void SetVisible(bool value) { if (value) { _object.SetActive(true); _subObject.SetActive(true); foreach (var obs in _obsticals.Values) obs.SetActive(true); Visible = true; } else { Visible = false; _object.SetActive(false); _subObject.SetActive(false); foreach (var obs in _obsticals.Values) obs.SetActive(false); } } public void OnAnimationFrameChange(int frame) { if (!Visible) return; Mesh mesh = new Mesh(); int vertexIndex = 0; List vertices = new List(); List triangles = new List(); List uvs = new List(); for (int x = 0; x < World.ChunkSize; x++) { for (int y = World.ChunkSize; y > 0; y--) { Vector3 pos = new Vector2(x, y); Block block = Block.GetBlock(_blocks[pos + _object.transform.position]); if (!block.Animatable) continue; vertices.Add(pos); vertices.Add(pos + new Vector3(0, 1)); vertices.Add(pos + new Vector3(1, 0)); vertices.Add(pos + new Vector3(1, 1)); RectangleF texture = block.GetTile(World.GetTerrainTextureManager(), frame % block.Tiles.Length); uvs.Add(new Vector2(texture.X, texture.Y)); uvs.Add(new Vector2(texture.X, texture.Y + texture.Height)); uvs.Add(new Vector2(texture.X + texture.Width, texture.Y)); uvs.Add(new Vector2(texture.X + texture.Width, texture.Y + texture.Height)); triangles.Add(vertexIndex); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 2); triangles.Add(vertexIndex + 2); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 3); vertexIndex += 4; } } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.uv = uvs.ToArray(); mesh.RecalculateNormals(); _subFilter.mesh = mesh; } public Block GetBlock(Vector2 pos) { try { return Block.GetBlock(_blocks[pos]); } catch (Exception) { return null; } } public Obstical GetObstical(Vector2 pos) { try { return _obsticals[pos]; } catch (Exception) { return null; } } public GameObject GetObject() { return _object; } public void AddObstical(Obstical obstical) { _obsticals.Add(obstical.Position, obstical); } public void RemoveObstical(Vector2 pos) { Obstical obstical = _obsticals[pos]; MonoBehaviour.Destroy(obstical.GameObject); _obsticals.Remove(pos); } public override string ToString() => _object.name; } }