1

我一直在为我正在研究的模拟创建一个六边形(平顶)网格。我试图从指定的目标六边形算出六边形之间的距离。

我的解决方案大部分时间都有效,除了目标以北目标六边形的每个奇数列都向上移动 1。我知道这听起来令人困惑,但我附上了一张图片来解释我的意思:

六边形游戏网格

正如你们所看到的,目标六边形下方网格的下半部分和目标六边形上方的所有其他列都是正确的。我不明白为什么:S

这是对轴向和立方体坐标的解释。

http://www.redblobgames.com/grids/hexagons/#coordinates

这是负责将轴向坐标转换为立方体坐标的代码。

public void setQR(int theQ, int theR){

    this.q = theQ;
    this.r = theR;

    this.x = this.q;
    this.z = this.r - (this.q - (this.q&1)) /2;
    this.y = -(this.x + this.z);
}

这是计算距离的代码。

仅供参考,六边形是从中心点(CPx,CPy)创建的。

    private double distance = 0;

public double workOutDistance(Hexagon hexagon, HexagonFood target){

    double targetX = target.getCPX();
    double targetY = target.getCPY();

    double hexagonX = hexagon.getCPX();
    double hexagonY = hexagon.getCPY();

    double deltaX = (targetX-hexagonX)*-1;
    double deltaY = (targetY-hexagonY)*-1;

    double deltaXRadius = (deltaX/(SimField.hexSize)/1.5);
    double deltaYApothem = (deltaY/(SimField.hexSize/1.155)/2);

    hexagon.setQR((int)deltaXRadius, (int)deltaYApothem);


    ArrayList<Integer> coords = new ArrayList<>();

    coords.add(
    Math.abs(hexagon.getX() - target.getX())
    );

    coords.add(
    Math.abs(hexagon.getZ() - target.getZ())
    );

    coords.add(
    Math.abs(hexagon.getY() - target.getY())
    );

    System.out.println(coords);
    distance = Collections.max(coords);

    return distance;
}

谁能告诉我为什么会这样?将不胜感激。

编辑:

按照 Tim 的建议将 Int 更改为 Double 后,我明白了。

http://i.stack.imgur.com/javZb.png

**

解决方案

**

在尝试了给出的答案之后,这个小调整解决了这个问题。

改变这个..

public void setQR(int theQ, int theR){

    this.q = theQ;
    this.r = theR;

    this.x = this.q;
    this.z = this.r - (this.q - (this.q&1)) /2;
    this.y = -(this.x + this.z);
}

到这个..

public void setQR(int theQ, int theR){

    this.q = theQ;
    this.r = theR;

    this.x = this.q;
    if (this.r>0){
        this.z = this.r - (this.q - (this.q&1))/2;
    }
    else {
        this.z = this.r - (this.q + (this.q&1))/2;
    }
    this.y = -(this.x + this.z);
}
4

3 回答 3

3

调用 setQR(); 时,您将 double 转换为 int;你确定那是你所期望的吗?双打使用浮点数学,因此您期望为 2.0 的数字实际上可能是 1.999999989,然后在转换为 int 时会向下舍入为 1。

我也对读取的行持怀疑态度this.z = this.r - (this.q - (this.q&1)) /2;。当数字为奇数时,您加 1,这似乎是您遇到的失败案例;我会确保这条线也在做你所期望的。

如果您没有使用调试器逐步执行此操作并检查值,那么您做错了。

于 2014-07-15T19:13:24.570 回答
1

您也可以对这个问题采取完全不同的方法。您知道两个六边形的 X/Y(笛卡尔)坐标,这意味着您可以获得每个六边形相对于六边形空间原点的三次坐标。两个六边形之间的距离只是两个六边形的 X、Y 和 Z 三次坐标之间差异的绝对值之和。(也就是说,dist = |h2.X - h1.X| + |h2.Y - h1.Y| + |h2.Z - h1.Z|)因此,与其尝试计算两个中心点之间的向量,然后将其转换为三次坐标,不如直接在三次坐标中计算距离(就像这些是笛卡尔坐标中的正方形一样)。 .

但是,即使您采用这种方法,我强烈建议您调试原始方法的情况。即使您最终放弃了代码,调试练习也可能会教给您宝贵的经验教训,您将来可以应用这些经验教训。

读者注意:“立方”坐标不是 3 维笛卡尔坐标,它们是 OP 提供了链接的六边形特定坐标系。

于 2014-07-15T20:52:20.010 回答
0

计算(即从偏移到立方坐标的转换,以及立方坐标中距离的计算)似乎是正确的这一事实表明蒂姆对浮点误差的假设是正确的。

您应该尝试更改线路

hexagon.setQR((int)deltaXRadius, (int)deltaYApothem);

从您的原始代码到类似

hexagon.setQR((int)Math.round(deltaXRadius), (int)Math.round(deltaYApothem));

在这种情况下可以解决问题。

如果不是......或者......无论如何,这是一个小例子,基本上和你做的一样,但是作为一个MVCE......

import java.awt.Point;

public class HexagonsTest
{
    public static void main(String[] args)
    {
        // Above and below
        test(8,6, 8,5,  1);
        test(8,6, 8,7,  1);

        // Left
        test(8,6, 7,5,  1);
        test(8,6, 7,6,  1);

        // Right
        test(8,6, 9,5,  1);
        test(8,6, 9,6,  1);

        // The first one that was wrong:
        test(8,6, 7,4,  2);
    }

    private static void test(int x0, int y0, int x1, int y1, int expected)
    {
        int distance = computeStepsDistance(x0, y0, x1, y1);
        System.out.println(
            "Distance of (" + x0 + "," + y0 + ") to " + 
            "(" + x1 + "," + y1 + ") is " + distance + 
            ", expected " + expected);
    }

    private static int computeStepsDistance(int x0, int y0, int x1, int y1)
    {
        Point cp0 = convertOffsetToCubeCoordinates(x0, y0, null);
        Point cp1 = convertOffsetToCubeCoordinates(x1, y1, null);
        int cx0 = cp0.x;
        int cy0 = cp0.y;
        int cz0 = -cx0-cy0;
        int cx1 = cp1.x;
        int cy1 = cp1.y;
        int cz1 = -cx1-cy1;
        int dx = Math.abs(cx0 - cx1); 
        int dy = Math.abs(cy0 - cy1); 
        int dz = Math.abs(cz0 - cz1); 
        return Math.max(dx, Math.max(dy, dz));
    }

    private static Point convertOffsetToCubeCoordinates(
        int ox, int oy, Point p) 
    {
        int cx = ox;
        int cz = oy - (ox - (ox&1)) / 2;
        int cy = -cx-cz;
        if (p == null)
        {
            p = new Point();
        }
        p.x = cx;
        p.y = cy;
        return p;
    }


}
于 2014-07-15T20:09:57.253 回答