2-2 链接相邻的力图单元

  在上一章节中,我们通过扩展方法,可以计算出一个地图单元自身相对于其周围地图单元的位置,并完善了相关的方法。接下来,我们要将地图内的所有地图单元互相链接起来。
  在HexGrid.cs中使用CreateCell方法创建地图单元的时候,建立每个地图单元之间的位置链接关系。在这里,我们是从左至右逐行查看每个地图单元时,就可以知道已经创建了哪些地图单元,这些创建好的地图单元就是我们要进行链接的地图单元。
  最简单的链接关系就是E-W链接。每一行的第一个地图单元,是没有E方向的相邻地图单元的。除此之外,其他的单元格都是有E方向的相邻单元格。而且这些地图单元都是在我们遍历它们并建立链接之前,就已经被创建好了。所以这里可以很容易的建立所有地图单元的E-W方向的链接。

HexGrid.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void CreateCell(int x, int z, int i)
{


//为每个cell赋颜色初始值
cell.color = defaultColor;

//判断cell是否为每一行第一个
//如果不是第一个,则cell会有W方位相邻的cell,就可以建立E-W链接
if (x > 0)
{
cell.SetNeighbor(HexDirection.W, cells[i - 1]);
}

//该变量用来存储被实例化的cellLabelPrefab预置
Text label = Instantiate<Text>(cellLabelPrefab);


}

  横向的链接关系建立完毕后,我们来看地图单元之间的纵向关系。由于每一行地图单元之间是呈锯齿状交错的,所以建立两行地图单元之间的链接需要不同的处理方法。在这里我们先处理偶数行的SE方向链接。

HexGrid.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
private void CreateCell(int x, int z, int i)
{


//为每个cell赋颜色初始值
cell.color = defaultColor;

//判断cell是否为每一行第一个
//如果不是第一个,则cell会有W方位相邻的cell,就可以建立E-W链接
if (x > 0)
{
cell.SetNeighbor(HexDirection.W, cells[i - 1]);
}

//这里判断是否为第一行,因为行之间的链接会不太一样,尤其是第一行,需要做一次额外判断
if (z > 0)
{
//这里的&为位运算符 MSDN:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators
//这里使用位运算符,判断是否为偶数行
if ((z & 1) == 0)
{
//当为偶数行的时候,创建SE方向的链接
//cells[1 - width]为SE方向的实例,图片参考 http://magi-melchiorl.gitee.io/pages/Pics/Hexmap/2-2-3.png
cell.SetNeighbor(HexDirection.SE, cells[1 - width]);
}
}

//该变量用来存储被实例化的cellLabelPrefab预置
Text label = Instantiate<Text>(cellLabelPrefab);


}

什么是位运算符?MSDN连接

大家都知道”&&”是布尔运算符里的”与”运算,”&”就是”按位与”。它们的执行逻辑相同,但后者是对于每一位进行运算。
两个Bit都是1,与运算的结果就是1。例如:10101010 & 00001111结果是00001010。
从计算机原理层面上来说,所有的数字都是用二进制表示的。二进制中的1、2、3、4写作1、10、11、100。如你所见,表示是否为偶数的最低有效位是第一位。
我们把二进制的与运算作为一个遮罩,忽略除了第一位之外的其他位数,如果结果是0,那就表示这是一个偶数。

  与SE方向的链接关系类似,这里我们可以使用相同的方法创建SW方向的链接。要注意,每一行的第一个地图单元是没有SW方向的链接的。
  

HexGrid.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void CreateCell(int x, int z, int i)
{


//这里使用位运算符,判断是否为偶数行
if ((z & 1) == 0)
{
//当为偶数行的时候,创建SE方向的链接
//cells[i - width]为SE方向的实例,也就是右下方的cell
cell.SetNeighbor(HexDirection.SE, cells[i - width]);

//每行的第一个cell是没有左下角(SW)方向的链接,这里要判断cell是否为第一个
if (x > 0)
{
//cells[i - width - 1]为SW方向的实例,也就是左下方的cell
cell.SetNeighbor(HexDirection.SW, cells[i - width - 1]);
}
}


}

  奇数行与偶数行有相同的逻辑,只不过SE-NW、SW-NE方向的链接,与偶数行是相互镜像的。

HexGrid.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
private void CreateCell(int x, int z, int i)
{


//这里使用位运算符,判断是否为偶数行
if ((z & 1) == 0)
{

//这里是奇数行建立链接的部分
else
{
//i - width 为自身SW方向的实例
cell.SetNeighbor(HexDirection.SW, cells[i - width]);

//判断奇数行cell是否为每行最后一个,因为奇数行最后一个cell是没有SE方向的实例
if (x < width - 1)
{
//i - width + 1 为奇数行自身SE方向的实例
cell.SetNeighbor(HexDirection.SE, cells[i - width + 1]);
}
}
}


}

  通过以上的代码,我们创建了SE-NW、SW-NE、W-E方向的链接。通过观察可以发现,除了横向的W-E链接,我们都是先创建每个地图单元的SW和SE方向的链接,也就是将自身左下、右下的地图单元实例先进行链接,同时SetNeighbor方法会创建SW和SE的镜像链接,也就是NE和NW链接。所以最特殊的第一行也就和其他的地图单元自动创建了方位链接关系。

  通过观察和代码可以发现,在整个地图系统中,并不是每个地图单元都有全部的6个相邻地图单元,也就是说,在边缘处的地图单元,其Neighbors数组并没有填满。有些边缘处地图单元最多有5个与其相邻的地图单元,最少的则有2个。

  现在,我们已经将所有的地图单元建立了相互的链接关系,这是颜色混合的前置工作之一。接下来会进行一些代码的调整,并实现单元格之间颜色过渡的效果。

Github代码