用Python存储三角形/六边形网格的最佳方法

11

我正在制作一个六边形方块的游戏,并决定使用三角形/六边形网格。我发现这个问题帮助我生成坐标,并稍微修改了代码,将所有坐标存储为字典中的键,值为"."(地板)或"X"(墙),并包括一个函数,打印出一个字符串表示地图,其中每个非空字符代表一个六边形瓷砖。这是新代码:

deltas = [[1,0,-1],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0]]
class HexGrid():
    def __init__(self, radius):
        self.radius = radius
        self.tiles = {(0, 0, 0): "X"}
        for r in range(radius):
            a = 0
            b = -r
            c = +r
            for j in range(6):
                num_of_hexas_in_edge = r
                for i in range(num_of_hexas_in_edge):
                    a = a+deltas[j][0]
                    b = b+deltas[j][1]
                    c = c+deltas[j][2]           
                    self.tiles[a,b,c] = "X"

    def show(self):
        l = []
        for y in range(20):
            l.append([])
            for x in range(60):
                l[y].append(".")
        for (a,b,c), tile in self.tiles.iteritems():
            l[self.radius-1-b][a-c+(2*(self.radius-1))] = self.tiles[a,b,c]
        mapString = ""
        for y in range(len(l)):
            for x in range(len(l[y])):
                mapString += l[y][x]
            mapString += "\n"
        print(mapString)

使用此代码,我可以生成所有在半径范围内的坐标,如下所示:

import hexgrid
hg = hexgrid.HexGrid(radius)

可以像这样访问坐标:

hg.tiles[a,b,c]

目前看来这个方法还可以正常工作,但我相信以这种方式存储地图一定存在一些缺点。如果有任何缺点,请指出并可能提出更好的地图存储方式?非常感谢您的时间。


a、b和c是什么?这是3D吗? - Has QUIT--Anony-Mousse
4个回答

15

使用数组作为存储器可能会节省一些CPU时间,但差异可能是微不足道的。

然而,您错过了一种非常简单的管理此类映射的方法。将其视为行和列,只是单元格具有稍微不同的形状。

+--+--+--+--+--+--+--+
 \/ \/ \/ \/ \/ \/ \/    Even row

  /\ /\ /\ /\ /\ /\ /\   Odd row
 +--+--+--+--+--+--+--+

对于六边形:

  __    __    __    __
 /  \__/  \__/  \__/  \__ Even row
 \__/  \__/ A\__/  \__/   Odd  row
 /  \__/ F\__/ B\__/  \__ Even row
 \__/  \__/ X\__/  \__/   Odd  row
 /  \__/ E\__/ C\__/  \__ Even row
 \__/  \__/ D\__/  \__/   Odd  row
 /  \__/  \__/  \__/  \__ Even row
 \__/  \__/  \__/  \__/   Odd  row
然后,您可以将数据存储为普通的二维数组。奇数行向右偏移0.5,并且需要确定X的邻居步骤:上方:A = (0,-2),右上方:B = (1,-1),右下方:C = (1,1),下方:D = (0,2), 左下方:E = (0,1),左上方:F = (0,-1) 如果您可以容忍浪费一些内存,还可以让每一列为空,邻居关系会变得更简单:(0,-2),(1,-1),(1,-1),(0,-2),(-1,-1),(-1,1)

谢谢你的回答。我最喜欢的将六边形网格存储在二维数组中的方法是使用偏轴方法,该方法在此处描述(http://roguebasin.roguelikedevelopment.org/index.php/Hexagonal_Tiles#Coordinate_systems_with_a_hex_grid)。这就是我以前使用的方法。然而,我现在非常喜欢使用的坐标系,想要坚持使用它,所以我不会接受你的答案。非常感谢你的帮助!(制作那些ASCII图肯定很烦人!) - RylandAlmanza
@RylandAlmanza:链接现在是这个。看起来维基百科已经重新组织了! :-) 祝好…… - pythonlarry
基于此,我编写了一个简单的Python + NumPy代码来生成六边形网格 - meduz

7
我也做了一些研究,发现有一种更简单的方法来实现它。您不必像您所做的那样复杂!该表格可以是一个简单的数组,没有任何特殊规则。
您想要使用六边形根坐标系。请参见此处的理论:https://en.wikipedia.org/wiki/Root_system。还有https://www.redblobgames.com/grids/hexagons/ 单元格(0,0)位于结构的中心,然后具有六个相邻元素:如同众所周知的正交表格(1,0)、(0,1)、(-1,0)、(0,-1),以及 (1,1)、(-1,-1)。其他单元格也有类似的六个相邻元素,无需取模!
以下是一些Ascii图形,以便更好地理解:
   _____       _____      ____      __
  / -2,2\_____/ 0,1 \____/2,0 \____/  \__ 
  \_____/-1,1 \_____/ 1,0\____/3,-1\__/   
  /-2,1 \_____/0,0  \____/2,-1\____/  \__     
  \_____/-1,0 \_____/1,-1\____/3,-2\__/   
  /-2,0 \_____/ 0,-1\____/2,-2\____/  \__ 
  \_____/     \_____/    \____/    \__/   

由于它遵循向量几何的规则,因此您可以计算平面(以及屏幕)中每个单元格的中心位置。 向量的坐标为60°而不是90°:a =(0,1),但b =(0,87,0.5),只需将这些坐标相乘和相加即可!

您可能希望使用Python库 Hexy


0
不要使用hg.tiles[a,b,c]。
可以通过以下方式将tiles变为三维列表: hg.tiles = [[[z for z in range(10)] for y in range(10)] for x in range(10)] 现在,您可以使用hg.tiles[a][b][c] 来访问瓦片。
附注:a = a+deltas[j][0] 应该改为 a += deltas[j][0] ,其他赋值也是如此。

两者都没有实质性的变化。 - Has QUIT--Anony-Mousse
这种方法有什么优势吗?它会更快吗?我会接受这个答案,但在接受答案之前,我想知道为什么它是正确的答案。 - RylandAlmanza
我怀疑他的方法是否更快,它基本上是3次查找而不是创建1个哈希表。你需要进行基准测试才能确定这一点。 - Otto Allmendinger

0

也许最好的存储六边形的方法是使用Python列表或元组;然后挑战就变成了如何从列表中检索特定的六边形。在Python中,一个很好的方法是建立一个以六边形坐标为键,列表索引(或六边形对象)为值的字典。假设您的六边形列表不需要更改,则完成后可以将列表转换为元组。如果您将六边形对象保存为字典中的值,则可以放弃保存列表;字典直接将六边形坐标映射到对象。

您的代码已经生成了一系列六边形坐标,只需将它们保存在列表中,并同时构建参考字典即可。此外,如果您正在编写一个管理游戏板的类,则所有存储和检索的细节都可以隐藏在类中。

一些代码:

radius = 4
deltas = [[1,0,-1],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0]]

hexes = []
indices = {}
index = 0

for r in range(radius):
    print("radius %d" % r)
    x = 0
    y = -r
    z = +r

    hexes.append((x,y,z))
    indices[(x,y,z)] = index   # Or store objects here
    index += 1
    print(x,y,z)

    for j in range(6):
        if j==5:
            num_of_hexes_in_edge = r-1
        else:
            num_of_hexes_in_edge = r
        for i in range(num_of_hexes_in_edge):
            x = x+deltas[j][0]
            y = y+deltas[j][1]
            z = z+deltas[j][2]

            hexes.append((x,y,z))
            indices[(x,y,z)] = index   # Or store objects here
            index += 1
            print(x,y,z)

hexes = tuple(hexes)
print(hexes)
print(indices)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接