1

私は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位置から発射された場合でも、ミサイルはバグアウトします。ターゲットに向かって飛び込みますが、失敗すると、ループ・デ・ループを実行するかのように突然カーブします。

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となり、ミサイルは間違った方向に大きく回転します。

ゼロ交差をチェックし、この問題を回避するために、小さなロジックを追加する必要があります。

コメントを追加

OK-atanは常にトリッキーなものです。b=tan(a)は範囲(-inf <a <+ inf)内の任意のaに対して有効ですが、a = atan(b)は常に特定の範囲(bmin)のabを提供します。 <= b <bmin + 360)、ベクトルが円を描いて回転すると、ある時点で、bmin + 360を超えて上昇し、すぐにbminに低下するか、bminを下回ると、計算された角度に不連続性が生じます。突然bmin+360にジャンプします。自分自身を明確にしない場合は、時計が午後12時59分を過ぎたときに何が起こるかを考えてみてください。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 に答える