メモリ内でヘクタイル/ヘックスグリッドを表すにはどうすればよいですか?


118

カタンの開拓者のように、ヘクタイルグリッドでボードゲームを構築しているとします。

imgur.comがホスト

各頂点とエッジには属性(上記の道路と集落)がある場合があることに注意してください。

このボードを表すデータ構造をどのように作成しますか?各タイルの隣接、エッジ、および頂点にアクセスするためのパターンは何ですか?


ヒルベルト曲線を使用することもできます。これらは、平面の隣接性が線形エンコーディングで維持されるように間隔をあけたファイリング曲線です。空間インデックスとそれらの使用方法を確認してください!v面白い
pbordeaux 2018

回答:


155

Amit Patelがこのトピックに関する素晴らしいページを投稿しました。それは非常に包括的で素晴らしいので、この質問に対する決定的な答えである必要があります:六角形グリッド

キューブズ


27
おかげで:)そのページはエッジと頂点をカバーしていませんが、www-cs-students.stanford.edu /〜amitp / game-programming / grids(図は正方グリッドの場合、ただし表には軸六角グリッドの数式も含まれます)
amitp

18

このようなグリッドは、2次元配列で表すことができます。

もし

   2
7     3
   1   
6     4
   5

は、16進グリッド内で隣接するナンバー1であり、これを次のように2D配列に入れることができます。

2 3
7 1 4
  6 5

明らかに、隣接性は、このグリッドで水平方向または垂直方向に隣接するだけでなく、1つの対角線を使用して決定されます。

ただし、必要に応じてグラフを使用することもできます。


涼しい。エッジと頂点のデータはどうですか?
有料オタク

1
私はおそらくそれらを別々に保管するでしょう。主にタイルを見るのか、それともエッジ/頂点を見るのかに関係なく、データの残りの半分は、保存するのが面倒であるか冗長です。
Joey、

「有料のオタク」の回答のAmit Patelの記事を参照してください。
aredridel 2013年

11

この記事では、Isomeric / Hexagonalグリッドゲームをセットアップする方法について説明します。Forcing Isometric and Hexagonal Maps onto a Rectangular Gridセクションとムーブメントセクションをご覧になることをお勧めします。それはあなたが探しているものとは異なりますが、それはあなたが望むことをする方法を策定するのに役立つかもしれません。


2

私はヘクスをたくさん扱いました。このような場合、ヘクスの境界の6つのポイントをそれぞれ追跡します。これにより、非常に簡単に描画できます。

ヘクスを表すオブジェクトの単一の配列があります。これらの16進オブジェクトのそれぞれには、「辺」の別の配列を指す6つの「ポインター」(または別の配列へのインデックス)もあります。「頂点」についても同様です。もちろん、頂点には隣接するヘクスへの3つのポインターがあり、側面には2つあります。

つまり、ヘクスはX、Y、Point(6)、Vertices(6)、Sides(6)のようなものになります。

次に、Hex配列、vertice配列、side配列があります。

次に、ヘクスなどの頂点/辺を見つけるのは非常に簡単です。

私がポインタと言うとき、それは頂点またはサイド配列または何でも要素を指す整数と同じくらい簡単になりえました。そしてもちろん、配列はリストなど何でもかまいません。


0
   2
7     3
   1   
6     4
   5

マップの行を「フラット」にすることもできます。この例では、次のようになります。

  2
7 1 3
6 5 4

1つの行に行を置くと、より便利になる場合があります。P


1
たとえば、1と6は隣接しているが、3と5はそうではないが、相対的な位置は同じであるため、これには厄介な隣接チェックコードが含まれる可能性があります。
Bernhard Barker

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;

各ヘクスには6つのエッジと6つの頂点があります。各エッジは2つの隣接するヘクスを追跡し、各頂点は3つの隣接するヘクスを追跡します(マップのエッジ上のヘクスは特殊なケースになります)。

もちろん、別の方法でできることはたくさんあります。配列ではなくポインタを使用したり、レコードではなくオブジェクトを使用したり、他の回答者が提案したように2次元配列に16進数を格納したりできます。

うまくいけば、それはあなたにそれに取り組む一つの方法についていくつかの考えを与えるかもしれません。


0

クラスプロジェクトにカタン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

この答えはひどいです。何も説明せずに大量のコードを貼り付けたところです(誰がコードを書いたかを除いて)。ここで問題がなくても、コード自体は恐ろしいものです。docstringがなく、ほとんどコメントがなく、含まれているいくつかのコメントは理解不能です(これはgetEdgesOfVertexと言っていると考えてから、各エッジに対してgetVertexEnds、== vertexの3つを取り出します)。
カールスミス

0

私はここに座って、ヘックスで「楽しみのためにコーディングする自由時間」にいます。こんな感じで…どんな感じか教えてあげます。

  1. 六角形:隣接する六角形が6つあります。隣接する各ヘックスタイルのリファレンスを提供できます。それはそれが何から構成されているか(水、岩、ほこり)を教えてくれます。自分自身を他の人に接続したり、その逆を行うことができます。それは自動的に彼を取り巻く他のものを接続してより大きなフィールドを作成したり、あるいはすべてのフィールドがその隣人に対処できるようにすることさえできます。
  2. 建物は最大3つの道路と3つの六角タイルを参照します。彼らはあなたがどちらであるかをあなたに伝えることができます。
  3. 道路は、2つのヘクスと他の道路が隣接するタイルに対応しているときに参照します。彼らは、どのタイルであり、どの道路や建物につながっているかを知ることができます。

これは、私がそれに取り組む方法のアイデアにすぎません。


0

2D配列を作成し、有効な位置を次のように考えることができます。

  • 偶数番号の行(0、2、4、...):奇数番号のセル。
  • 奇数行(1,3,5、...):偶数番号のセル。

各セルの隣接セルは次のとおりです。

  • 同じ列、2行上
  • 同じ列、2行下
  • 左1つ+上1
  • 左1 +下1
  • 右1つ+上1
  • 1右+ 1ダウン

イラスト: 六角グリッド

xマークは16進数です。互いに対角であるxは近傍です。| 垂直方向の隣接セルを接続します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.