给定一个具有位置、初始旋转和目标点以“面向”的实体,找到用于旋转的角度差。

3
我正在使用Java编写一种群集算法,但是我在某个点上卡住了(在2D平面中使用Ardor3D库)。
基本上,我需要找到要添加到当前旋转的角度差异。如果您只能获取极坐标下应该指向的方式,其中0度为北,而不是差异,请不要担心 - 我有一种方法,该方法考虑了角度和负角度的环绕。
目前,我有以下代码,显然不能工作,因为算法没有参考初始旋转:
  long tpf = currUpdateTimeMS - lastUpdateTimeMS;

  Vector2 pos = new Vector2();
  rt.getPosition(pos);

  double rot = pos.angleBetween(app.getAvgBoidPos(new Vector2()).normalizeLocal());
  rt.setRotation(rot);

  pos.addLocal(
   Math.cos((rot - MathUtils.HALF_PI)) * (tpf / 10f),
   Math.sin((rot - MathUtils.HALF_PI)) * (tpf / 10f)
  );
  rt.setPosition(pos);

  super.updateLogic();

更新后的代码(不起作用,来自第一个答案):

    long tpf = currUpdateTimeMS - lastUpdateTimeMS;
    //rt.setRotation(rt.getRotation() + ((tpf / (ROT_SPEED / 2f)) % 360));

    Vector2 avgpos = app.getAvgBoidPos(new Vector2());
    Vector2 pos = rt.getPosition(new Vector2());
    avgpos.subtractLocal(pos);

    double angleRads = rt.getRotation() * FastMath.DEG_TO_RAD;
    double rot = MathUtils.acos((
        (avgpos.getX() * MathUtils.sin(angleRads)
    ) +
        (avgpos.getY() * MathUtils.cos(angleRads)
    )) / ((Math.pow(avgpos.getX(), 2) + Math.pow(avgpos.getY(), 2)) * 0.5));

    double adegdiff = rot * FastMath.RAD_TO_DEG;

    rt.setRotation(rt.getRotation() - adegdiff);
    double newrot = rt.getRotation();

    pos.addLocal(
        Math.cos((newrot - MathUtils.HALF_PI)) * (tpf / 10f),
        Math.sin((newrot - MathUtils.HALF_PI)) * (tpf / 10f)
    );
    rt.setPosition(pos);

    super.updateLogic();

基于其他答案的另一种修改方式:
    long tpf = currUpdateTimeMS - lastUpdateTimeMS;
    //rt.setRotation(rt.getRotation() + ((tpf / (ROT_SPEED / 2f)) % 360));

    Vector2 avgpos = app.getAvgBoidPos(new Vector2());
    Vector2 pos = rt.getPosition(new Vector2());
    avgpos.subtractLocal(pos);

    double rot = pos.angleBetween(
        app.getAvgBoidPos(new Vector2()).normalizeLocal()
    ) - (rt.getRotation() * MathUtils.DEG_TO_RAD);

    rt.setRotation(rt.getRotation() - (rot * MathUtils.RAD_TO_DEG));
    double newrot = rt.getRotation();

    pos.addLocal(
        Math.cos((newrot - MathUtils.HALF_PI)) * (tpf / 10f),
        Math.sin((newrot - MathUtils.HALF_PI)) * (tpf / 10f)
    );
    rt.setPosition(pos);

    super.updateLogic();

我不太擅长数学问题,所以编程代码比公式更有帮助 :)

输入

  • 实体的当前位置
  • 实体的当前旋转角度(极坐标定向)以度为单位

输出

  • 要添加或减去到当前旋转角度的度数或弧度
  • ... 或表示为极坐标定向角度的度数或弧度

如果您能提供帮助,谢谢!

克里斯


那么 rt.getPosition 给出实体的位置,而 app.getAvgBOIDPos 给出目标位置,是这样吗?那么什么可以给出实体的方向呢? - Beta
那就是 rt.getRotation(),它以极向度表示 :) - Chris Dennett
抱歉,忘了说你是对的,app.getAvgBoidPos()会提供实体的平均位置,以便实体向其移动。 - Chris Dennett
4个回答

