【问题标题】:Calculating distance on a hexagon grid在六边形网格上计算距离
【发布时间】:2013-01-23 23:40:39
【问题描述】:

我想要做的是找出六边形网格上两点之间有多少个六边形。我尝试在网上搜索公式,但找不到与我正在使用的十六进制网格类型匹配的公式。

十六进制网格布局如下,坐标系相同:http://www.gamedev.net/index.php?app=core&module=attach&section=attach&attach_rel_module=ccs&attach_id=1962

我知道使用此坐标系可能无法做到这一点,但这是我回去更改它之前的最后努力。 非常感谢您。

【问题讨论】:

  • 您指的是以六角形为中心的点吗?换句话说,您是否试图找出 [0,0] 和 [3,3] 之间有多少?
  • 是的,这正是我想要找到的
  • 之间怎么定义?两点之间的十六进制集合不是唯一的
  • 对不起@thang 我不是 100% 确定你的意思。
  • 您是否正在寻找两个六角形之间的最短路径的步数,同时只向左、向右或对角线行走?还是您在寻找更复杂的东西?

标签: hexagonal-tiles


【解决方案1】:

如果您使用的坐标系在两个方向上沿着六边形的纹理,您可以使用:

distance = max(
     abs(dest.y - start.y),     
     abs(dest.x - start.x),
     abs((dest.x - dest.y)*-1 - (start.x - start.y)*-1)
)

但是您没有这样做,您使用的是波浪形坐标系,该坐标系仅沿一个方向(水平)与纹理一起使用。幸运的是,我们可以在两者之间转换使用

straight.y = squiggle.y
straight.x = ciel(squiggle.y / -2) + squiggle.x

所以,使用这个方程组求解距离可以:

distance = max(
     abs(dest.y - start.y),     
     abs(ceil(dest.y / -2) + dest.x - ceil(start.y / -2) - start.x),
     abs(-dest.y - ceil(dest.y / -2) - dest.x + start.y  + ceil(start.y / -2) + start.x)
)

这将使您仅使用它们的坐标来获得两个六角形之间的曼哈顿距离(假设我没有将 x 和 y 进行任何拼写错误,因为您的网格与我的网格旋转了 90 度)。但是你必须为我的中学代数老师买一块饼干才能让它起作用,否则我会搞砸分配属性。

注意:可能需要摆弄负坐标,我没有检查。

