Làm cách nào để biểu diễn lưới hextile / hex trong bộ nhớ?


118

Giả sử tôi đang xây dựng một trò chơi hội đồng với lưới lục giác, như Settlers of Catan :

Được lưu trữ bởi imgur.com

Lưu ý rằng mỗi đỉnh và cạnh có thể có một thuộc tính (một con đường và khu định cư ở trên).

Làm cách nào để tạo cấu trúc dữ liệu đại diện cho bảng này? Các mẫu để truy cập các lân cận, cạnh và đỉnh của mỗi ô là gì?


bạn cũng có thể sử dụng đường cong hilbert, chúng là các đường cong dũa giãn cách sao cho cạnh kề trong mặt phẳng được bảo toàn trong mã hóa tuyến tính. kiểm tra lập chỉ mục không gian và cách chúng được sử dụng! v thú vị
pbordeaux

Câu trả lời:


155

Amit Patel đã đăng một trang tuyệt vời về chủ đề này. Nó toàn diện và tuyệt vời đến mức nó cần phải là câu trả lời dứt khoát cho câu hỏi này: Lưới lục giác

cubez


27
Cảm ơn :) Trang đó không bao gồm các cạnh và đỉnh, nhưng tôi đề cập chúng trong phần Mối quan hệ giữa các bộ phận của bài viết lưới của tôi tại www-cs-students.stanford.edu/~amitp/game-programming/grids (các sơ đồ là dùng cho lưới vuông nhưng bảng này bao gồm các công thức dùng cho lưới hex trục cũng)
amitp

18

Lưới như vậy có thể được biểu diễn trong một mảng hai chiều:

Nếu

   2
7     3
   1   
6     4
   5

là số một với các hàng xóm của nó trong lưới hex, sau đó bạn có thể đặt nó vào một mảng 2D như sau:

2 3
7 1 4
  6 5

Rõ ràng là lân cận được xác định trong lưới này không chỉ bằng cách kề nhau theo chiều ngang hoặc chiều dọc mà còn sử dụng một đường chéo.

Tuy nhiên, bạn cũng có thể sử dụng một biểu đồ, nếu bạn muốn.


Mát mẻ. Còn dữ liệu cho các cạnh và đỉnh?
một mọt sách được trả tiền

1
Tôi có lẽ sẽ lưu trữ chúng riêng biệt. Bất kể bạn chủ yếu nhìn vào các ô hay các cạnh / đỉnh, thì nửa còn lại của dữ liệu là khó hoặc dư thừa để lưu trữ.
Joey

Xem bài viết của Amit Patel trong câu trả lời "mọt sách trả phí".
aredridel

11

bài viết này hướng dẫn cách thiết lập trò chơi lưới Isomeric / Hexagon. Tôi khuyên bạn nên xem Forcing Isometric and Hexagonal Maps onto a Rectangular Gridphần và phần chuyển động. Mặc dù nó khác với những gì bạn đang tìm kiếm nhưng nó có thể giúp bạn hình thành cách làm những gì bạn muốn.


2

Tôi đã xử lý rất nhiều với hexes. Trong những trường hợp như thế này, bạn theo dõi từng điểm trong số 6 điểm cho đường viền của hệ lục phân. Điều này cho phép bạn vẽ nó khá dễ dàng.

Bạn sẽ có một mảng các đối tượng đại diện cho các hex. Mỗi đối tượng hex này cũng có 6 "con trỏ" (hoặc một chỉ mục đến một mảng khác) trỏ đến một mảng "cạnh" khác. Điều tương tự đối với "đỉnh". Tất nhiên các đỉnh sẽ có 3 con trỏ đến các lục giác liền kề và các cạnh sẽ có 2.

Vì vậy, một hex có thể là một cái gì đó như: X, Y, Point (6), Vertices (6), Sides (6)

Sau đó, bạn có một mảng Hex, mảng đỉnh và mảng bên.

Sau đó, khá đơn giản để tìm các đỉnh / cạnh của một hex, hoặc bất cứ điều gì.

Khi tôi nói con trỏ, nó có thể dễ dàng là một số nguyên trỏ đến phần tử trong đỉnh hoặc mảng bên hoặc bất cứ điều gì. Và tất nhiên mảng có thể là danh sách hoặc bất cứ thứ gì.


0
   2
7     3
   1   
6     4
   5

Bạn cũng có thể thử 'làm phẳng' các hàng trên bản đồ của mình. Đối với ví dụ này, nó sẽ là:

  2