2
你可以使用点积来确定当前方向和你想要面对的点之间的余弦角度(以弧度为单位)。假设查看的代理位于原点,并朝某个方向朝向给定的角度θ相对于Y轴(即0度是“上”或“北”)。您想要找到该方向与面对点(x,y)之间的角度差Θ'。那是由以下公式给出的:
θ' = cos-1[(x*sin(θ) + y*cos(θ)) / sqrt(x2 + y2)]
从θ中减去θ'将使代理朝向目标。
如果代理不在原点,则只需从要查看的对象中减去代理的位置即可将其转换为上述形式。

明白了,我会尝试实现这个并且如果有任何问题会回来找你的 :) - Chris Dennett
我已经实现了它,但似乎没有起作用。我认为位置的x和y元素由于某处除以零而变成NaN了?无论如何,我已经使用新代码更新了问题 :) - Chris Dennett
@Chris Dennett:我觉得我试图过于聪明,结果适得其反。你将分母乘以1/2。我试图将其表达为升至1/2次幂,即平方根。对于某些x和y的值,这可能导致商不在[-1,1]范围内,当你对其进行acos时会导致NaN。我已更新我的方程以更好地显示这一点。 - andand
我解决了这个问题,原来是我使用的角度差函数有点简单。但是这篇文章因为最接近实现方案而被标记为答案 :) - Chris Dennett
你的解决方案是什么? - Hobbes
如果我只有源位置、当前方向和目标位置,如何计算 θ - undefined

1

顺便说一下,这并不完全是离题的,因为我想要实现的是群集。"找到平均位置"元素是其中的一部分,我已经让它工作了。但是问题还有其他方面,似乎在这里已经解决了 :) - Chris Dennett
唯一的问题是粒子代码没有旋转和速度,我可以用它来做 - 它只有在x、y和z方向上的速度。但是将它们之间进行转换不应该很难。如果我将速度作为旋转和速度的乘积(我已经有了公式),那么应该没问题。 - Chris Dennett

0

这里提供了一种实现方式,用于寻找两个具有共同起点的向量之间的角度。该方法基于以下算法拼凑而成: http://www.wikihow.com/Find-the-Angle-Between-Two-Vectors

public class DeltaDoodle {

    private double delta(ColumnVector v1,ColumnVector v2) throws Exception{
        double sp=scalarProduct(v1,v2);
        double magV1=magnitude(v1);
        double magV2=magnitude(v2);
        return Math.acos(sp/(magV1*magV2)) * (180/Math.PI);
    }

    private double scalarProduct(ColumnVector a, ColumnVector b) {
        return (a.x*b.x) + (a.y*b.y);
    }
    private double magnitude(ColumnVector a){
        return Math.sqrt((a.x*a.x) + (a.y*a.y));
    }

    public static void main(String[] args) {
        DeltaDoodle v=new DeltaDoodle();
        try {
            System.out.println("angle: " + v.delta(new ColumnVector(5, 5), new ColumnVector(1,1)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

public class ColumnVector {
    public final double x, y;

    public ColumnVector(double x1, double x2) {
        this.x = x1;
        this.y = x2;
    }
}

希望这有所帮助...


0

如果我正确理解这些函数,这可能会起作用:

Vector2 pos = new Vector2(); 
rt.getPosition(pos); 

double rot = pos.angleBetween(app.getAvgBoidPos(new Vector2()).normalizeLocal()) - rt.getRotation();

附言:忘了提一下:这个rot是指角度差异。将实体旋转这么多,它应该指向目标。

编辑:
谢谢,这段代码确实有帮助(我误解了angleBetween)。让我再试一次:

这是从实体到点的向量(如果我语法错误,请原谅,我不懂Java):

Vector2 pos = new Vector2(); 
rt.getPosition(pos); 

Vector2 direction = app.getAvgBoidPos(new Vector2());
direction.subtractLocal(pos);

现在我们对其进行归一化,得到一个指向该点的单位向量,并计算角度差:
double rot = rt.getRotation().angleBetween(direction.normalizeLocal())

无法工作 :/ 如果有帮助的话,这是Vector2源代码:http://ardorlabs.trac.cvsdude.com/Ardor3Dv1/browser/trunk/ardor3d-core/src/main/java/com/ardor3d/math/Vector2.java?rev=152 - Chris Dennett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接