【讨论】:

    【解决方案2】:

    感谢@user2709663 和@jonathankoren 提供答案。我花了很多时间来回答你的问题,但发现这两个都有一些问题。或者至少没有明确说明为这些答案考虑的网格类型。然而,我找到了一个非常好的教程和代码实现,以及一个用于管理十六进制网格的库:http://www.redblobgames.com/grids/hexagons/(库代码:http://www.redblobgames.com/grids/hexagons/implementation.html)。我还为“odd-q”垂直布局实现了一个matlab版本的距离代码,如下所示:

    function f = offset_distance(x1,y1,x2,y2)
        ac = offset_to_cube(x1,y1);
        bc = offset_to_cube(x2,y2);
        f = cube_distance(ac, bc);
    end
    
    function f = offset_to_cube(row,col)
        %x = col - (row - (row&1)) / 2;
        x = col - (row - mod(row,2)) / 2;
        z = row;
        y = -x-z;
        f = [x,z,y];
    end
    
    function f= cube_distance(p1,p2)
        a = abs( p1(1,1) - p2(1,1));
        b = abs( p1(1,2) - p2(1,2));
        c = abs( p1(1,3) - p2(1,3));
        f =  max([a,b,c]);
    end
    

    这是一个matlab测试代码

    sx = 6;
    sy = 1;
    for i = 0:7
        for j = 0:5
            k = offset_distance(sx,sy,i,j);
            disp(['(',num2str(sx),',',num2str(sy),')->(',num2str(i),',',num2str(j),')=',num2str(k)])
        end
    end
    

    【讨论】:

      【解决方案3】:

      The accepted answer 不正确。当它提到在非正交轴上使用正交坐标时,我最初对此表示怀疑,但针对我自己的解决方案运行代码表明距离复合的过度估计。

      实际正确的解决方案是:

      def hexDistance(start, dest):
        if (start.x == dest.x):
          return abs(dest.y - start.y)
        elif (start.y == dest.y):
          return abs(dest.x - start.x)
        else:
          dx = abs(dest.x - start.x)
          dy = abs(dest.y - start.y)
          if start.y < dest.y:
            return dx + dy - int(math.ceil(dx / 2.0))
          else:
            return dx + dy - int(math.floor(dx / 2.0))
      

      这使用十六进制->正方形表示:

      ------ ------ 0, +1 ------ -1, +1 ------ +1, +1 ------ 0, 0 ------ -1, 0 ------ +1, 0 ------ 0, -1 ------ ------ -------------------------- | -1, +1 | 0, +1 | +1,+1 | |-------------------------- | -1, 0 | 0, 0 | +1, 0 | |--------------------------| | | 0, -1 | | --------------------------

      【讨论】:

      • 这为对角向下的字段提供了过高的值,不幸的是给定一个普通的十六进制字段
      【解决方案4】:

      要找到两个十六进制之间的最短路径:

      1. 从一个十六进制开始,
      2. 在不同的行上时,沿着对角线朝向另一行。
      3. 在同一行时,直奔另一个格子。

      我们将 x 方向的差异称为 dx 和 y 方向的差异 dy。如果dy / 2 &gt; dx,就不用做第二步了,所以距离简直就是dy。否则,距离为dy + (dx - dy / 2)。除非我犯了错误。

      【讨论】:

        【解决方案5】:

        M H Rasel 在他之前的回答中链接了这篇文章:Hexagonal Grids。在这篇出色的帖子之后,我发现我需要立方体坐标;这提供了计算距离的最简单方法。这是 Kotlin 中的代码 sn-p:

        data class Point(val x: Int, val y: Int, val z: Int) {
        
            fun distance(b: Point): Int {
                return (abs(x - b.x) + abs(y - b.y) + abs(z - b.z)) / 2
            }
        
        }
        
        var x = 0
        var y = 0
        var z = 0
        
        val p1 = Point(x, y, z)    // starting position
        
        val steps = "ne,ne,ne".split(",")    // go to North-East 3 times
        
        for (direction in steps) {
            when(direction) {
                "n"  -> { ++y; --z }
                "ne" -> { ++x; --z }
                "se" -> { ++x; --y }
                "s"  -> { --y; ++z }
                "sw" -> { --x; ++z }
                "nw" -> { ++y; --x }
            }
        }
        
        val p2 = Point(x, y, z)    // we arrived here
        val dist = p1.distance(p2)    // the result is here (in this example: 3)
        

        编辑:我假设是一个平顶六角网格。

        【讨论】:

          【解决方案6】:

          这是一个次优但不是太次优(应该是 O(n))的算法:

          首先,制作一个函数来确定六边形网格中某个位置的六边形是否与具有某个起点和终点的线段相交(例如,计算它所组成的六条线并执行类似的操作:http://alienryderflex.com/intersect/。)

          其次,做一个函数来确定一个点在六边形网格上的哪个六边形。

          现在,这样写你的算法:

          • 保留到目前为止线段重叠的所有六边形的列表
          • 从线段起点所在的六边形开始
          • 对于最近重叠的六边形周围的每个六边形,如果它不在列表中,请查看 lin 段是否与该六边形相交。如果是这样,请将其设为列表的新标题并重复
          • 如果我们用完了要测试的六边形,请返回我们制作的列表

          我建议您测试一条线段与六边形之间的接缝完全平行并沿着该接缝延伸的情况,看看您是否在一侧、两侧或都没有(因此停止)得到六边形。

          【讨论】:

            【解决方案7】:

            如果网格上的图块可能会被阻塞,那么您对 ​​A*(或 A-Star)迷宫求解算法感兴趣: http://labs.makemachine.net/2010/03/a-star-maze-solver/

            本视频中使用的机制适用于正方形网格,但几乎无需任何额外编码即可将其应用于六边形网格。 对于每个图块,求解器知道接下来要尝试哪些图块,因为图块存储指向其周围图块的指针。在正方形网格中,每个图块最多可存储 4 个指针(maximum,因为它们只存储指向未阻塞图块的指针),您的情况唯一不同的是最多存储 6 个指针。

            如果图块总是可遍历的,那么 A* 肯定仍能完成工作,但可能有更快的方法。如果我正确地解释了您的问题,您感兴趣的不是距离,而是两个给定十六进制之间的十六进制数的整数计数?请尝试以下操作:

            class Coord {
                int x;
                int y;
            }
            
            int dist(Coord hex1, Coord hex2) {
                int xSteps = Math.abs(hex1.x - hex2.x);
                int ySteps = Math.abs(hex1.y - hex2.y);
            
                return Math.max(xSteps, ySteps) + Math.abs(xSteps - ySteps);
            }
            

            为什么你会问?这个问题是关于确定我们必须垂直水平而不是对角移动多少次。我们想尽可能多地沿对角线移动,否则我们对距离并不聪明。 Math.abs(xSteps - ySteps) 是我们必须进行的非对角线移动的数量。再加上 x 和 y 的距离中的较大者,就完成了。

            【讨论】:

            • 这似乎不起作用,因为当我使用坐标 0,0 和 3,3 时,返回的距离是 3 而不是 4。
            【解决方案8】:

            如果您的六边形平铺有方向:N、NE、SE、S、SW、NW,如Advent of Code 2017 problem 11 并且您将目标偏移到 (0,0)(通过预先从目标中减去您的位置)以下逻辑对我有用:

            def distance(self):
                # Take diagonal steps towards self.x == 0
                steps = abs(self.x)
                # y moves closer to 0 by steps because moving diagonal, but never moving too far
                if self.y > 0:
                    # Might still be positive, but never negative
                    y = max(self.y - steps, 0)
                else:
                    # Might still be negative, but not positive
                    y = min(self.y + steps, 0)
                # You move 2 positions north/south by a single move so divide y's by 2
                return abs(y) // 2 + abs(steps)
            

            我认为它可能适用于具有 EAST 和 WEST 方向的六角网格,而不是像你的那样的 NORTH 和 SOUTH,只需切换 x 和 y 的角色。在这种情况下,您将朝着 self.y == 0 对角线移动(如果需要)等。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-11-24
              • 2020-08-06
              • 2011-07-02
              • 1970-01-01
              • 2014-09-15
              相关资源
              最近更新 更多