7 1 3
6 5 4

Đôi khi hữu ích hơn khi có các hàng trong một hàng: P


1
Điều này có thể có một số mã kiểm tra hàng xóm lộn xộn, bởi vì, ví dụ: 1 và 6 là hàng xóm, nhưng 3 và 5 thì không, nhưng chúng có cùng vị trí tương đối.
Bernhard Barker

0

Tôi sẽ đề xuất một cái gì đó như sau (tôi sẽ sử dụng khai báo kiểu 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;

Mỗi hex có sáu cạnh và sáu đỉnh. Mỗi cạnh theo dõi hai lục giác liền kề của nó và mỗi đỉnh theo dõi ba lục giác liền kề của nó (các lục giác trên các cạnh của bản đồ sẽ là một trường hợp đặc biệt).

Tất nhiên, có rất nhiều thứ mà bạn có thể làm theo một cách khác. Bạn có thể sử dụng con trỏ thay vì mảng, bạn có thể sử dụng các đối tượng thay vì các bản ghi và bạn có thể lưu trữ các hex của mình trong một mảng hai chiều như những người trả lời khác đã đề xuất.

Hy vọng rằng điều đó có thể cung cấp cho bạn một số ý tưởng về một cách để tiếp cận nó.


0

Chúng tôi đã triển khai Bộ giải quyết Catan AI cho một dự án lớp và sửa đổi mã từ câu trả lời này (bị lỗi) để tạo Bảng có quyền truy cập ngẫu nhiên theo thời gian liên tục vào các đỉnh và cạnh. Đó là một vấn đề thú vị, nhưng hội đồng quản trị mất rất nhiều thời gian, vì vậy trong trường hợp bất kỳ ai vẫn đang tìm kiếm một cách triển khai đơn giản thì đây là mã Python của chúng tôi:

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

Câu trả lời này là khủng khiếp. Bạn vừa dán hàng tấn mã mà không giải thích bất cứ điều gì (ngoại trừ ai đã viết mã). Ngay cả khi điều đó là ổn ở đây, thì bản thân đoạn mã này thật kinh khủng. Không có docstrings, hầu như không có nhận xét, và một vài nhận xét được đưa vào là không thể hiểu được (Logic khi nghĩ rằng điều này đang nói getEdgesOfVertex và sau đó đối với mỗi cạnh getVertexEnds, lấy ra ba cạnh là == đỉnh).
Carl Smith

0

Tôi đang ngồi đây "trong thời gian rảnh rỗi viết mã cho vui" với hexes. Và nó diễn ra như thế này ... Tôi sẽ cho bạn biết nó trông như thế nào bằng lời.

  1. Hình lục giác: nó có sáu hình lục giác hàng xóm. Nó có thể cung cấp tham chiếu cho từng ô hex lân cận. Nó có thể cho bạn biết nó bao gồm những gì (nước, đá, bụi). Nó có thể kết nối chính nó với những người khác và ngược lại. Nó thậm chí có thể tự động kết nối những người khác xung quanh anh ta để tạo ra một trường lớn hơn và hoặc đảm bảo tất cả các trường đều có thể được các trường bên cạnh áp dụng.
  2. Một tòa nhà tham chiếu đến ba con đường và ba ô Hex. Họ có thể cho bạn biết họ là ai.
  3. Một con đường tham chiếu đến hai hình lục giác và các đường khác khi chúng được tô bởi các ô lân cận. Họ có thể cho biết đó là ô nào và những con đường hoặc tòa nhà mà họ kết nối với.

Đây chỉ là một ý tưởng về cách tôi sẽ làm việc với nó.


0

Bạn có thể tạo một mảng 2D và sau đó xem xét các vị trí hợp lệ là:

  • Trên hàng số chẵn (0,2,4, ...): các ô được đánh số lẻ.
  • Trên các hàng số lẻ (1,3,5, ...): các ô được đánh số chẵn.

Đối với mỗi ô, hàng xóm của nó sẽ là:

  • Cùng một cột, tăng 2 hàng
  • Cùng một cột, giảm 2 hàng
  • 1 trái + 1 lên
  • 1 trái + 1 xuống
  • 1 phải + 1 lên
  • 1 phải + 1 xuống

Hình minh họa: Lưới Hex

Các dấu x là hexes. x là đường chéo của nhau là hàng xóm của nhau. | kết nối hàng xóm dọc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.