Jak przedstawić siatkę hextile / hex w pamięci?


119

Powiedzmy, że tworzę grę planszową z hextile grid, na przykład Settlers of Catan :

Hostowane przez imgur.com

Zwróć uwagę, że każdy wierzchołek i krawędź mogą mieć atrybut (droga i osada powyżej).

Jak mógłbym stworzyć strukturę danych reprezentującą tę tablicę? Jakie są wzory dostępu do sąsiadów, krawędzi i wierzchołków każdego kafelka?


można również użyć krzywej Hilberta, rozmieszczają one krzywe wypełniania w taki sposób, że przyleganie w płaszczyźnie jest zachowane w kodowaniu liniowym. sprawdź indeksowanie przestrzenne i zobacz, jak są używane! v interesujące
pbordeaux

Odpowiedzi:


156

Amit Patel opublikował niesamowitą stronę na ten temat. Jest tak obszerna i cudowna, że ​​musi być ostateczną odpowiedzią na to pytanie: siatki sześciokątne

Cubez


27
Dzięki :) Ta strona nie obejmuje krawędzi i wierzchołków, ale opisuję je w sekcji Relacje między częściami mojego artykułu o siatkach na www-cs-students.stanford.edu/~amitp/game-programming/grids (diagramy są dla siatek kwadratowych, ale tabela zawiera również wzory dla osiowych siatek sześciokątnych)
amitp

18

Taką siatkę można przedstawić w postaci dwuwymiarowej tablicy:

Jeśli

   2
7     3
   1   
6     4
   5

jest numerem jeden z jego sąsiadami w siatce heksadecymalnej, możesz to umieścić w tablicy 2D w następujący sposób:

2 3
7 1 4
  6 5

Oczywiście sąsiedztwo jest określane w tej siatce nie tylko przez to, że sąsiaduje w poziomie lub w pionie, ale także na podstawie jednej przekątnej.

Możesz jednak użyć wykresu, jeśli chcesz.


Chłodny. A co z danymi dotyczącymi krawędzi i wierzchołków?
płatny kujon

1
Prawdopodobnie przechowywałbym je osobno. Bez względu na to, czy patrzysz głównie na kafelki, czy na krawędzie / wierzchołki, druga połowa danych jest albo bolesna, albo zbędna do przechowywania.
Joey,

Zobacz artykuł Amita Patela w odpowiedzi „płatny nerd”.
aredridel

11

W tym artykule opisano, jak skonfigurować grę z siatką izomeryczną / sześciokątną. Polecam zajrzeć do Forcing Isometric and Hexagonal Maps onto a Rectangular Gridsekcji i sekcji ruchu. Chociaż różni się od tego, czego szukasz, może pomóc ci sformułować, jak robić to, co chcesz.


2

Dużo zajmowałem się klątwami. W takich przypadkach śledzisz każdy z 6 punktów dla granic heksa. Pozwala to dość łatwo go narysować.

Będziesz mieć pojedynczą tablicę obiektów reprezentujących heksy. Każdy z tych obiektów heksadecymalnych ma również 6 „wskaźników” (lub indeks do innej tablicy) wskazujących na inną tablicę „boków”. To samo dotyczy „wierzchołków”. Oczywiście wierzchołki miałyby 3 wskaźniki do sąsiednich heksów, a boki miałyby 2.

Zatem hex może być czymś w rodzaju: X, Y, Point (6), Vertices (6), Sides (6)

Następnie masz tablicę Hex, tablicę wierzchołków i tablicę boczną.

Wtedy bardzo łatwo jest znaleźć wierzchołki / boki dla hexa lub czegokolwiek.

Kiedy mówię pointer, równie łatwo może to być liczba całkowita wskazująca na element w tablicy wierzchołków lub bocznych lub cokolwiek innego. I oczywiście tablice mogą być listami lub czymkolwiek.


0
   2
7     3
   1   
6     4
   5

Możesz także spróbować „spłaszczyć” wiersze mapy. W tym przykładzie byłoby to:

  2
