1

我正在为安卓中的水平滚动太空射击游戏开发寻的导弹。我无法从我正在使用的算法中获得所需的行为。我希望导弹从玩家的船上水平射出,然后逐渐瞄准目标,如果弧度太大,有机会错过目标。它可以工作,除非导弹没有击中目标,在这种情况下,它通常会尝试跟随正弦波的路径,直到它跑出屏幕的右侧。我想要的是,导弹试图在其当前曲线上继续围绕目标盘旋(就像典型的寻的导弹),直到它击中一个精灵或跑出屏幕边缘。我可能会添加一个限制,这样它就会爆炸并且不会在屏幕上继续旋转,

下面是代码示例:

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 位置发射的,它仍然会出错。它向目标俯冲,但如果它未命中,它会突然向上弯曲,就好像它要进行循环解循环一样。

4

2 回答 2

2

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

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

如果 h = 180 则 t = 200 且 h(final) = 0.9*180+0.1*200 = 182,因此导弹按预期顺时针转动了少量。

但是如果 h = 350 那么 t = 370 = 10(根据 atan 公式),所以现在 h(final) = 0.9*350+0.1*10=316 并且导弹已经朝错误的方向转动了很大距离。

您需要添加一些逻辑来检查过零并避免此问题。

添加评论

好的 - atan 总是一件棘手的事情,因为 b = tan(a) 对范围内的任何 a 都有效 (-inf < a < + inf),但 a = atan(b) 总是会在某个范围内提供 ab (bmin <= b < bmin + 360),所以当一个向量围绕一个圆圈旋转时,在某个点上,当它上升超过 bmin + 360 并立即下降到 bmin 或下降到 bmin 以下时,计算的角度会出现不连续性突然跳到 bmin + 360。如果我没有说清楚,那么请考虑一下时钟超过 12:59 pm 时会发生什么。它不会持续到 13:00(欧洲除外),而是回到凌晨 1:00。

黑熊的回答显示了正确的想法。我等效的“修复”是替换您的行

  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;

其中 Sign(x) = +1 表示 x >=0 和 -1 表示 x < 0

请注意,我的代码不是 C++、Java 或您使用的任何代码,因此您将无法直接进行剪切和粘贴 - 但您应该能够将其翻译成有用的东西。

于 2012-08-09T22:23:14.333 回答
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 是所有者为了面对目标而应该转动的角度。解决方案是添加标记部分以纠正该行为

于 2012-08-09T22:43:14.213 回答