实现了六边形网格的向量工具,
Hex为斜角坐标系,三个轴平面夹角为120度
NetHex为offset型坐标系,xy轴夹角90度
AxialHex为轴坐标系p,r夹角120度,支持互相转换
Hex坐标系方便制作技能范围,寻路等功能
AxialHex坐标系一般用于辅助计算
NetHex坐标系方便存储二维数组数据
同时支持坐标类型生成二维坐标(x,0,z)。后续会加入形状,距离等判断。用于制作复杂技能区域的游戏,并方便进行寻路计算。
图为Hex坐标系
图为几种不同的NetHex类型
具体数学原理参考redblobgames
下面是代码。
Hex.cs
提供基本数据格式
using System;
using System.Collections.Generic;
using UnityEngine;
namespace GridTool.Hex
{
public struct Hex
{
public int x;
public int y;
public int z;
public Hex(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
public int this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
default:
throw new IndexOutOfRangeException("Invalid Hex index!");
}
}
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default:
throw new IndexOutOfRangeException("Invalid HexTransform index!");
}
}
}
//========== 快速构造 ==========
/// <summary>
/// 原点
/// </summary>
public static Hex zero
{
get
{
return new Hex(0, 0, 0);
}
}
/// <summary>
/// 左上
/// </summary>
public static Hex LTop
{
get
{
return new Hex(0, 1, -1);
}
}
/// <summary>
/// 右上
/// </summary>
public static Hex RTop
{
get
{
return new Hex(1, 0, -1);
}
}
public static Hex Left
{
get
{
return new Hex(-1, 1, 0);
}
}
public static Hex Right
{
get
{
return new Hex(1, -1, 0);
}
}
public static Hex LBottom
{
get
{
return new Hex(-1, 0, 1);
}
}
public static Hex RBottom
{
get
{
return new Hex(0, -1, 1);
}
}
public static Hex[] Directions
{
get
{
return new Hex[6] {
Hex.LTop,
Hex.RTop,
Hex.Right,
Hex.RBottom,
Hex.LBottom,
Hex.Left
};
}
}
//========== 数组操作 =========
public static Hex[] operator +(Hex[] a, Hex b)
{
Hex[] c = new Hex[a.Length];
for (int i = 0; i < a.Length; i++)
{
c[i] = a[i] + b;
}
return c;
}
public static Hex[] operator -(Hex[] a, Hex b)
{
Hex[] c = new Hex[a.Length];
for (int i = 0; i < a.Length; i++)
{
c[i] = a[i] - b;
}
return c;
}
//========== 运算符 =========
public static Hex operator +(Hex a, Hex b)
{
return new Hex(a.x + b.x, a.y + b.y, a.z + b.z);
}
public static Hex operator -(Hex a, Hex b)
{
return new Hex(a.x - b.x, a.y - b.y, a.z - b.z);
}
public static Hex operator -(Hex a)
{
return new Hex(-a.x, -a.y, -a.z);
}
public static Hex operator *(Hex a, int d)
{
return new Hex(a.x * d, a.y * d, a.z * d);
}
public static Hex operator *(int d, Hex a)
{
return new Hex(a.x * d, a.y * d, a.z * d);
}
public static Hex operator /(Hex a, int d)
{
return new Hex(a.x / d, a.y / d, a.z / d);
}
public static bool operator ==(Hex lhs, Hex rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}
public static bool operator !=(Hex lhs, Hex rhs)
{
return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z;
}
//========== 工具 =========
public static int Distance(Hex from, Hex to)
{
return (int)Mathf.Max(Mathf.Abs(from.x - to.x), Mathf.Abs(from.y - to.y), Mathf.Abs(from.z - to.z));
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", x, y, z);
}
public string ToString(string format)
{
return String.Format("({0}, {1}, {2})", x.ToString(format), y.ToString(format), z.ToString(format));
}
public void Fix()
{
y = -x - z;
}
}
public struct AxialHex
{
public int q;
public int r;
public AxialHex(int q, int r)
{
this.q = q;
this.r = r;
}
public static AxialHex RoundAxial(float q, float r)
{
return HexTools.RoundHex(q, -q - r, r).ToAxial();
}
}
public enum HexType
{
Odd_r,// 行对齐 偶数行偏左
Even_r,// 行对齐 偶数行偏右
Odd_q,// 列对齐 0
Even_q,//
NULL//
}
public struct NetHex
{
public int col;
public int row;
public NetHex(int col, int row)
{
this.col = col;
this.row = row;
}
public override string ToString()
{
return String.Format("({0}, {1})", col, row);
}
}
}
工具类
处理各种坐标系之间的转换关系等。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GridTool.Hex
{
public static class HexTools
{
public static Hex ToHex(this AxialHex ax)
{
return new Hex(ax.q, -ax.q - ax.r, ax.r);
}
public static AxialHex ToAxial(this Hex hex)
{
return new AxialHex(hex.x, hex.z);
}
public static Hex ToHex(this NetHex grid, HexType type)
{
Hex hex = new Hex(0, 0, 0);
int col = grid.col;
int row = grid.row;
switch (type)
{
case HexType.Odd_q:
{
hex.x = col;
hex.z = row - (col - (col & 1)) / 2;
hex.y = -hex.x - hex.z;
}
break;
case HexType.Even_q:
{
hex.x = col;
hex.z = row - (col + (col & 1)) / 2;
hex.y = -hex.x - hex.z;
}
break;
case HexType.Odd_r:
{
hex.x = col - (row - (row & 1)) / 2;
hex.z = row;
hex.y = -hex.x - hex.z;
}
break;
case HexType.Even_r:
{
hex.x = col - (row + (row & 1)) / 2;
hex.z = row;
hex.y = -hex.x - hex.z;
}
break;
default:
break;
}
return hex;
}
public static NetHex ToNet(this Hex hex, HexType type)
{
int col;
int row;
switch (type)
{
case HexType.Odd_q:
{
col = hex.x;
row = hex.z + (hex.x - (hex.x & 1)) / 2;
}
break;
case HexType.Even_q:
{
col = hex.x;
row = hex.z + (hex.x + (hex.x & 1)) / 2;
}
break;
case HexType.Odd_r:
{
col = hex.x + (hex.z - (hex.z & 1)) / 2;
row = hex.z;
}
break;
case HexType.Even_r:
{
col = hex.x + (hex.z + (hex.z & 1)) / 2;
row = hex.z;
}
break;
default:
{
col = 0;
row = 0;
}
break;
}
return new NetHex(col, row);
}
//类型是否尖角朝上
public static bool IsPointTop(this HexType type)
{
return type == HexType.Even_r || type == HexType.Odd_r;
}
//网格中心相对0点便宜位置,y轴为0。
public static Vector3 ToPixel(this Hex hex, float size, HexType type)
{
AxialHex ax = hex.ToAxial();
if (IsPointTop(type))
return new Vector3(
size * 1.73205080756887f * (ax.q + ax.r * .5f),
0,
size * 1.5f * ax.r);
else
return new Vector3(size * 1.5f * ax.q,
0,
size * 1.73205080756887f * (ax.r + ax.q * .5f));
}
public static Hex ToHex(this Vector3 pixel, float size, HexType type)
{
float q = 0;
float r = 0;
if (IsPointTop(type))
{
q = ((pixel.x * 1.73205080756887f - pixel.z) / (size * 3));
r = (pixel.z * 0.666666666666f / size);
}
else
{
q = (pixel.x * 0.666666666666f / size);
r = ((pixel.z * 1.73205080756887f - pixel.x) / (size * 3));
}
AxialHex ax = AxialHex.RoundAxial(q, r);
return ax.ToHex();
}
public static Hex RoundHex(float x, float y, float z)
{
float rx = Mathf.Round(x);
float ry = Mathf.Round(y);
float rz = Mathf.Round(z);
float x_diff = Mathf.Abs(rx - x);
float y_diff = Mathf.Abs(ry - y);
float z_diff = Mathf.Abs(rz - z);
if (x_diff > y_diff && x_diff > z_diff)
rx = -ry - rz;
else if (y_diff > z_diff)
ry = -rx - rz;
else
rz = -rx - ry;
return new Hex((int)rx, (int)ry, (int)rz);
}
/// <summary>
/// 获取相邻坐标组,左上顺时针旋转
/// </summary>
/// <param name="hex">目标坐标</param>
/// <returns>坐标组</returns>
public static Hex[] GetNeighbor(this Hex hex)
{
return Hex.Directions + hex;
}
/// <summary>
/// 获取经过两点之间的坐标组
/// </summary>
/// <param name="from">开始坐标</param>
/// <param name="target">结束坐标</param>
/// <returns>坐标组</returns>
public static Hex[] LineTo(this Hex from, Hex target)
{
int N = Hex.Distance(from, target);
Hex[] results = new Hex[N];
for (int i = 0; i <= N; i++)
{
float t = (float) i / (float) N;
results[i] = HexTools.RoundHex(Mathf.Lerp(from.x,target.x,t), Mathf.Lerp(from.y,target.y,t), Mathf.Lerp(from.z,target.z,t));
}
return results;
}
/// <summary>
/// 获取中心点范围内的所有坐标
/// </summary>
/// <param name="center">中心点</param>
/// <param name="radius">范围</param>
/// <returns>坐标组</returns>
public static Hex[] Range(this Hex center, int radius)
{
List<Hex> results = new List<Hex>();
for (int dx = -radius; dx <= radius; dx++)
{
for (int dy = Math.Max(-radius, -dx-radius); dy <= Math.Min(radius, -dx+radius); dy++)
{
int dz = -dx - dy;
results.Add(center + new Hex(dx,dy,dz));
}
}
return results.ToArray();
}
/// <summary>
/// 获取以center为圆心的环形区域,半径为Radius
/// </summary>
/// <param name="center">中心位置</param>
/// <param name="radius">环半径(不为0)</param>
/// <returns>环形包含的格子</returns>
public static Hex[] Ring(this Hex center, int radius)
{
List<Hex> results = new List<Hex>();
Hex tmp = center + Hex.Directions[4] * radius;
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < radius; j++)
{
results.Add(tmp);
tmp = tmp.GetNeighbor()[i];
}
}
return results.ToArray();
}
}
}