如何在内存中表示六边形/十六进制网格?

假设我正在构建一个带有六边形网格的棋盘游戏,比如 卡坦岛:

Hosted by imgur.com

请注意,每个顶点和边缘可能有一个属性(上面的道路和定居点)。

如何创建一个表示这块板的数据结构?访问每个瓷砖的邻居、边缘和顶点的模式是什么?

53256 次浏览

这样的网格可以用二维数组表示:

如果

   2
7     3
1
6     4
5

是十六进制网格中的第一个,然后你可以把它放入一个二维数组,像这样:

2 3
7 1 4
6 5

显然,在这个网格中,邻居性不仅仅是通过水平或垂直相邻来确定的,而且还使用一个对角线。

不过,如果您愿意,也可以使用图表。

   2
7     3
1
6     4
5

你也可以尝试在地图上画“平面”行,例如:

  2
7 1 3
6 5 4

有时将行放在一行中更有用: P

这篇文章 介绍了如何设置一个异构/六边形网格游戏。我建议你看看 Forcing Isometric and Hexagonal Maps onto a Rectangular Grid部分和运动部分。虽然它不同于你正在寻找它可能会帮助你制定如何做你想要的。

我的建议如下(我将使用 Delphi 样式的声明) :

type
THexEdge = record
Hexes: array[1..2] of Integer; // Index of adjoining hexes.
// Other edge stuff goes here.
end;


THexVertex = record
Hexes: array[1..3] of Integer; // Index of adjoining hexes.
// Other vertex stuff goes here.
end;


THex = record
Edges: array[1..6] of Integer; // Index of edge.
Vertices: array[1..6] of Integer; // Index of vertex.
// Other hex stuff goes here.
end;


var
Edges: array of THexEdge;
Vertices: array of THexVertex;
HexMap: array of THex;

每个十六进制有六个边和六个顶点。每个边保持它的两个相邻的六边形的轨迹,每个顶点保持它的三个相邻的六边形的轨迹(在地图的边缘的六边形将是一个特殊的情况)。

当然,有很多事情你可以用不同的方式来做。您可以使用指针而不是数组,您可以使用对象而不是记录,您可以像其他答案所建议的那样,将您的六边形存储在一个二维数组中。

不过,希望这可以给你一些关于如何处理这个问题的想法。

我对付过很多巫术。在这样的情况下,您跟踪六进制边界的每一个点。这样你就可以很容易地画出来。

您将拥有一个表示巫术的对象数组。每个十六进制对象还有6个“指针”(或另一个数组的索引)指向另一个“边”数组。“顶点”也是一样。当然,这些顶点有3个指向相邻六边形的指针,两边有2个。

所以,一个咒语可能是这样的: X,Y,点(6) ,顶点(6) ,边(6)

然后有一个十六进制数组、顶点数组和侧面数组。

然后就很容易找到十六进制的顶点/边了。

当我说指针的时候,它也可以是一个指向顶点或者边数组中的元素的整数。当然数组也可以是列表或者其他什么东西。

Amit Patel 已经发布了一个关于这个主题的 太棒了页面。它是如此的全面和精彩,以至于它需要成为这个问题的最终答案: 六角网格

cubez

我们为一个类项目实现了一个卡坦岛人工智能,并修改了来自 这个答案的代码(这是错误的) ,以创建一个董事会与恒定的时间随机访问顶点和边。这是一个有趣的问题,但是这个板花了很多时间,所以如果有人还在寻找一个简单的实现,这里是我们的 Python 代码:

