六角格坐标定位

1、外观

在开始讲解之前,先上图!


一个传统的四方格地图。




一个倾斜X轴的六角格地图。





一个水平X轴的六角格地图。


2、六角网格的寻址


Cell坐标系(CellMap)


六角网格的方向

规定六角网格两个锐角为六角网格的水平方向,当与传统坐标系的水平轴平行时(如第一张我们看到的那些网格),认为网格为水平方向。
因此,当格子的水平方向和传统坐标系的水平方向垂直时,认为网格为垂直方向。
本文的所有结论均在水平网格下进行验证,有兴趣的朋友可以试试垂直网格好不好用。

建立Map坐标系

我们平时使用的坐标,要么是屏幕坐标(2D游戏),要么是笛卡尔坐标(3D游戏)。

上述两种坐标系最大的特点是:

1、坐标轴互相垂直。

2、坐标值连续。

根据第一节的直观观察,可以知道六角格无法建立出垂直的坐标轴。

而为了满足第二点,一般都为其建立斜X轴坐标系,或不太水平的坐标系。


寻址网格 (AddressingGrid)

过所有Cell的中点做水平和垂直方向上的线,这些互相垂直/平行的线形成的网格,称为六角网格的寻址网格。

寻址网格的责任是将worldPosition 转化为 GridPosition ,再通过GridPosition转化为CellPosition。如下图所示:


可以看到,一个GridCell为一个正方形,规定六角形边长为 SideLength , 则这个GridCell的边长为 3/2 * SideLength;

同时,我们也认为一个HexCell的Size 和 GridCell的Size等值。

通过上图可以看到,这个Grid的左上角并不在worldspace  的(0,0)位置,而是在 (-0.5*cellsize.x , -0.5*cellsize.y )的地方。




看到上图彩色的部分了吗?整个CellMap 会被GridCell 分割成A、B两种情况,我们需要关注的是gridCell框住的三个HexCell中处于右下角的那个。

对于情况A,就是绿色部分, 对于情况B,就是粉色部分。稍后在描述 GridPosition to CellPosition 的时候会用到。


世界坐标和六角网格坐标之间的关系 (CellPosition To WorldPosition)



这是一个 BiasHexMap ,可以看到, cell.y = 0 的格子在WorldSpace 上,一直在往下走。因此当一个CellPosition -> WorldPosition 时,需要考虑其cell.x的值对world.y的影响。我们再看一张 HorizontalHexMap 的图。


可以看到,HorizontalHexMap 只有偶数列才会不一样,奇数列和没有发生什么特别的事情




前面我们知道 CellSize  = 3/2*SideLength;

因此 cell的左上角的(上图左上角的蓝色框框的LeftTop点)

world.x = cell.x * cellsize.x

world.y = cell.y * cellsize.y + cell.x * cellsize.y * 0.5

cellCenter.x = world.x + SideLength

cellCenter.y = world.y + SideLength * sqrt(3);


对于 HorizontalCellMap , 映射关系比较简单,我就不在这里写了,用上的大侠自己推导吧。 需要注意的是cell.x 的奇偶性


世界坐标和寻址网格坐标的关系(WorldPosition To GridPosition)

world->grid 就是四角形网格寻址,一个除法就可以搞定,唯一需要注意的是, GridMap 的(0,0) 不在 world(0,0),而是做了一个 1/2 SideLength 的偏移。
设point 为世界坐标中的某个点。则其Grid坐标为:
grid.x =(int)((point.x + SideLength * 0.5f) /  hexCellSize.x);
grid.y = (int)point.y / hexCellSize.y;

这个转换对于 BiasHexCellMap 和 HorizontalHexCellMap 都适用,因为两者建立GridMap 的方式是一样的。


寻址网格坐标和六角网格坐标的关系( GridPosition To CellPosition)


先上图:


观察被标记为绿色的Grid,里面 右下角HexCell的cell坐标。
可以得出结论
rigthBottomCell.x  = grid.x
rigthBottomCell.y = grid.y - grid.x / 2

而对于HorzontalHexCell,超级简单:
rbCell = grid;

为什么要求右下角Cell呢,这是为了定位一个point在grid的哪个Cell。

选取正确的Cell (Pick Cell In Grid)




我们以情况A为例,上图棕色边框为一个 Grid ,黄色线段为3个Cell 的几何中心。

为了选择出正确的Cell,只需要在WorldSpace下计算 Point到哪个中点的距离近就可以了。

因此可以简单的判断Cell :

d = Min( d0, Min(d1,d2));

然后就可以得知是哪个Cell了。

 

推导过程如下:





可以证明,3个Cell的共线边,是这个黄色三角形的3条中垂线


线段中垂线上的点呢,到线段两端的距离是相等的,因此我们可以证明上述最短距离的结论了。


My PickCell Method

网上对于PickCell的详细步骤讲述比较少,可能帖子久远,都丢了吧。我在这里贴一下自己的思路。





我们以 Grid 左上角为起点,顺时针标记3个Cell , C0(粉),C1(黄),C2(绿) [对于情况B: C0(黄), C1(粉), C2(绿) ]

蓝色的线将 整个 Grid 分为上下两个部分。
在每一个部分中,只有一条中垂线,将剩下的空间分为线的上面和下面。
因此我们其实可以很简单的通过判断指定点是否在指定直线的上方来选出最终的Cell.

