使用Math.atan2导航导弹故障

时间:2012-08-09 21:14:08

标签: algorithm trigonometry game-physics

我正在研制一种用于Android中水平滚动,太空射击游戏的导弹导弹。我无法从我正在使用的算法中获得所需的行为。我想让导弹从玩家的船上水平射出,然后逐渐回到目标上,如果弧太大,有机会错过目标。它起作用,除非导弹错过了目标,在这种情况下,它通常会试图沿着正弦波的路径行进,直到它从屏幕的右侧流出。我想要的是,导弹试图在其当前曲线(如定型导弹)中绕目标绕圈,直到它击中精灵或跑出屏幕边缘。我可能会添加一个限制,以便它会爆炸并且不会继续在屏幕上旋转,但如果我开始工作,这将是一个后来的补充。

以下是代码示例:

public class HomingMissile extends Shot
{
    Bitmap bitmap;
    Rect sourceRect;
    Rect destRect;

    private double heading;

    private Sprite target;

    private int frameNumber;
    private int currentFrame;
    private int frameDelta;

    public HomingMissile(Context context, ImageLoader imageLoader, int x,
        int y, int minX, int minY, int maxX, int maxY, int dx, int dy)
    {
        super(context, imageLoader, x, y, minX, minY, maxX, maxY, dx, dy);

        heading = 0;

        frameNumber = 3;
        currentFrame = 0;
        frameDelta = 1;

        target = new Sprite(context, imageLoader, 300, 50, minX, minY, 
            maxX, maxY, 0, 0);
    }

    @Override
    public void setBitmap(int id)
    {
        bitmap = imageLoader.GetBitmap(id);

        width = bitmap.getWidth();
        height = bitmap.getHeight() / frameNumber;

        sourceRect = new Rect(0, 0, width - 1, height);
        destRect = new Rect(X, Y, X + width - 1, Y + height - 1);
    }

    public void setTarget(Sprite sprite)
    {
        target = sprite;
    }

    @Override
    public void Move()
    {
        if (!visible)
            return;

        final double f = 0.03;
        double oldHeading = heading;

        double atanY = target.Y + (target.height / 2) - Y;
        double atanX = target.Y + target.X - X;

        heading = (1 - f) * oldHeading + f * Math.atan2(atanY, atanX);

        X += Math.cos(heading) * 10;
        Y += Math.sin(heading) * 10;

        UpdateBounds();

        if (currentFrame == frameNumber - 1)
            frameDelta = -frameDelta;

        if (currentFrame < 0)
        {
            frameDelta = -frameDelta;
            currentFrame += frameDelta;
        }

        sourceRect.top = height * currentFrame;
        sourceRect.bottom = sourceRect.top + height;

        currentFrame += frameDelta;

        if (target.Collide(destRect, bitmap))
        {
            visible = false;

            heading = 0;
        }

        if (OutOfBounds())
        {
            visible = false;

            heading = 0;
        }
    }

    @Override
    public void Draw(Canvas canvas)
    {
        if (visible)
        {
            canvas.save();
            canvas.rotate((float) (heading * 180 / Math.PI) * 1.5f, X + width
                / 2, Y + height / 2);
            canvas.drawBitmap(bitmap, sourceRect, destRect, paint);
                canvas.restore();
        }
    }
}

归位算法发生在Move()中。我发现问题的部分解决方案是添加此检查:

if (atanY >= 0 && atanX < 0)
    atanY = -atanY;
在航向计算之前

,但如果从大于目标Y位置的Y位置发射导弹,导弹仍然会发出错误。

我已经和他斗争了好几天了,而且我对触发不是很好,所以我希望有人可以帮助我。我试着不要用代码弄乱这个问题,但是如果需要更多的代码或信息,我可以提供它。

谢谢!

修改

我换了一行:

double atanX = target.Y + target.X - X;

为:

double atanX = target.X - X;

但是,如果导弹从大于目标Y位置的Y位置发射,它仍会发出声响。它向目标潜水,但是如果它没有击中,它会突然弯曲,好像它会进行一个循环去循环。

2 个答案:

答案 0 :(得分:2)

问题可能是由于跨越360度/ 0度边界。

使用h =航向,即导弹进入的方向,t =从导弹到目标的方向,f = 0.1。现在考虑t是顺时针20度到h的情况。

如果h = 180则t = 200且h(最终)= 0.9 * 180 + 0.1 * 200 = 182,因此导弹按预期顺时针旋转了一小部分。

但是如果h = 350然后t = 370 = 10(根据atan公式),那么现在h(最终)= 0.9 * 350 + 0.1 * 10 = 316并且导弹在错误的方向上转了很长的距离。

您需要添加一点逻辑来检查零交叉并避免此问题。

添加评论

好的 - atan总是很棘手,因为b = tan(a)对范围内的任何a有效(-inf&lt; a&lt; + inf),但a = atan(b)将始终提供ab在一定范围内(bmin <= b

Blackbear的回答显示了正确的想法。我的等效'修复'将是替换你的行

  heading = (1 - f) * oldHeading + f * Math.atan2(atanY, atanX);

类似

  TheAngle = Math.atan2(atanY, atanX);
  if Abs(TheAngle-oldheading) > 180 then
    TheAngle = TheAngle - Sign(TheAngle)*360;
  heading = (1 - f) * oldHeading + f * TheAngle;

其中,对于x&gt; = 0,Sign(x)= +1,对于x&lt; 0

请注意,我的代码不完全使用C ++,Java或任何您使用的代码,因此您无法直接进行剪切和粘贴 - 但您应该能够将其转换为有用的代码。

答案 1 :(得分:1)

前一段时间我遇到了同样的问题,实际上找到的解决方案非常简单;)这是我更新例程的违规代码,它是助手类的一部分,所以你可以认为拥有者是你的导弹并瞄准目标。它是python,但应该是直截了当的:

vectorToTarget = target.position - owner.position
ang = vectorToTarget.angle() - owner.angle

self.distanceToTarget = vectorToTarget.length()

if ang < -180:       # \
    ang += 360       #  | 
                     #  |-- this solved the problem!
elif ang > 180:      #  |
    ang -= 360       # /

self.angleToTarget = ang
owner.movement.turn(ang)

ang是所有者为了面对目标而应该转动的角度。解决方案是添加标记的部分以纠正该行为