从零开始做一个SLG游戏(一):六边形网格

本文的主要工作是六边形网格的绘制。
 


如图所示。六边形有6个方向,6个顶点,同时定义中心点到边的最短距离为内径innerRadius,定义中心点到顶点的距离为外径outerRadius。

六边形可以拆分为6个等边三角形,所以很容易得出:
 


定义游戏中六边形的边长(即外径)为10.

手游账号买号用一个静态类以及一个枚举将这些定义下来:

  1. using UnityEngine;
  2. public static class HexMetrics
  3. {
  4.         /// <summary>
  5.         /// 外径,中心到顶点距离
  6.         /// </summary>
  7.         public const float outerRadius = 10f;
  8.         /// <summary>
  9.         /// 内径,中心到边距离,0.866025404为二分之根号三的近似值
  10.         /// </summary>
  11.         public const float innerRadius = outerRadius * 0.866025404f;
  12.         
  13.         /// <summary>
  14.         /// 六边形的六个顶点坐标
  15.         /// </summary>
  16.         public static readonly Vector3[] corners = {
  17.                 new Vector3(0f, 0f, outerRadius),
  18.                 new Vector3(innerRadius,0f,0.5f*outerRadius),
  19.                 new Vector3(innerRadius,0f,-0.5f*outerRadius),
  20.                 new Vector3(0f,0f,-outerRadius),
  21.                 new Vector3(-innerRadius,0f,-0.5f*outerRadius),
  22.                 new Vector3(-innerRadius,0f,0.5f*outerRadius)
  23.         };
  24. }
  25. /// <summary>
  26. /// 六边形的方向
  27. ///                NW /\ NE
  28. ///                W |  |E
  29. ///                SW \/ SE
  30. /// </summary>
  31. public enum HexDirection
  32. {
  33.         NE,
  34.         E,
  35.         SE,
  36.         SW,
  37.         W,
  38.         NW,
  39. }

复制代码


之后开始写关于图片绘制的代码:

将六边形分解为6个等边三角形,三角形的三个顶点分别为六边形中心点,以及六边形一条边的两个顶点:

即HexMetrics.corners和HexMetrics.corners[i+1]

因为第六条边的时候,i=5,i+1=6,corners[6]不存在,所以在定义里加一个coners[6],和coners[0]相等:

 

  1. public static readonly Vector3[] corners = {
  2.                 new Vector3(0f, 0f, outerRadius),
  3.                 new Vector3(innerRadius,0f,0.5f*outerRadius),
  4.                 new Vector3(innerRadius,0f,-0.5f*outerRadius),
  5.                 new Vector3(0f,0f,-outerRadius),
  6.                 new Vector3(-innerRadius,0f,-0.5f*outerRadius),
  7.                 new Vector3(-innerRadius,0f,0.5f*outerRadius),
  8.                 new Vector3(0f,0f,outerRadius),
  9.         };

复制代码


所以绘制三角形的时候,代码可以写成这样:

        

  1. /// <summary>
  2.         /// 绘制地形
  3.         /// </summary>
  4.         public void Draw()
  5.         {
  6.                 Clear();
  7.                 Vector3 center = Vector3.zero;
  8.                 for (HexDirection dir = HexDirection.NE; dir <= HexDirection.NW; dir++)
  9.                 {
  10.                         AddTriangle(center,
  11.                                 HexMetrics.corners[(int)dir],
  12.                                 HexMetrics.corners[(int)dir + 1]);
  13.                 }
  14.                 UpdateMesh();
  15.         }

复制代码


然后贴上全部代码:
 

  1. [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider))]
  2. public class HexCell : MonoBehaviour {
  3.         private Mesh mesh;
  4.         private List<Vector3> vertices;
  5.         private List<int> triangles;
  6.         private void Awake()
  7.         {
  8.                 GetComponent<MeshFilter>().mesh = mesh = GetComponent<MeshCollider>().sharedMesh = new Mesh();
  9.                 GetComponent<MeshCollider>().convex = true;
  10.                 mesh.name = "Hex Cell";
  11.                 vertices = new List<Vector3>();
  12.                 triangles = new List<int>();
  13.                 Draw();
  14.         }
  15.         // Use this for initialization
  16.         void Start () {
  17.                
  18.         }
  19.         
  20.         // Update is called once per frame
  21.         void Update () {
  22.                
  23.         }
  24.         /// <summary>
  25.         /// 绘制地形
  26.         /// </summary>
  27.         public void Draw()
  28.         {
  29.                 Clear();
  30.                 Vector3 center = Vector3.zero;
  31.                 for (HexDirection dir = HexDirection.NE; dir <= HexDirection.NW; dir++)
  32.                 {
  33.                         AddTriangle(center,
  34.                                 HexMetrics.corners[(int)dir],
  35.                                 HexMetrics.corners[(int)dir + 1]);
  36.                 }
  37.                 UpdateMesh();
  38.         }
  39.         /// <summary>
  40.         /// 清空mesh数据
  41.         /// </summary>
  42.         private void Clear()
  43.         {
  44.                 mesh.Clear();
  45.                 vertices.Clear();
  46.                 triangles.Clear();
  47.         }
  48.         /// <summary>
  49.         /// 绘制mesh数据
  50.         /// </summary>
  51.         private void UpdateMesh()
  52.         {
  53.                 mesh.vertices = vertices.ToArray();
  54.                 mesh.triangles = triangles.ToArray();
  55.                 mesh.RecalculateNormals();
  56.                 mesh.RecalculateBounds();
  57.         }
  58.         /// <summary>
  59.         /// 添加三角形。
  60.         /// </summary>
  61.         /// <param name="v1"></param>
  62.         /// <param name="v2"></param>
  63.         /// <param name="v3"></param>
  64.         private void AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3)
  65.         {
  66.                 int count = triangles.Count;
  67.                 vertices.Add(v1);
  68.                 triangles.Add(count++);
  69.                 vertices.Add(v2);
  70.                 triangles.Add(count++);
  71.                 vertices.Add(v3);
  72.                 triangles.Add(count++);
  73.         }
  74. }

复制代码


在unity中新建一个空的物体,挂上这个脚本,然后随便扔一个材质上去,运行就会出现如下图片:
 


接下来就是构建地图网格:
 


如图,仔细观察我们就可以我们可以发现:

1.同一行的相邻六边形的间距为2个内径的距离

2.两列六边形,在纵坐标上相差1.5个外径的距离

3.列数为奇数的六边形在横坐标上,会向右偏移1个内径的距离。

所以我们可以通过如下方式将队列上的坐标(x,y)转换为空间上的坐标:

 

  1. private Vector3 GetPos(int x, int y)
  2.         {
  3.                 float posX = x;
  4.                 float posY = y;
  5.                 if ((y & 1) != 0)
  6.                 {
  7.                         posX += 0.5f;
  8.                 }
  9.                 Vector3 pos = new Vector3(2f * posX * HexMetrics.innerRadius, 0f, 1.5f * posY * HexMetrics.outerRadius);
  10.                 return pos;
  11.         }

复制代码


        
为了便于区分,再将网格的颜色变一下:

 

  1. cell.GetComponent<MeshRenderer>().material.color = new Color(posX / width, (posX + posY) / (height + width), posY / height);

复制代码


一个六边形的网格地图的雏形就做好了,运行一下,我们就可以得到下面图片:
 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值