2-1 计算相邻的地图单元的位置

  上一章中我们完成了改变单个地图单元颜色的功能。但是通过观察可以发现,如果相邻的两个地图单元颜色不相同,他们之间的颜色过渡会显得非常的生硬。两者的对比效果如下图:

  观察对比图可以发现,地图单元之间拥有颜色过渡后,视觉效果会更好一些。在接下来的部分,我们暂时将改变色时重新构建整个地图Mesh的问题放一边,重点放在视觉表现上面。

  要想产生平滑的颜色过渡,我们首先需要知道每个地图单元与谁相邻,才可以得到相邻地图单元的颜色,进一步进行颜色的平滑过渡。在这里我们创建一个新的脚本,命名为HexDirection,其中声明一个枚举,用来标识一个地图单元与其相邻的其他地图单元的方位。其分别为东北(NE)、东( E )、东南(SE)、西南(SW)、西(W)、西北(NW)。如下图所示:

HexDirection.cs
1
2
3
4
5
6
7
8
9
10
11
12
//表示相邻cell方位的枚举
//从左上角顺时针依次开始
//参考图:http://magi-melchiorl.gitee.io/pages/Pics/Hexmap/2-1-2.png
public enum HexDirection
{
NE,
E,
SE,
SW,
W,
NW
}

关于枚举类型
MSDN连接

可以使用enum关键字来定义枚举,是有名字的有序列表。这种类型的变量可以使用这些名称中的一个作为它的值,每个名称都对应一个数字,默认情况下从0开始。当你需要有限长度的可命名有序列表时,这些非常有用。
实际上枚举就是简单的整数。你可以对它们进行加、减操作,转换成整数再转回来。同样也可以转换成少数其他类型,但整数是其基本类型。

  回到HexCell.cs脚本中,在开头部分添加一个数组,用来存储相邻地图单元的实例。这里将该数组设置为Private,并使用[SerializeField]标记这个变量,使其能够在Inspector面板中显示出来。代码如下:

HexCell.cs
1
2
3
4
5
6
7
public class HexCell : MonoBehaviour
{


//用来存储每个cell的 相邻cell实例
[SerializeField] private HexCell[] neighbors;
}

  在这里,我们会保留相邻地图单元的引用,虽然可以通过坐标来直接获取相邻地图单元,但是在有引用关系的情况下会更加方便一些,而且保留引用关系在之后的一些操作中也很便利。

  回到Unity中,选中Hex Cell的预置,我们可以看到neighbors变量已经显示在了Inspector面板上。因为每个地图单元最多可以有6个相邻地图单元。所以这里将neighbors数组的长度设置为6。效果如下:

  接下来在HexCell.cs脚本中,创建一个公共方法,用来获取neighbors数组中的内容。因为之前声明的HexDirection枚举,其取值范围在0-5之间,同时neighbors数组长度为6,所以这里不用检查数组下标是否越界。代码如下:

HexCell.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HexCell : MonoBehaviour
{


/// <summary>
/// 用来获取neighbors中相应方位cell的实例
/// 这里注意,虽然HexDirection取值为0-5,neighbors长度为6,不会越界
/// 但是不是所有cell都有6个相邻的cell_neighbor,所以可能取出为空,之后会添加判断
/// </summary>
/// <param name="direction">相邻cell的方位 枚举</param>
/// <returns>相应方位cell 的实例</returns>
public HexCell GetNeighbor(HexDirection direction)
{
return neighbors[(int)direction];
}
}

  因为neighbors数组为private,所以还需要一个将相邻地图单元实例赋值到数组中的一个方法。代码如下:

HexCell.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HexCell : MonoBehaviour
{


/// <summary>
/// 将相邻的cell实例赋值到neighbors中对应的位置
/// 这里注意neighbors的索引下标,应与HexDirection方位的int值对应
/// </summary>
/// <param name="direction">相邻cell的方位</param>
/// <param name="cell">相邻cell的实例</param>
public void SetNeighbor(HexDirection direction, HexCell cell)
{
neighbors[(int)direction] = cell;
}
}

  因为方位是相对的。例如一个地图单元A,其E方位的地图单元实例为B,那么也可以说A在B的W方位。如下图所示:

  所以在获取自身相邻地图单元实例的时候,同时将自身加入其相邻单元格实例的neighbors数组中。代码如下:

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


/// <summary>
/// 将相邻的cell实例赋值到neighbors中对应的位置
/// 这里注意neighbors的索引下标,应与HexDirection方位的int值对应
/// </summary>
/// <param name="direction">相邻cell的方位</param>
/// <param name="cell">相邻cell的实例</param>
public void SetNeighbor(HexDirection direction, HexCell cell)
{
neighbors[(int)direction] = cell;

//在赋值自身的neighbors实例的同时,也将自身实例赋值到相邻cell的neighbors数组中
cell.neighbors[(int)direction.Opposite()] = this;
}
}

  接下来,让我们到HexDirection.cs脚本中完成Oppositie()方法。这个方法主要是将地图单元自身实例,添加到相邻地图单元neighbors数组中的对应位置。代码如下:

HexDirection.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


public static class HexDirectionExtensions
{
/// <summary>
/// 参考图 http://magi-melchiorl.gitee.io/pages/Pics/Hexmap/2-1-4.png
/// 根据 相邻cell 位于 自身cell 的位置,获得 自身cell 位于 相邻cell 的位置
/// 也就是得到相反位置
/// </summary>
/// <param name="direction"></param>
/// <returns></returns>
public static HexDirection Opposite(this HexDirection direction)
{
//return (int)direction < 3 ? (direction + 3) : (direction - 3);

//已知了 相邻cell 的位置,自身cell位置与相邻cell位置相反
//即 W(1) - E(4)这样的对应关系,之间正好相差3
if ((int)direction < 3)
{
return (direction + 3);
}
else
{
return (direction - 3);
}
}
}

什么是扩展方法?
MSDN连接

扩展方法是一个静态类中的静态方法,但使用起来像是某些类型的实例方法。这个类型可以是任何东西:自定义类、接口、结构体、原生数据结构或者是枚举。扩展方法的第一个参数之前必须有this关键字,它定义方法将操作的类型和实例的值。
这个特性允许我们在任何东西上添加方法,就像是静态方法的参数可以是任何类型。在有节制适量使用的情况下这个特性非常方便。但过度使用会造成代码结构的混乱。

Github代码