2D游戏算法计算子弹射击目标所需的速度?

Phi*_*sen 9 javascript math graphics geometry 2d

我有一个相当简单的鸟瞰2D游戏,塔精灵通过射击子弹来防御传入的移动精灵.我的问题:如果子弹总是具有相同的定义速度,我如何计算子弹到达其移动目标所需的子弹速度?

我正在使用JavaScript并拥有这些精灵变量(以及其他):sprite.x,sprite.y,sprite.width,sprite.height,sprite.speedX(即velocity),sprite.speedY ...所以我有对象originSprite,targetSprite和bulletSprite都具有这些类型的值,我需要设置正确的bulletSprite速度值.

可能它看起来不错,子弹将从originSprite的外部开始(或者一些定义的半径,虽然我猜起始于originSprite中心也可以工作),但它的子弹中心会尝试击中targetSprite的中心或者所以.请注意,这个世界上没有引力或任何东西.(也许我应该使用角度和速度来获得我的精灵变量,但是现在我正在使用speedX和speedY ...)

非常感谢!

Chr*_*ian 6

在二维房间中将目标精灵视为直线,其中:

A(time) = (sprite.positionX + sprite.speedX * time, sprite.positionX + sprite.speedX * time)
Run Code Online (Sandbox Code Playgroud)

由于你的子弹速度恒定,你也知道:

bullet.speedX^2 + bullet.speedY^2 = bullet.definedSpeed^2
Run Code Online (Sandbox Code Playgroud)

然后你也可以计算子弹的直线:

B(time) = (bullet.positionX + bullet.speedX * time, bullet.positionX + bullet.speedX * time)
Run Code Online (Sandbox Code Playgroud)

而且你知道两条线都在某处:

A(time) = B(time)
Run Code Online (Sandbox Code Playgroud)

然后由您决定使用您给定的值来解决这些方程式并寻求最小值time.


Dr.*_*ius 5

一些物理洞察力

1)目标是"点对象"

所以你必须解决VECTOR方程

位置项目符号 [time = t 1 > t 0 ] ==位置目标 [时间= t 1 > t 0 ] - (公式1)

位置由运动(也是VECTOR)方程给出

位置对象 [t] =位置对象 [t 0 ] +速度对象*(t - t 0)

现在,子弹能够达到目标的条件是等式1具有x和y的解.让我们写下x的等式:

X bullet [t 0 ] + SpeedX bullet*(t - t 0)= X target [t 0 ] + SpeedX target*(t - t 0)

因此,对于我们的碰撞时间

(t 碰撞 - t 0)=(x target [t 0 ] - x bullet [t 0 ])/(SpeedX bullet - SpeedX 目标) - (公式2)

因为我们需要t> t 0的解决方案,这意味着拦截就足够了>

符号(x target [t 0 ] - x bullet [t 0 ])= Sign(SpeedX bullet - SpeedX 目标) - (Eq 3)

Which tells us the evident fact that if an object is moving faster than the other, and in the same direction, they will eventually collide.

从式2中,您可以看到,对于给定的SpeedX 目标 ,存在 t和SpeedX 项目符号的无限解(如其他答案中已经指出的那样),因此我认为您的规范并不完整.

I guess (as stated in a commentary I made in another answer) thinking in a "tower defense" kind of game, that your bullets have a limited range.
所以你还需要另一个约束:

距离[位置目标 [t 碰撞 - t 0 ] - 位置项目符号 [t 0 ]] <BulletRange - (公式4)

Which still permits infinite solutions, but bounded by an upper value for the Collision time, given by the fact that the target may abandon the range.
此外,距离由下式给出

距离[v,u] = + Sqrt [(Vx-Ux)^ 2 +(Vx-Vy)^ 2]

那么,Eq 4变成,

(X target [t Collision - t 0 ] - X bullet [t 0 ])2 +(Y target [t Collision - t 0 ] - Y bullet [t 0 ])2 <BulletRange 2 - (Eq 5)

