4-11 封装顶点计算方法

  在上一章中,我们完成了地图单元与矩形连接区域的的连接。在我们新增代码的时候发现,计算六边形一条边上新增顶点的这部分代码,是可以单独抽象出来,组成一个新的方法,方便在之后计算顶点时调用。
  我们创建一个新的脚本EdgeVertices.cs,其中是一个结构体,包含了4个顺时针排列在六边形一条边上的顶点。代码如下:

EdgeVertices.cs
1
2
3
4
5
6
7
using UnityEngine;

public struct EdgeVertices
{
//4个顺时针排列在六边形一条边上的顶点
public Vector3 v1, v2, v3, v4;
}

  接着再为这个结构体添加一个构造函数,用来计算每个顶点的位置信息。代码如下:

EdgeVertices.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
public struct EdgeVertices
{


//通过六边形一条边上,边缘的两个顶点,计算出中间的两个点的位置
public EdgeVertices(Vector3 corner1, Vector3 corner2)
{
v1 = corner1;
v2 = Vector3.Lerp(corner1, corner2, 1f / 3f);
v3 = Vector3.Lerp(corner1, corner2, 2f / 3f);
v4 = corner2;
}
}

  回到HexMesh.cs脚本中,我们创建一个方法,通过4个顶点位置,对六边形内每个三角面片进行细分。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class HexMesh : MonoBehaviour
{


/// <summary>
/// 对cell的六边形其中一个三角面片进行细分,细分为3个三角面片
/// </summary>
/// <param name="center">cell中心点位置</param>
/// <param name="edge">一条边上细分后的4个顶点信息</param>
/// <param name="color">cell的颜色</param>
private void TriangulateEdgeFan(Vector3 center, EdgeVertices edge, Color color)
{
AddTriangle(center, edge.v1, edge.v2);
AddTriangleColor(color);
AddTriangle(center, edge.v2, edge.v3);
AddTriangleColor(color);
AddTriangle(center, edge.v3, edge.v4);
AddTriangleColor(color);
}
}

  再创建一个方法,用来创建两个地图单元之间,经过细分后的连接区域。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HexMesh : MonoBehaviour
{


/// <summary>
/// 创建2个cell之间细分后的的连接区域
/// </summary>
/// <param name="e1">第一个cell一条边上的4个顶点</param>
/// <param name="c1">第一个cell的颜色</param>
/// <param name="e2">第二个cell一条边上的4个顶点</param>
/// <param name="c2">第二个cell的颜色</param>
private void TriangulateEdgeStrip(EdgeVertices e1, Color c1, EdgeVertices e2, Color c2)
{
AddQuad(e1.v1, e1.v2, e2.v1, e2.v2);
AddQuadColor(c1, c2);
AddQuad(e1.v2, e1.v3, e2.v2, e2.v3);
AddQuadColor(c1, c2);
AddQuad(e1.v3, e1.v4, e2.v3, e2.v4);
AddQuadColor(c1, c2);
}
}

  完成以上步骤后,我们就可以简化Triangulate方法了。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void Triangulate(HexDirection direction, HexCell cell)
{
Vector3 center = cell.Position;

//通过六边形一条边上的两个端点信息,计算出细分的中间两个点的信息
EdgeVertices e = new EdgeVertices(
center + HexMetrics.GetFirstSolidCorner(direction),
center + HexMetrics.GetSecondSolidCorner(direction)
);

//在计算出各个点的位置信息后,直接构建三角面片
TriangulateEdgeFan(center, e, cell.color);

//TriangulateConnection方法增加新的参数,自身不在进行顶点的计算了
if (direction <= HexDirection.SE)
{
TriangulateConnection(direction, cell, e);
}
}

  最后,我们要修改TriangulateConnection方法,使用TriangulateEdgeStrip来创建细分后的连接区域。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//这里不再使用单个顶点,而直接使用EdgeVertices进行顶点计算
private void TriangulateConnection(HexDirection direction, HexCell cell, EdgeVertices e1)
{
HexCell neighbor = cell.GetNeighbor(direction);
if (neighbor == null)
{
return;
}

Vector3 bridge = HexMetrics.GetBridge(direction);

//先计算出两个相邻cell的高度差
bridge.y = neighbor.Position.y - cell.Position.y;

//利用高度差和第一个cell的坐标,获得连接区域另外一边的4个顶点位置
EdgeVertices e2 = new EdgeVertices(e1.v1 + bridge, e1.v4 + bridge);

if (cell.GetEdgeType(direction) == HexEdgeType.Slope)
{
//这里使用EdgeVertices计算的顶点来构建矩形
TriangulateEdgeTerraces(e1.v1, e1.v4, cell, e2.v1, e2.v4, neighbor);
}
else
{
//这里使用EdgeVertices计算的顶点来构建矩形
TriangulateEdgeStrip(e1, cell.color, e2, neighbor.color);
}

HexCell nextNeighbor = cell.GetNeighbor(direction.Next());

if (direction <= HexDirection.E && nextNeighbor != null)
{
Vector3 v5 = e1.v4 + HexMetrics.GetBridge(direction.Next());
v5.y = nextNeighbor.Position.y;

if (cell.Elevation <= neighbor.Elevation)
{
if (cell.Elevation <= nextNeighbor.Elevation)
{
//这里使用EdgeVertices计算的顶点来构建矩形
TriangulateCorner(e1.v4, cell, e2.v4, neighbor, v5, nextNeighbor);
}
else
{
//这里使用EdgeVertices计算的顶点来构建矩形
TriangulateCorner(v5, nextNeighbor, e1.v4, cell, e2.v4, neighbor);
}
}
else if (neighbor.Elevation <= nextNeighbor.Elevation)
{
//这里使用EdgeVertices计算的顶点来构建矩形
TriangulateCorner(e2.v4, neighbor, v5, nextNeighbor, e1.v4, cell);
}
else
{
//这里使用EdgeVertices计算的顶点来构建矩形
TriangulateCorner(v5, nextNeighbor, e1.v4, cell, e2.v4, neighbor);
}
}
}

  至此,我们就完成了新方法的封装,这样在细分六边形三角面片、构建双色连接区域的时候,就不用在方法内部计算每个顶点的位置了。接下来的一章中,我们在这个基础上,完成阶梯状连接区域的细分。

Github代码