我正在为安卓中的水平滚动太空射击游戏开发寻的导弹。 我无法从我正在使用的算法中获得所需的行为。 我希望导弹从玩家的飞船上水平射出,然后逐渐回到目标上,如果弧线太大,则有机会错过目标。 它有效,除非导弹错过目标,在这种情况下,它通常会尝试沿着正弦波的路径运行,直到它从屏幕右侧跑出。 我想要的是,导弹试图在其当前曲线中继续围绕目标盘旋(就像刻板的寻的导弹一样),直到它击中精灵或跑出屏幕边缘。 我可能会添加一个限制,以便它会爆炸并且不会在屏幕上不断旋转,但如果我让它工作,那将是以后的添加。
下面是代码的示例:
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 位置发射,它仍然会出错。 它向目标俯冲,但如果它错过了,它会突然弯曲起来,就好像它要做一个循环一样。
问题可能是由于跨越 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 <+>inf),但是a = atan(b)总是在某个范围内提供b(bmin <= b
黑熊的回答表明了正确的想法。我的等效"修复"是更换您的线路
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) = +1 表示 x>=0,-1 表示 x <0
请注意,我的代码不是C++,Java或您正在使用的任何内容,因此您将无法直接剪切和粘贴 - 但您应该能够将其转换为有用的内容。
前我遇到了同样的问题,一旦发现实际上;)这是我的更新例程的违规代码,它是帮助程序类的一部分,因此您可以将所有者视为您的导弹并瞄准其目标。它是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 是所有者为了面对目标而应转动的角度。解决方案是添加标记的部分以纠正该行为