7 1 3
6 5 4

Czasami bardziej przydatne jest umieszczanie wierszy w jednym wierszu: P


1
Może to mieć trochę niechlujnego kodu sprawdzającego sąsiada, ponieważ na przykład 1 i 6 są sąsiadami, ale 3 i 5 nie, ale mają te same względne pozycje.
Bernhard Barker

0

Sugerowałbym coś takiego (użyję deklaracji w stylu 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;

Każdy heks ma sześć krawędzi i sześć wierzchołków. Każda krawędź śledzi dwa sąsiednie heksy, a każdy wierzchołek śledzi jego trzy sąsiednie heksy (heksy na krawędziach mapy będą przypadkiem specjalnym).

Oczywiście jest wiele rzeczy, które możesz zrobić w inny sposób. Możesz użyć wskaźników zamiast tablic, możesz użyć obiektów zamiast rekordów i możesz przechowywać swoje heksy w dwuwymiarowej tablicy, jak sugerowali inni respondenci.

Miejmy nadzieję, że może to dać ci kilka pomysłów na temat jednego ze sposobów podejścia.


0

Zaimplementowaliśmy AI Settlers of Catan dla projektu klasowego i zmodyfikowaliśmy kod z tej odpowiedzi (który był błędny), aby stworzyć tablicę ze stałym, losowym dostępem do wierzchołków i krawędzi. To był fajny problem, ale tablica zajęła dużo czasu, więc na wypadek, gdyby ktoś nadal szukał prostej implementacji, oto nasz kod w Pythonie:

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

Ta odpowiedź jest okropna. Właśnie wkleiłeś mnóstwo kodu, niczego nie wyjaśniając (z wyjątkiem tego, kto napisał kod). Nawet jeśli to było w porządku, sam kod jest okropny. Nie ma żadnych ciągów dokumentów, prawie żadnych komentarzy, a kilka komentarzy, które są dołączone, jest niezrozumiałych (logika myślenia, że ​​to mówi getEdgesOfVertex, a następnie dla każdej krawędzi getVertexEnds, usuwając trzy, które są == wierzchołkiem).
Carl Smith

0

Siedzę tu „w wolnym czasie programując dla zabawy” z heksami. I wygląda to tak ... Opowiem wam słowami, jak to wygląda.

  1. Sześciokąt: ma sześć sąsiadujących sześciokątów. Może dostarczyć odniesienie do każdego sąsiedniego pola heksów. Potrafi powiedzieć, z czego się składa (woda, skała, pył). Może łączyć się z innymi i odwrotnie. Może nawet automatycznie łączyć inne, które go otaczają, aby stworzyć większe pole i / lub upewnić się, że wszystkie pola mogą być zajęte przez jego sąsiadów.
  2. Budynek odnosi się do trzech dróg i trzech heksów. Mogą ci powiedzieć, kim oni są.
  3. Droga odnosi się do dwóch heksów i innych dróg, gdy są skierowane przez sąsiednie kafelki. Potrafią określić, które to kafelki iz którymi drogami lub budynkami się łączą.

To tylko pomysł, jak bym nad tym pracował.


0

Możesz utworzyć tablicę 2D, a następnie rozważyć prawidłowe pozycje jako:

  • W wierszach o numerach parzystych (0,2,4, ...): komórki o numerach nieparzystych.
  • W wierszach o numerach nieparzystych (1, 3, 5, ...): komórki o numerach parzystych.

W przypadku każdej komórki sąsiadami byłyby:

  • Ta sama kolumna, 2 wiersze w górę
  • Ta sama kolumna, 2 wiersze w dół
  • 1 w lewo + 1 w górę
  • 1 w lewo + 1 w dół
  • 1 w prawo + 1 w górę
  • 1 w prawo + 1 w dół

Ilustracja: Siatka sześciokątna

Znaki x to heksy. x, które są ukośnie względem siebie, są sąsiadami. | łączy pionowych sąsiadów.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.