class Board:
# Layout is just a double list of Tiles, some will be None
def __init__(self, layout=None):
self.numRows = len(layout)
self.numCols = len(layout[0])
self.hexagons = [[None for x in xrange(self.numCols)] for x in xrange(self.numRows)]
self.edges = [[None for x in xrange(self.numCols*2+2)] for x in xrange(self.numRows*2+2)]
self.vertices = [[None for x in xrange(self.numCols*2+2)] for x in xrange(self.numRows*2+2)]
for row in self.hexagons:
for hexagon in row:
if hexagon == None: continue
edgeLocations = self.getEdgeLocations(hexagon)
vertexLocations = self.getVertexLocations(hexagon)
for xLoc,yLoc in edgeLocations:
if self.edges[xLoc][yLoc] == None:
self.edges[xLoc][yLoc] = Edge(xLoc,yLoc)
for xLoc,yLoc in vertexLocations:
if self.vertices[xLoc][yLoc] == None:
self.vertices[xLoc][yLoc] = Vertex(xLoc,yLoc)


def getNeighborHexes(self, hex):
neighbors = []
x = hex.X
y = hex.Y
offset = 1
if x % 2 != 0:
offset = -1


if (y+1) < len(self.hexagons[x]):
hexOne = self.hexagons[x][y+1]
if hexOne != None: neighbors.append(hexOne)
if y > 0:
hexTwo = self.hexagons[x][y-1]
if hexTwo != None: neighbors.append(hexTwo)
if (x+1) < len(self.hexagons):
hexThree = self.hexagons[x+1][y]
if hexThree != None: neighbors.append(hexThree)
if x > 0:
hexFour = self.hexagons[x-1][y]
if hexFour != None: neighbors.append(hexFour)
if (y+offset) >= 0 and (y+offset) < len(self.hexagons[x]):
if (x+1) < len(self.hexagons):
hexFive = self.hexagons[x+1][y+offset]
if hexFive != None: neighbors.append(hexFive)
if x > 0:
hexSix = self.hexagons[x-1][y+offset]
if hexSix != None: neighbors.append(hexSix)
return neighbors


def getNeighborVertices(self, vertex):
neighbors = []
x = vertex.X
y = vertex.Y
offset = -1
if x % 2 == y % 2: offset = 1
# Logic from thinking that this is saying getEdgesOfVertex
# and then for each edge getVertexEnds, taking out the three that are ==vertex
if (y+1) < len(self.vertices[0]):
vertexOne = self.vertices[x][y+1]
if vertexOne != None: neighbors.append(vertexOne)
if y > 0:
vertexTwo = self.vertices[x][y-1]
if vertexTwo != None: neighbors.append(vertexTwo)
if (x+offset) >= 0 and (x+offset) < len(self.vertices):
vertexThree = self.vertices[x+offset][y]
if vertexThree != None: neighbors.append(vertexThree)
return neighbors


# used to initially create vertices
def getVertexLocations(self, hex):
vertexLocations = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
vertexLocations.append((x, 2*y+offset))
vertexLocations.append((x, 2*y+1+offset))
vertexLocations.append((x, 2*y+2+offset))
vertexLocations.append((x+1, 2*y+offset))
vertexLocations.append((x+1, 2*y+1+offset))
vertexLocations.append((x+1, 2*y+2+offset))
return vertexLocations


# used to initially create edges
def getEdgeLocations(self, hex):
edgeLocations = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
edgeLocations.append((2*x,2*y+offset))
edgeLocations.append((2*x,2*y+1+offset))
edgeLocations.append((2*x+1,2*y+offset))
edgeLocations.append((2*x+1,2*y+2+offset))
edgeLocations.append((2*x+2,2*y+offset))
edgeLocations.append((2*x+2,2*y+1+offset))
return edgeLocations


def getVertices(self, hex):
hexVertices = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
hexVertices.append(self.vertices[x][2*y+offset]) # top vertex
hexVertices.append(self.vertices[x][2*y+1+offset]) # left top vertex
hexVertices.append(self.vertices[x][2*y+2+offset]) # left bottom vertex
hexVertices.append(self.vertices[x+1][2*y+offset]) # right top vertex
hexVertices.append(self.vertices[x+1][2*y+1+offset]) # right bottom vertex
hexVertices.append(self.vertices[x+1][2*y+2+offset]) # bottom vertex
return hexVertices


