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;
}
}
}