准备工作
如果大佬对ECS版的海克斯无限地图感兴趣,不妨参与进来,欢迎Star/Folk源码
0下载Unity编辑器(2019.1.12f1 or 更新的版本),if(已经下载了)continue;
1克隆:git clone https://github.com/cloudhu/HexMapMadeInUnity2019ECS.git --recurse
或下载Zip压缩包
2如果下载的是压缩包,需要先将压缩包解压。然后将HexMapMadeInUnity2019ECS添加到Unity Hub项目中;
3用Unity Hub打开的开源项目:HexMapMadeInUnity2019ECS,等待Unity进行编译工作;
4打开项目后,启动场景在Scenes目录下,打开AdvancedHexMap场景(优化重构的版本)。
高地与阶梯连接
今天的计算非常复杂,不过还是尽量表述清楚,要创建高地就需要新增数据字段来保存高地的海拔参数。打开Data脚本:
+++在UpdateData中新增
/// <summary>
/// 海拔
/// </summary>
public int Elevation;
+++++在Cell中新增
/// <summary>
/// 当前单元的海拔
/// </summary>
public int Elevation;
//六个方向相邻单元的海拔
public int NEElevation;
public int EElevation;
public int SEElevation;
public int SWElevation;
public int WElevation;
public int NWElevation;
这样我们需要使用的海拔数据字段都有了,我们可以先随机生成一些海拔数据给六边形单元,看看效果。打开CellSpawnSystem脚本,添加一个数组来保存随机生成的海拔,然后把这些数据交给每一个单元,这是在Job中完成的,非常高效:
//保存单元颜色的原生数组
NativeArray<Color> Colors=new NativeArray<Color>(Height * Width, Allocator.Temp);
//保存单元海拔的原生数组
NativeArray<int> Elevations = new NativeArray<int>(Height * Width, Allocator.Temp);
//后面将从服务器获取这些数据,现在暂时随机生成
for (int i = 0; i < Height* Width; i++)
{
Colors[i]= new Color(random.NextFloat(), random.NextFloat(), random.NextFloat());
Elevations[i]= random.NextInt(6);
}
+++在循环生成单元的过程中把随机生成的数据交给单元
//3.计算阵列对应的六边形单元坐标
float _x = (x + z * 0.5f - z / 2) * (HexMetrics.InnerRadius * 2f);
float _y = Elevations[i] * HexMetrics.elevationStep;
float _z = z * (HexMetrics.OuterRadius * 1.5f);
//5.设置每个六边形单元的数据
CommandBuffer.SetComponent(index, instance, new Cell
{
Index=i,
Color = color,
Position= new Vector3(_x, _y, _z),
NE=blendColors[0],
E=blendColors[1],
SE=blendColors[2],
SW=blendColors[3],
W=blendColors[4],
NW= blendColors[5],
NEIndex=directions[0],
EIndex = directions[1],
SEIndex = directions[2],
SWIndex = directions[3],
WIndex = directions[4],
NWIndex = directions[5],
Elevation=Elevations[i],
NEElevation=Elevations[directions[0]],
EElevation = Elevations[directions[1]],
SEElevation = Elevations[directions[2]],
SWElevation = Elevations[directions[3]],
WElevation = Elevations[directions[4]],
NWElevation = Elevations[directions[5]]
});
这个时候我们的六边形单元就有了海拔高度了,效果如图所示:
通过上图,我们发现单元和单元之间因为海拔高度的差距而脱节,整个地图变得支离破碎,我们需要让它们重新连接起来。
桥面倾斜连接
支离破碎的原因是桥断了,这需要到CellSystem脚本中处理接收到的海拔数据,只需把相邻单元的海拔更新到算法中即可:
//添加外围桥接区域的顶点3和顶点4
Vector3 bridge = (HexMetrics.GetBridge(j));
Vector3 vertex3 = (vertex1 + bridge);
Vector3 vertex4 = (vertex2 + bridge);
//获取相邻单元的海拔
int neighborElevation = elevations[j];
//顶点3和顶点4在相邻单元上,海拔应与其同高
vertex3.y = vertex4.y = neighborElevation * HexMetrics.elevationStep;
顶点3和顶点4是桥的另一端,只需更新它们,使其与相邻单元同高,这样在连接单元的时候,桥就不会断掉了:
阶梯状的桥连接
桥面已经修复了,这个时候我们产生了新的需求:斜坡的桥面应该有适当的阶梯,使得玩家可以步行。
如上图所示,我们先设计一个桥面由两个阶梯组成,把这种设计添加到HexMetrics脚本中:
/// <summary>
/// 海拔步长
/// </summary>
public const float elevationStep = 5f;
/// <summary>
/// 每个斜坡上的阶梯数
/// </summary>
public const int terracesPerSlope = 2;
/// <summary>
/// 阶梯步长
/// </summary>
public const int terraceSteps = terracesPerSlope * 2 + 1;
/// <summary>
/// 水平阶梯步长
/// </summary>
public const float horizontalTerraceStepSize = 1f / terraceSteps;
/// <summary>
/// 垂直阶梯步长
/// </summary>
public const float verticalTerraceStepSize = 1f / (terracesPerSlope + 1);
/// <summary>
/// 阶梯插值
/// </summary>
/// <param name="a">向量a</param>
/// <param name="b">向量b</param>
/// <param name="step">步数</param>
/// <returns>渐变坡度</returns>
public static Vector3 TerraceLerp(Vector3 a, Vector3 b, int step)
{
float h = step * HexMetrics.horizontalTerraceStepSize;
a.x += (b.x - a.x) * h;
a.z += (b.z - a.z) * h;
float v = ((step + 1) / 2) * HexMetrics.verticalTerraceStepSize;
a.y += (b.y - a.y) * v;
return a;
}
/// <summary>
/// 颜色插值
/// </summary>
/// <param name="a">颜色A</param>
/// <param name="b">颜色B</param>
/// <param name="step">步长</param>
/// <returns>渐变色</returns>
public static Color TerraceLerp(Color a, Color b, int step)
{
float h = step * HexMetrics.horizontalTerraceStepSize;
return Color.Lerp(a, b, h);
}
我们不但定义了斜坡阶梯数,还计算了阶梯步数、水平步长、垂直步长、阶梯插值、颜色插值,插值计算是为了产生渐变效果。
为了实现桥面的阶梯,我们需要更改CellSystem中与桥面相关的代码:
//插值计算第一个三角的两个顶点
Vector3 vertex6 = HexMetrics.TerraceLerp(vertex1, vertex3, 1);
Vector3 vertex7 = HexMetrics.TerraceLerp(vertex2, vertex4, 1);
/(First Step)/
bridgeColor = HexMetrics.TerraceLerp(currCellColor, neighborColor, 1);
colorBuffer.Add(currCellColor);
vertexBuffer.Add(vertex1);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex6);
colorBuffer.Add(currCellColor);
vertexBuffer.Add(vertex2);
colorBuffer.Add(currCellColor);
vertexBuffer.Add(vertex2);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex6);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex7);
///(Middle Steps)///
for (int i = 2; i < HexMetrics.terraceSteps; i++)
{
Vector3 stepVertex1 = vertex6;
Vector3 stepVertex2 = vertex7;
Color c1 = bridgeColor;
vertex6 = HexMetrics.TerraceLerp(vertex1, vertex3, i);
vertex7 = HexMetrics.TerraceLerp(vertex2, vertex4, i);
bridgeColor = HexMetrics.TerraceLerp(currCellColor, neighborColor, i);
colorBuffer.Add(c1);
vertexBuffer.Add(stepVertex1);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex6);
colorBuffer.Add(c1);
vertexBuffer.Add(stepVertex2);
colorBuffer.Add(c1);
vertexBuffer.Add(stepVertex2);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex6);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex7);
}
///(Last Step)///
//bridgeColor = HexMetrics.TerraceLerp(currCellColor, neighbor, HexMetrics.terraceSteps);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex6);
colorBuffer.Add(neighborColor);
vertexBuffer.Add(vertex3);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex7);
colorBuffer.Add(bridgeColor);
vertexBuffer.Add(vertex7);
colorBuffer.Add(neighborColor);
vertexBuffer.Add(vertex3);
colorBuffer.Add(neighborColor);
vertexBuffer.Add(vertex4);
代码看起来十分冗余,很多地方可以进行提炼,但是我不想到处传递参数,传参如果不够简洁,还不如复制粘贴。这里如果提炼成方法,会有一大堆参数传递,即使这样其实也应该提炼成方法,我有点后悔没有这么做了,太冗余了。
虽然不够简洁,但是效果仍然实现了,后面优化的时候再进行代码提炼,先看看效果:
阶梯合理化
虽然效果实现了,但是上图中又产生了许多不合理的东西,例如桥面之间的三角桥洞补丁,例如相邻单元处于同一海拔高度的水平面,这时就不需要阶梯了,所以需要继续写代码来改进这些bug。
打开静态类脚本HexMetrics,我们来定义桥面的类型,已经判断方法:
/// <summary>
/// 单元边界类型
/// </summary>
public enum HexEdgeType {
Flat, Slope, Cliff
}
/// <summary>
/// 获取边缘的类型
/// </summary>
/// <param name="elevation1">海拔1</param>
/// <param name="elevation2">海拔2</param>
/// <returns>边缘类型</returns>
public static HexEdgeType GetEdgeType(int elevation1, int elevation2)
{
if (elevation1 == elevation2)
{
return HexEdgeType.Flat;
}
int delta = elevation2 - elevation1;
if (delta == 1 || delta == -1)
{
return HexEdgeType.Slope;
}
return HexEdgeType.Cliff;
}
所以我们可以通过判断桥面类型来确定是否需要阶梯化,如果是斜坡,则添加阶梯,于是我们对阶梯方法进行提炼:
Job之内
#region 桥面
//判断当前单元与相邻单元的海拔高低差,如果是斜坡,则添加阶梯,平面和峭壁则无需阶梯
if (HexMetrics.GetEdgeType(elevation, neighborElevation) == HexMetrics.HexEdgeType.Slope)
{
TriangulateEdgeTerraces(vertex1,vertex2,currCellColor,vertex3,vertex4,neighborColor,ref colorBuffer, ref vertexBuffer);
}
else
{
Color bridgeColor = (currCellColor + neighborColor) * 0.5f;
AddQuad(vertex1, currCellColor,vertex2, bridgeColor, vertex3, bridgeColor, vertex4,neighborColor,ref colorBuffer,ref vertexBuffer);
}
#endregion
Job之外
//三角化桥面阶梯
void TriangulateEdgeTerraces(Vector3 beginLeft, Vector3 beginRight, Color beginColor, Vector3 endLeft, Vector3 endRight, Color endColor,ref DynamicBuffer<ColorBuffer> colorBuffer, ref DynamicBuffer<VertexBuffer> vertexBuffer)
{
Vector3 vertex3 = HexMetrics.TerraceLerp(beginLeft, endLeft, 1);
Vector3 vertex4 = HexMetrics.TerraceLerp(beginRight, endRight, 1);
/(First Step)/
Color bridgeColor = HexMetrics.TerraceLerp(beginColor, endColor, 1);
AddQuad(beginLeft, beginColor, beginRight, beginColor,vertex3, bridgeColor, vertex4, bridgeColor, ref colorBuffer, ref vertexBuffer);
///(Middle Steps)///
for (int i = 2; i < HexMetrics.terraceSteps; i++)
{
Vector3 stepVertex1 = vertex3;
Vector3 stepVertex2 = vertex4;
Color c1 = bridgeColor;
vertex3 = HexMetrics.TerraceLerp(beginLeft, endLeft, i);
vertex4 = HexMetrics.TerraceLerp(beginRight, endRight, i);
bridgeColor = HexMetrics.TerraceLerp(beginColor, endColor, i);
AddQuad(stepVertex1, c1,stepVertex2, c1,vertex3, bridgeColor, vertex4, bridgeColor, ref colorBuffer, ref vertexBuffer);
}
///(Last Step)///
AddQuad(vertex3, bridgeColor, vertex4, bridgeColor, endLeft, endColor, endRight, endColor, ref colorBuffer, ref vertexBuffer);
}
同时也对添加四平面和添加三角进行提炼:
//添加矩形三角顶点和颜色
void AddQuad(Vector3 v1,Color c1, Vector3 v2,Color c2, Vector3 v3, Color c3, Vector3 v4, Color c4, ref DynamicBuffer<ColorBuffer> colorBuffer, ref DynamicBuffer<VertexBuffer> vertexBuffer)
{
colorBuffer.Add(c1);
vertexBuffer.Add(v1);
colorBuffer.Add(c3);
vertexBuffer.Add(v3);
colorBuffer.Add(c2);
vertexBuffer.Add(v2);
colorBuffer.Add(c2);
vertexBuffer.Add(v2);
colorBuffer.Add(c3);
vertexBuffer.Add(v3);
colorBuffer.Add(c4);
vertexBuffer.Add(v4);
}
//添加三角顶点与颜色
void AddTriangle(Vector3 v1, Color bottomColor, Vector3 v2, Color leftColor, Vector3 v3,Color rightColor, ref DynamicBuffer<ColorBuffer> colorBuffer, ref DynamicBuffer<VertexBuffer> vertexBuffer)
{
colorBuffer.Add(bottomColor);
vertexBuffer.Add(v1);
colorBuffer.Add(leftColor);
vertexBuffer.Add(v2);
colorBuffer.Add((bottomColor + leftColor + rightColor) / 3f);
vertexBuffer.Add(v3);
}
提炼之后,之前的Job变得简洁多了:
/// <summary>
/// 计算六边形单元的顶点和颜色
/// </summary>
struct CalculateJob : IJobForEachWithEntity<Cell,NewDataTag> {
public EntityCommandBuffer.Concurrent CommandBuffer;
[BurstCompile]
public void Execute(Entity entity, int index,[ReadOnly] ref Cell cellData,[ReadOnly]ref NewDataTag tag)
{
//0.获取单元索引,Execute的index不可靠,添加动态缓存
int cellIndex = cellData.Index;
DynamicBuffer<ColorBuffer> colorBuffer = CommandBuffer.AddBuffer<ColorBuffer>(index, entity);
DynamicBuffer<VertexBuffer> vertexBuffer = CommandBuffer.AddBuffer<VertexBuffer>(index, entity);
//1.获取当前单元的中心位置
Vector3 currCellCenter = cellData.Position;
//缓存当前单元的颜色
Color currCellColor = cellData.Color;
保存需要混合的颜色,使用数组[]是为了方便循环
Color[] blendColors = new Color[6];
blendColors[0] = cellData.NE;
blendColors[1] = cellData.E;
blendColors[2] = cellData.SE;
blendColors[3] = cellData.SW;
blendColors[4] = cellData.W;
blendColors[5] = cellData.NW;
//前3个方向相邻单元的索引
int[] directions = new int[3];
directions[0] = cellData.NEIndex;
directions[1] = cellData.EIndex;
directions[2] = cellData.SEIndex;
//前三个方向相邻单元的海拔
int[] elevations = new int[3];
elevations[0] = cellData.NEElevation;
elevations[1] = cellData.EElevation;
elevations[2] = cellData.SEElevation;
//添加六边形单元六个方向的顶点、三角和颜色
for (int j = 0; j < 6; j++)
{
//1.添加中心区域的3个顶点
Vector3 vertex1 = (currCellCenter + HexMetrics.SolidCorners[j]);
Vector3 vertex2 = (currCellCenter + HexMetrics.SolidCorners[j + 1]);
AddTriangle(currCellCenter, currCellColor, vertex1, currCellColor, vertex2, currCellColor, ref colorBuffer, ref vertexBuffer);
//Connection Between 2 cells
#region Bridge=桥
//桥只连接前三个方向相邻的单元,从而避免重复连接
if (j <= 2)
{
if (directions[j] == 0)
{//如果没有相邻的单元,则跳过循环
continue;
}
Color neighborColor = blendColors[j];
//添加外围桥接区域的顶点
Vector3 bridge = (HexMetrics.GetBridge(j));
Vector3 vertex3 = (vertex1 + bridge);
Vector3 vertex4 = (vertex2 + bridge);
//当前单元的海拔
int elevation = cellData.Elevation;
//邻居单元的海拔
int neighborElevation = elevations[j];
//顶点3和顶点4在相邻单元上,海拔应与其同高
vertex3.y = vertex4.y = neighborElevation * HexMetrics.elevationStep;
#region 桥面
//判断当前单元与相邻单元的海拔高低差,如果是斜坡,则添加阶梯,平面和峭壁则无需阶梯
if (HexMetrics.GetEdgeType(elevation, neighborElevation) == HexMetrics.HexEdgeType.Slope)
{
TriangulateEdgeTerraces(vertex1,vertex2,currCellColor,vertex3,vertex4,neighborColor,ref colorBuffer, ref vertexBuffer);
}
else
{
Color bridgeColor = (currCellColor + neighborColor) * 0.5f;
AddQuad(vertex1, currCellColor,vertex2, bridgeColor, vertex3, bridgeColor, vertex4,neighborColor,ref colorBuffer,ref vertexBuffer);
}
#endregion
///TODO:处理桥洞
}
#endregion
}
//4.turn off cell system by remove NewDataTag
CommandBuffer.RemoveComponent<NewDataTag>(index,entity);
}
完成后的效果如下:
从上图看得出,桥洞的三角补丁不见了,那是因为我将其剪切到记事本上了,为了排除干扰,而且那段代码非常冗余。
制作三角补丁
下面开始制作三角补丁,它的样子如下图所示:
三角补丁位于三个六边形单元之间,我们把最低的单元称为底部(Bottom,简称B),然后按照B=》L(左边Left)=》R(右边Right)的顺序来进行处理,这里不仅要画三角,而且需要做阶梯化处理,方法如下:
//桥洞阶梯化
void TriangulateCornerTerraces(Vector3 begin, Color beginColor, Vector3 left, Color leftColor, Vector3 right, Color rightColor, ref DynamicBuffer<ColorBuffer> colorBuffer,ref DynamicBuffer<VertexBuffer> vertexBuffer)
{
First Triangle
Vector3 v3 = HexMetrics.TerraceLerp(begin, left, 1);
Vector3 v4 = HexMetrics.TerraceLerp(begin, right, 1);
Color c3 = HexMetrics.TerraceLerp(beginColor, leftColor, 1);
Color c4 = HexMetrics.TerraceLerp(beginColor, rightColor, 1);
AddTriangle(begin,beginColor,v3,c3,v4,c4,ref colorBuffer,ref vertexBuffer);
///Middle Steps
for (int i = 2; i < HexMetrics.terraceSteps; i++)
{
Vector3 v1 = v3;
Vector3 v2 = v4;
Color c1 = c3;
Color c2 = c4;
v3 = HexMetrics.TerraceLerp(begin, left, i);
v4 = HexMetrics.TerraceLerp(begin, right, i);
c3 = HexMetrics.TerraceLerp(beginColor, leftColor, i);
c4 = HexMetrics.TerraceLerp(beginColor, rightColor, i);
AddQuad(v1,c1,v2,c2,v3,c3, v4, c4, ref colorBuffer, ref vertexBuffer);
}
/Last Step
AddQuad(v3,c3,v4,c4,left,leftColor,right,rightColor, ref colorBuffer, ref vertexBuffer);
}
如下图所示,阶梯化的三角补丁正好弥补三个单元之间的三角裂缝,但是不是所有的情况都是这样的。
三角的左右两边可能是悬崖,也有可能是水平的,下面需要对所有三角补丁的情况进行判断:
//判断相邻的三个六边形单元的高低关系,按照最低(Bottom),左(Left),右(Right)的顺序进行三角化处理
if (elevation <= neighborElevation)
{
if (elevation <= nextElevation)
{
//当前单元海拔最低
TriangulateCorner(vertex2, currCellColor, vertex4, neighborColor, vertex5, blendColors[next],ref colorBuffer,ref vertexBuffer,elevation, neighborElevation, nextElevation);
}
else
{
TriangulateCorner(vertex5, blendColors[next], vertex2, currCellColor, vertex4, neighborColor, ref colorBuffer, ref vertexBuffer, nextElevation, elevation, neighborElevation);
}
}
else if (neighborElevation <= nextElevation)
{
TriangulateCorner(vertex4, neighborColor, vertex5, blendColors[next], vertex2, currCellColor, ref colorBuffer, ref vertexBuffer, neighborElevation, nextElevation, elevation);
}
else
{
TriangulateCorner(vertex5, blendColors[next], vertex2, currCellColor, vertex4, neighborColor, ref colorBuffer, ref vertexBuffer, nextElevation, elevation, neighborElevation);
}
以上代码只是确定了B=》L=》R的处理顺序,下面才是判断阶梯类型的代码:
///桥洞三角化
void TriangulateCorner(Vector3 bottom, Color bottomColor, Vector3 left, Color leftColor, Vector3 right, Color rightColor, ref DynamicBuffer<ColorBuffer> colorBuffer, ref DynamicBuffer<VertexBuffer> vertexBuffer, int bottomElevation, int leftElevation, int rightElevation)
{
HexMetrics.HexEdgeType leftEdgeType = HexMetrics.GetEdgeType(bottomElevation, leftElevation);
HexMetrics.HexEdgeType rightEdgeType = HexMetrics.GetEdgeType(bottomElevation, rightElevation);
if (leftEdgeType == HexMetrics.HexEdgeType.Slope)
{
if (rightEdgeType == HexMetrics.HexEdgeType.Slope)
{
TriangulateCornerTerraces(bottom, bottomColor, left, leftColor, right, rightColor, ref colorBuffer, ref vertexBuffer);
}else if (rightEdgeType == HexMetrics.HexEdgeType.Flat)
{
TriangulateCornerTerraces(left, leftColor, right, rightColor, bottom, bottomColor, ref colorBuffer, ref vertexBuffer);
}
else
{
TriangulateCornerTerracesCliff(bottom, bottomColor, left, leftColor, right, rightColor, ref colorBuffer, ref vertexBuffer, bottomElevation, leftElevation, rightElevation);
}
}
else if (rightEdgeType == HexMetrics.HexEdgeType.Slope)
{
if (leftEdgeType == HexMetrics.HexEdgeType.Flat)
{
TriangulateCornerTerraces(right, rightColor, bottom, bottomColor, left, leftColor, ref colorBuffer, ref vertexBuffer);
}
else
{
TriangulateCornerCliffTerraces(bottom, bottomColor, left, leftColor, right, rightColor, ref colorBuffer, ref vertexBuffer, bottomElevation, leftElevation, rightElevation);
}
}
else if (HexMetrics.GetEdgeType(leftElevation, rightElevation) == HexMetrics.HexEdgeType.Slope)
{
if (leftElevation < rightElevation)
{
TriangulateCornerCliffTerraces(right, rightColor, bottom, bottomColor, left, leftColor, ref colorBuffer, ref vertexBuffer, rightElevation, bottomElevation, leftElevation);
}
else
{
TriangulateCornerTerracesCliff( left, leftColor, right, rightColor, bottom, bottomColor, ref colorBuffer, ref vertexBuffer,leftElevation, rightElevation, bottomElevation);
}
}
else
{
两个单元处于同一平面,填充桥三角补丁,添加桥三角的3个顶点
AddTriangle(bottom,bottomColor,left,leftColor,right,rightColor,ref colorBuffer,ref vertexBuffer);
}
}
有很多种情况,斜坡(Slope)+斜坡(S)+平面(Flat),也可能是SFS、FSS、FFS、SFF,这还没有考虑到悬崖(Cliff),所以还得额外判断C的情况,大概有27种排列组合,不过我们尽量精简判断:
//三角化陡峭的阶梯
void TriangulateCornerTerracesCliff(Vector3 begin, Color beginCellColor, Vector3 left, Color leftCellColor, Vector3 right, Color rightCellColor, ref DynamicBuffer<ColorBuffer> colorBuffer, ref DynamicBuffer<VertexBuffer> vertexBuffer, int bottomElevation, int leftElevation, int rightElevation)
{
float b = 1f / (rightElevation - bottomElevation);
if (b < 0)
{
b = -b;
}
Vector3 boundary = Vector3.Lerp(begin, right, b);
Color boundaryColor = Color.Lerp(beginCellColor, rightCellColor, b);
TriangulateBoundaryTriangle(begin,beginCellColor,left,leftCellColor,boundary,boundaryColor,ref colorBuffer,ref vertexBuffer);
if (HexMetrics.GetEdgeType(leftElevation,rightElevation) == HexMetrics.HexEdgeType.Slope)
{
TriangulateBoundaryTriangle(left, leftCellColor, right, rightCellColor, boundary, boundaryColor, ref colorBuffer, ref vertexBuffer);
}
else
{
AddTriangle(left,leftCellColor,right,rightCellColor,boundary,boundaryColor,ref colorBuffer,ref vertexBuffer);
}
}
///峭壁镜像处理:左右对调
void TriangulateCornerCliffTerraces(Vector3 begin, Color beginCellColor, Vector3 left, Color leftCellColor, Vector3 right, Color rightCellColor, ref DynamicBuffer<ColorBuffer> colorBuffer, ref DynamicBuffer<VertexBuffer> vertexBuffer, int bottomElevation, int leftElevation, int rightElevation)
{
float b = 1f / (leftElevation - bottomElevation);
if (b < 0)
{
b = -b;
}
Vector3 boundary = Vector3.Lerp(begin, left, b);
Color boundaryColor = Color.Lerp(beginCellColor, leftCellColor, b);
TriangulateBoundaryTriangle(right, rightCellColor, begin, beginCellColor, boundary, boundaryColor, ref colorBuffer, ref vertexBuffer);
if (HexMetrics.GetEdgeType(leftElevation, rightElevation) == HexMetrics.HexEdgeType.Slope)
{
TriangulateBoundaryTriangle(left, leftCellColor, right, rightCellColor, boundary, boundaryColor, ref colorBuffer, ref vertexBuffer);
}
else
{
AddTriangle(left, leftCellColor, right, rightCellColor, boundary, boundaryColor, ref colorBuffer, ref vertexBuffer);
}
}
对于我来说,这个非常复杂,我花了十多个小时来消化这些内容,好歹实现了最终效果:
这里没有讲海拔编辑器,因为那是最简单的内容,相信难不倒诸君,所以就省略了。如果有什么疑惑,可以先看看源码,或者直接在评论区留言。下一篇会跟着Jasper Flick的教程Unity Hex Map Tutorial走,这里只不过将其ECS化而已。
ECS专题目录
ECS更新计划
作者的话
如果喜欢可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证!
如果有技术难题需要讨论,可以加入开发者联盟:566189328(付费群)为您提供有限的技术支持,以及,心灵鸡汤!
当然,不需要技术支持也欢迎加入进来,劈柴、遛狗、聊天、撸猫!( ̄┰ ̄*)