我采用点斜式,因为k一定是 +/- sqrt(3) ,而直线经过的点P(x0,y0) 则可以根据 GirdPosition -> WroldPosition  得到。
如下图的红色粗线和紫色粗线:




理论分析到此为止,具体情况在处理的时候还需要做情况B。
如果是HorizontalHex,还得区分奇偶列,因为HrozontalHex的“邻居”在奇偶列是不一样的。


Cell Adjacent

一个Cell 的 Adjacent,  根据是 BiasHexCell 还是 HorizontalHexCell ,有着不同的值。
可以参考QuadrangleCell , 其 Left 为 (-1,0) , Top( 0, 1) ,类似的,我们可以枚举出8个方向上前进时,其坐标变化。

HexCell 只有 6个Adjacent,因为没有 Left 和 Right。
而且 HorizontalHexCell 还要区分奇偶列。
我在这里直接给出布局吧,有兴趣的读者可以自己画画图,很容易就能得出结论。
	public static class Adjacent
	{
		public static class Quad
		{
			/*
				* lefttop(0)		top(1) 		righttop(2);
				* left(7)			self(-1)	right(3);
				* leftbottom(6)	bottom(5)	rightbottom(4);
				*/
			public static readonly Position Top = new Position(0, -1);
			public static readonly Position Left = new Position(-1, 0);
			public static readonly Position Bottom = new Position(0, 1); //- Top;
			public static readonly Position Right = new Position(1, 0); //- Left;
			public static readonly Position LeftTop = new Position(-1, -1);//Left + Top;
			public static readonly Position LeftBottom = new Position(-1, 1);//Left + Bottom;
			public static readonly Position RightTop = new Position(1, -1);//Right + Top;
			public static readonly Position RightBottom = new Position(1, 1);//Right + Bottom;
		}
		public static class BiasHex
		{
			/* 倾斜坐标轴,采用  lefttop -> rightbottom为x,  top->bottom 为y;
				* 仔细体会下;
				* 						top(1:[0,-1]);
				* lefttop(0:[-1,0])						righttop(2:[1,-1]);
				*						self(-1:[0,0]);
				* leftbottom(5:[-1,1])					rightbottom(3:[1,0]);
				*						bottom(4:[0,1]);
				*/
			public static readonly Position Top = new Position(0, -1);
			public static readonly Position Bottom = new Position(0, 1);//-Top;
			public static readonly Position LeftTop = new Position(-1, 0);
			public static readonly Position LeftBottom = new Position(-1, 1);// + LeftTop + Bottom;
			public static readonly Position RightTop = new Position(1, -1); //- LeftBottom;
			public static readonly Position RightBottom = new Position(1, 0);// - LeftTop;
		}
		public static class HorizontalHex
		{
			/* 水平坐标轴;
				* top->bottom 为y;
				* 偶数列采用 leftbotoom -> self - > rightbottom为x;                       __0__
				*                                                                        /     \
				*                                                                       0       0
				* 奇数列采用 leftup -> self - > rightup为x;           0       0
				*                                                      \__0__/
				* 仔细体会下;
				*/
			public static class Even
			{
				/* 
					* 						top(1:[0,-1]);
					* lefttop(0:[-1,-1])						righttop(2:[1,-1]);
					*						self(-1:[0,0]);
					* leftbottom(5:[-1,0])					rightbottom(3:[1,0]);
					*						bottom(4:[0,1]);
					*/
				public static readonly Position Top = new Position(0, -1);
				public static readonly Position Bottom = new Position(0, 1);//-Top;
				public static readonly Position LeftTop = new Position(-1, -1);
				public static readonly Position LeftBottom = new Position(-1, 0);// + LeftTop + Bottom;
				public static readonly Position RightTop = new Position(1, -1); //- LeftBottom;
				public static readonly Position RightBottom = new Position(1, 0);// - LeftTop;
			}
			public static class Uneven
			{
				/* 
					* 						top(1:[0,-1]);
					* lefttop(0:[-1,0])						righttop(2:[1,0]);
					*						self(-1:[0,0]);
					* leftbottom(5:[-1,1])					rightbottom(3:[1,1]);
					*						bottom(4:[0,1]);
					*/
				public static readonly Position Top = new Position(0, -1);
				public static readonly Position Bottom = new Position(0, 1);//-Top;
				public static readonly Position LeftTop = new Position(-1, 0);
				public static readonly Position LeftBottom = new Position(-1, 1);// + LeftTop + Bottom;
				public static readonly Position RightTop = new Position(1, 0); //- LeftBottom;
				public static readonly Position RightBottom = new Position(1, 1);// - LeftTop;
			}
		}
	}


有了Adjacent 和 右下角Cell的Position,就可以定位出具体的Cell了。


好了,到此
 WorldPosition -> GridPosition - > CellPosition

CellPosition - > WorldPosition

的关系就分析完成了。

3、参考文献

[1] 《给蜜蜂和游戏玩家:如何处理六边形贴片》 Thomas Jahn, King Art, Jorn Loviscach, Hochschule Bremen, 游戏编程精粹7 第1章 1.5, ISBN 978-7-115-22914-4

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值