def getEdges(self, hex):
hexEdges = []
x = hex.X
y = hex.Y
offset = x % 2
offset = 0-offset
hexEdges.append(self.edges[2*x][2*y+offset])
hexEdges.append(self.edges[2*x][2*y+1+offset])
hexEdges.append(self.edges[2*x+1][2*y+offset])
hexEdges.append(self.edges[2*x+1][2*y+2+offset])
hexEdges.append(self.edges[2*x+2][2*y+offset])
hexEdges.append(self.edges[2*x+2][2*y+1+offset])
return hexEdges


# returns (start, end) tuple
def getVertexEnds(self, edge):
x = edge.X
y = edge.Y
vertexOne = self.vertices[(x-1)/2][y]
vertexTwo = self.vertices[(x+1)/2][y]
if x%2 == 0:
vertexOne = self.vertices[x/2][y]
vertexTwo = self.vertices[x/2][y+1]
return (vertexOne, vertexTwo)


def getEdgesOfVertex(self, vertex):
vertexEdges = []
x = vertex.X
y = vertex.Y
offset = -1
if x % 2 == y % 2: offset = 1
edgeOne = self.edges[x*2][y-1]
edgeTwo = self.edges[x*2][y]
edgeThree = self.edges[x*2+offset][y]
if edgeOne != None: vertexEdges.append(edgeOne)
if edgeTwo != None: vertexEdges.append(edgeTwo)
if edgeThree != None: vertexEdges.append(edgeThree)
return vertexEdges


def getHexes(self, vertex):
vertexHexes = []
x = vertex.X
y = vertex.Y
xOffset = x % 2
yOffset = y % 2


if x < len(self.hexagons) and y/2 < len(self.hexagons[x]):
hexOne = self.hexagons[x][y/2]
if hexOne != None: vertexHexes.append(hexOne)


weirdX = x
if (xOffset+yOffset) == 1: weirdX = x-1
weirdY = y/2
if yOffset == 1: weirdY += 1
else: weirdY -= 1
if weirdX >= 0 and weirdX < len(self.hexagons) and weirdY >= 0 and weirdY < len(self.hexagons):
hexTwo = self.hexagons[weirdX][weirdY]
if hexTwo != None: vertexHexes.append(hexTwo)


if x > 0 and x < len(self.hexagons) and y/2 < len(self.hexagons[x]):
hexThree = self.hexagons[x-1][y/2]
if hexThree != None: vertexHexes.append(hexThree)


return vertexHexes

我坐在这里“在我的业余时间编程的乐趣”与巫术。它是这样的... 我用文字告诉你它是什么样子的。

  1. 六边形: 它有六个相邻的六边形。它可以提供每个相邻的六角形瓦片的参考。它可以告诉你它由什么组成(水,岩石,尘埃)。它可以将自己与他人联系起来,反之亦然。它甚至可以自动连接他周围的其他领域,以创建一个更大的领域,或确保所有领域可以由其邻居处理。
  2. 一座建筑涉及三条道路和三块六角砖,它们可以告诉你它们是哪一种。
  3. 道路引用两个六边形和其他道路时,它们是由邻近的瓷砖。他们可以分辨出哪些瓷砖,以及它们连接到哪些道路或建筑物上。

这只是我的一个想法。

您可以创建一个2D 数组,然后将有效位置视为:

  • 对偶数行(0,2,4,...) : 奇数编号的单元格。
  • 对奇数行(1,3,5,...) : 偶数单元格。

对于每个细胞,它的邻居是:

  • 同一列,上面两排
  • 同一列,下面两排
  • 左1 + 上1
  • 向左1 + 向下1
  • 右边1 + 上边1
  • 向右1 + 向下1

插图:

Hex Grid

对角线相连的标记是相邻的。 | 连接垂直的相邻的。