using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; namespace HexTiles { /// /// Chunk of several hex tiles. /// [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider))] [AddComponentMenu("Ellyality/Visual/Hex/Chunk")] public class HexChunk : MonoBehaviour { /// /// Upper bounds of the range of tiles contained in this chunk. /// [HideInInspector] public HexCoords upperBounds; /// /// Lower bounds of the range of tiles contained in this chunk. /// [HideInInspector] public HexCoords lowerBounds; /// /// Width of each individual hex tile. /// [SerializeField, HideInInspector] private float tileDiameter = 1f; /// /// Width of each individual hex tile. /// public float TileDiameter { get { return tileDiameter; } set { tileDiameter = value; } } /// /// Accessor for tile material for convenience. /// public Material Material { get { return GetComponent().sharedMaterial; } set { GetComponent().sharedMaterial = value; } } /// /// The mesh of this chunk. /// public MeshFilter MeshFilter { get { if (meshFilter == null) { meshFilter = GetComponent(); } return meshFilter; } } /// /// The mesh of this chunk. /// public MeshCollider MeshCollider { get { if (meshCollider == null) { meshCollider = GetComponent(); } return meshCollider; } } private MeshFilter meshFilter; private MeshCollider meshCollider; [SerializeField, HideInInspector] private List sidePieces = new List(); [SerializeField, HideInInspector] private List tiles = new List(); public IList Tiles { get { return tiles; } } /// /// Add a tile to this chunk. /// internal void AddTile(HexPosition position) { tiles.Add(position); Dirty = true; } /// /// Remove a tile from this chunk. /// internal void RemoveTile(HexCoords coords) { tiles.RemoveAll(tile => tile.Coordinates.Equals(coords)); Dirty = true; } /// /// Whether or not we need to re-generate the mesh for this tile. /// public bool Dirty { get; private set; } /// /// Create the mesh used to render the hex. /// public void GenerateMesh() { // Initial setup for game object transform.localScale = Vector3.one; transform.localPosition = Vector3.zero; transform.localRotation = Quaternion.identity; gameObject.isStatic = true; var mesh = MeshFilter.mesh = new Mesh(); mesh.name = "Chunk"; var vertices = new List(); var triangles = new List(); // UV coordinates for tops of hex tiles. var uv = new List(); foreach (var tile in tiles) { var startingTriIndex = vertices.Count; var data = HexMeshGenerator.GenerateHexMesh( tile.Coordinates, TileDiameter, sidePieces .Where(sideInfo => sideInfo.hex == tile.Coordinates) .Select(sideInfo => sideInfo.side) ); // Transform to correct position vertices.AddRange(data.verts.Select(vert => vert + tile.GetPositionVector(TileDiameter))); // Add to get correct indices triangles.AddRange(data.tris.Select(index => index + startingTriIndex)); uv.AddRange(data.uvs); } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.uv = uv.ToArray(); mesh.RecalculateNormals(); MeshCollider.sharedMesh = mesh; Dirty = false; } /// /// Generates and adds a side piece to the tile. /// internal void AddSidePiece(HexCoords tile, HexCoords side, float height) { var sideIndex = Array.IndexOf(HexMetrics.AdjacentHexes, side); if (sideIndex < 0) { throw new ApplicationException("Hex tile " + side + " is not a valid adjacent tile."); } sidePieces.Add(new SidePieceInfo { side = new SidePiece { direction = sideIndex, elevationDelta = height }, hex = tile }); Dirty = true; } /// /// Remove the specified side piece from this hex tile. /// internal void TryRemovingSidePiece(HexCoords tile, HexCoords side) { var sideIndex = Array.IndexOf(HexMetrics.AdjacentHexes, side); if (sideIndex < 0) { throw new ApplicationException("Hex tile " + side + " is not a valid adjacent tile."); } var sidePiecesToRemove = sidePieces .Where(sidePieceInfo => sidePieceInfo.hex == tile) .Where(sidePieceInfo => sidePieceInfo.side.direction == sideIndex) .ToArray(); foreach (var sidePiece in sidePiecesToRemove) { sidePieces.Remove(sidePiece); } Dirty = true; } /// /// Clear all side pieces. Note that this won't affect the actual mesh until GenerateMesh() is called. /// internal void ResetSidePieces() { sidePieces.Clear(); } [Serializable] private struct SidePieceInfo { public SidePiece side; public HexCoords hex; } } }