注意{X bullet [t 0 ],Y bullet [t 0 }是塔位置.

现在,在等式5中替换目标位置的值:

(X target [t 0 ] + SpeedX target*(tt 0) - X bullet [t 0 ])2 +(Y target [t 0 ] + SpeedY target*(tt 0) - Y bullet [t 0 ])2 < BulletRange 2 - (Eq 6)

调用初始距离:

Dxt0 = X target [t 0 ] - X bullet [t 0 ]

Dyt0 = Y target [t 0 ] - Y bullet [t 0 ]

等式6变为

(Dtx0 + SpeedX 目标*(tt 0))2 +(Dty0 + SpeedY 目标*(tt 0))2 <BulletRange 2 - (Eq 7)

这是在t-t0中要求解的二次方程.积极的解决方案将为我们提供最大的碰撞时间.之后目标将超出范围.

现在打电话

速度目标 2 = SpeedX 目标 2 + SpeedY 目标 2

H = Dtx0*SpeedX 目标 + Dty0*SpeedY 目标


T 碰撞最大值 = t 0 - (H +/- Sqrt(BulletRange 2*速度目标 2 - H 2))/速度目标 2

So you need to produce the collision BEFORE this time. The sign of the square root should be taken such as the time is greater than t0

After you select an appropriate flying time for your bullet from the visual 
effects point of view, you can calculate the SpeedX and SpeedY for the bullet 
from  
Run Code Online (Sandbox Code Playgroud)

Which tells us the evident fact that if an object is moving faster than the other, and in the same direction, they will eventually collide.

SpeedX bullet =(X target [t 0 ] - X bullet [t 0 ])/(t Collision - t 0)+ SpeedX target

SpeedY bullet =(Y 目标 [t 0 ] - Y bullet [t 0 ])/(t 碰撞 - t 0)+ SpeedY 目标

2)目标和塔是"广泛物体"

现在,对于目标是半径为R的圆的情况进行推广是微不足道的.你得到的,相当于子弹的"扩展范围".那个扩展只是R.

因此,将BulletRange替换为(BulletRange + R),您将获得允许的最大碰撞时间的新公式.

如果您还想考虑大炮的半径,则应用相同的考虑因素,给出"双倍扩展范围"

NewBulletRange = BulletRange + R 目标 + R

无限范围子弹

如果您确定某些特殊项目符号不应具有范围(和检测)限制,则仍然存在屏幕边界约束.但要解决这个问题要困难一些.如果你需要这种弹丸,请留言,我会尝试做一些数学运算.


gna*_*arf 3

使用向量可以使数学看起来更简单一些。 Sylvester似乎是 JavaScript 中向量的一个有前途的实现,但为了我的示例的目的,我将编写自己的向量函数。我还将假设.x/.y是在左上角/左上角测量的。

// this is a "constant"  - representing 10px motion per "time unit"
var bulletSpeed = 10; 
// calculate the vector from our center to their center
var enemyVec = vec_sub(targetSprite.getCenter(), originSprite.getCenter());
// measure the "distance" the bullet will travel
var dist = vec_mag(enemyVec);
// adjust for target position based on the amount of "time units" to travel "dist"
// and the targets speed vector
enemyVec = vec_add(enemyVec, vec_mul(targetSprite.getSpeed(), dist/bulletSpeed));
// calculate trajectory of bullet
var bulletTrajectory = vec_mul(vec_normal(enemyVec), bulletSpeed);
// assign values
bulletSprite.speedX = bulletTrajectory.x;  
bulletSprite.speedY = bulletTrajectory.y;  

// functions used in the above example:

// getCenter and getSpeed return "vectors"
sprite.prototype.getCenter = function() { 
  return {
    x: this.x+(this.width/2), 
    y: this.y+(this.height/2) 
  }; 
};

sprite.prototype.getSpeed = function() { 
  return {
    x: this.speedX, 
    y: this.speedY 
  }; 
};

function vec_mag(vec) { // get the magnitude of the vector
  return Math.sqrt( vec.x * vec.x + vec.y * vec.y); 
 }
function vec_sub(a,b) { // subtract two vectors
  return { x: a.x-b.x, y: a.y-b.y };
}
function vec_add(a,b) { // add two vectors
  return { x: a.x + b.x, y: a.y + b.y };
}
function vec_mul(a,c) { // multiply a vector by a scalar
  return { x: a.x * c, y: a.y * c };
}
function vec_div(a,c) { // divide == multiply by 1/c
  return vec_mul(a, 1.0/c);
}
function vec_normal(a) { // normalize vector
  return vec_div(a, vec_mag(a)); 
}
Run Code Online (Sandbox Code Playgroud)