Як я можу представити гексатичну / шестигранну сітку в пам'яті?


119

Скажіть, я будую настільну гру з гексатильною сіткою, як поселенці Катану :

Хостинг imgur.com

Зауважте, що кожна вершина та край можуть мати атрибут (дорога та селище вгорі).

Як би я створив структуру даних, яка представляє цю плату? Які схеми доступу до сусідів, країв і вершин кожної плитки?


Ви також можете використовувати криву Гільберта, вони розташовують між собою кривих подачі, щоб суміжність у площині зберігалася в лінійному кодуванні. перевірити просторову індексацію та як вони використовуються! v цікаво
pbordeaux

Відповіді:


156

Аміт Патель опублікував дивовижну сторінку на цю тему. Це настільки всебічно і чудово, що на це питання має бути остаточна відповідь: Шестикутні сітки

куб


27
Дякую :) Ця сторінка не охоплює краї та вершини, але я висвітлюю їх у розділі "Зв'язок між частинами" моєї статті сітки на веб- сайті www-cs-students.stanford.edu/~amitp/game-programming/grids (діаграми: для квадратних сіток, але таблиця містить також формули для осьових шестигранних сіток)
amitp

18

Така сітка може бути представлена ​​у двовимірному масиві:

Якщо

   2
7     3
   1   
6     4
   5

- це номер один із сусідами в шістнадцятковій сітці, тоді ви можете помістити це у 2D масив так:

2 3
7 1 4
  6 5

Очевидно, сусідність визначається в цій сітці не тільки через горизонтальне або вертикальне примикання, але і за допомогою однієї діагоналі.

Ви також можете використовувати графік, якщо хочете.


Класно. Що з даними для ребер і вершин?
платний ботанік

1
Я б, мабуть, зберігав їх окремо. Незалежно від того, чи ви в першу чергу дивитесь на плитки чи краї / вершини, друга половина даних болісно або надмірно зберігати.
Joey

Дивіться статтю Аміта Пателя у відповіді "платний ботанік".
aredridel

11

У цій статті йдеться про те, як налаштувати гра з ізомерною / шестикутною сіткою. Рекомендую ознайомитись із Forcing Isometric and Hexagonal Maps onto a Rectangular Gridсекцією та секцією руху. Хоча він відрізняється від того, що ви шукаєте, він може допомогти вам сформулювати, як робити те, що ви хочете.


2

Я багато мав справу з гексами. У таких випадках ви відстежуєте кожен з 6 пунктів для меж шестигранника. Це дозволяє намалювати його досить легко.

У вас був би один масив об'єктів, що представляють шестигранники. Кожен з цих шестигранних об'єктів також має 6 "покажчиків" (або індекс до іншого масиву), що вказують на інший масив "сторін". Те ж саме для "вершин". Звичайно, вершини мали б 3 вказівника на сусідні шестигранники, а сторони мали б 2.

Отже, шестигранник може бути чимось на зразок: X, Y, точка (6), вершини (6), сторони (6)

Тоді у вас є шестигранний масив, вершинальний масив та бічний масив.

Тоді досить просто знайти вершини / сторони для шестигранника чи будь-чого іншого.

Коли я кажу вказівник, він може так само легко бути цілим числом, що вказує на елемент вершини або бічний масив чи будь-що інше. І звичайно масиви можуть бути списками чи будь-якими іншими.


0
   2
7     3
   1   
6     4
   5

Ви також можете спробувати "плоскі" рядки карти. Для цього прикладу було б:

  2
7 1 3
6 5 4

Іноді корисніше мати рядки в одному ряду: P


1
Це може мати якийсь безладний код перевірки сусідів, оскільки, наприклад, 1 і 6 є сусідами, але 3 і 5 - ні, але вони мають однакові відносні позиції.
Бернхард Баркер

0

Я б запропонував щось на зразок наступного (я буду використовувати декларації у стилі 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;

Кожна шестигранна шість ребер і шість вершин. Кожен край відслідковує два сусідніх шестигранника, а кожна вершина відстежує свої три сусідні шестигранники (шестигранники на краях карти будуть окремим випадком).

Є багато речей, які можна було б зробити по-різному. Ви можете використовувати вказівники, а не масиви, ви можете використовувати об'єкти, а не записи, і ви можете зберігати свої шістнадцятки в двовимірному масиві, як запропонували інші відповіді.

Сподіваємось, це може дати вам кілька ідей щодо одного із способів наблизитись до цього.


0

Ми реалізували Settlers of Catan AI для класового проекту та модифікували код із цієї відповіді (що був помилкою), щоб створити Дошку з постійним випадковим доступом до вершин і країв. Це було цікавою проблемою, але рада займала багато часу, тому у випадку, якщо хтось все ще шукає просту реалізацію, тут є наш 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

Ця відповідь жахлива. Ви просто вставили тонни коду, нічого не пояснюючи (крім того, хто написав код). Навіть якщо це було нормально, сам код жахливий. Немає жодних докторських записів, майже немає коментарів, а кілька включених коментарів не зрозумілі (Логіка з думки, що це говорить getEdgesOfVertex, а потім для кожного краю getVertexEnds, виймаючи три, що є == вершиною).
Карл Сміт

0

Я сиджу тут, «у вільний час кодуючи для розваги» гексами. І йде так ... Я розповім, як це виглядає на словах.

  1. Шестикутник: він має шість сусідніх шестикутників. Він може доставити посилання на кожну сусідню шестигранну плитку. Він може сказати вам, з чого він складається (вода, скеля, пил). Він може з'єднати себе з іншими і навпаки. Він навіть може автоматично з'єднати оточуючих його, щоб створити більше поле і, переконавшись, що всі поля можуть бути адресовані сусідами.
  2. Будівля посилається на три дороги та три шестигранні плитки. Вони можуть вам сказати, які вони.
  3. Дорога посилається на два шестигранні та інші дороги, коли на них кладуть сусідні плитки. Вони можуть сказати, які плитки є і до яких доріг чи будівель вони підключаються.

Це лише ідея, як я би над цим працював.


0

Ви можете створити 2D масив, а потім вважати дійсні позиції такими:

  • На парних рядках (0,2,4, ...): непарні пронумеровані комірки.
  • На рядках з непарними номерами (1,3,5, ...): парні комірки з парними номерами.

Для кожної клітини її сусідами були:

  • Та сама колонка, 2 ряди вгору
  • Та сама стовпець, 2 ряди вниз
  • 1 зліва + 1 вгору
  • 1 зліва + 1 вниз
  • 1 справа + 1 вгору
  • 1 справа + 1 вниз

Ілюстрація: Шестигранна сітка

Знаки х - це шістдесят. х, які діагональні між собою, є сусідами. | з'єднує вертикальних сусідів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.