從零開始做一個SLG遊戲(一):六邊形網格

遊資網發表於2019-10-08
從零開始做一個SLG遊戲(一):六邊形網格

本文的主要工作是六邊形網格的繪製。

從零開始做一個SLG遊戲(一):六邊形網格

如圖所示。六邊形有6個方向,6個頂點,同時定義中心點到邊的最短距離為內徑innerRadius,定義中心點到頂點的距離為外徑outerRadius。

六邊形可以拆分為6個等邊三角形,所以很容易得出:

從零開始做一個SLG遊戲(一):六邊形網格

定義遊戲中六邊形的邊長(即外徑)為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中新建一個空的物體,掛上這個指令碼,然後隨便扔一個材質上去,執行就會出現如下圖片:

從零開始做一個SLG遊戲(一):六邊形網格

接下來就是構建地圖網格:

從零開始做一個SLG遊戲(一):六邊形網格

如圖,仔細觀察我們就可以我們可以發現:

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);
複製程式碼

一個六邊形的網格地圖的雛形就做好了,執行一下,我們就可以得到下面圖片:

從零開始做一個SLG遊戲(一):六邊形網格

參考文章:

Hex Map 1

當初也是因為這篇文章,才萌生了做六邊形SLG遊戲的念頭的

作者:觀復
專欄地址:https://zhuanlan.zhihu.com/p/44306412

相關文章