【问题标题】:2D game algorithm to calculate a bullet's needed speed to hit target?2D游戏算法计算子弹击中目标所需的速度?
【发布时间】:2011-03-13 18:53:35
【问题描述】:

我有一个相当简单的鸟瞰 2D 游戏,其中塔精灵通过向它们发射子弹来防御移动的精灵。我的问题:如果子弹始终具有相同的定义速度,我如何计算子弹到达其移动目标所需的子弹速度?

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

可能为了看起来不错,子弹会从 originSprite 的外部开始(或某个定义的半径,虽然我猜从 originSprite 中心开始也可以),但它的子弹中心会尝试击中targetSprite 左右。请注意,这个世界上没有重力或任何东西。 (也许我应该让我的精灵变量使用角度和速度,但现在我使用的是 speedX 和 speedY...)

非常感谢!

【问题讨论】:

  • 我不明白你的问题。你能举个例子吗?
  • 我们是在谈论塔防克隆,在这种克隆中,来袭目标可能会在子弹飞行时转弯,还是假设目标会直线移动?
  • Sjoerd,举个例子,会有一个静态精灵,我们称它为射手,还有一个传入的移动精灵,我们称它为移动器。射手有一定距离开始看到敌人的移动者,并且“知道”敌人的速度,然后尝试射击。射手的子弹总是以恒定的速度,所以如果说射手正好在动子的下方,并且动子向下移动,bullet.speedX 将为 0,bullet.speedY 为 -1。
  • Deceze,塔将始终假设目标将以直线和恒定速度移动(目标实际上可能很少在某处碰撞并改变方向,但塔不会计算这个或“知道”这一点,即塔在边境情况下很可能会错过目标,但没关系)。

标签: javascript math graphics geometry 2d


【解决方案1】:

使用vectors 可以使围绕这个的数学看起来更简单一些。 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)); 
}

【讨论】:

  • 像魅力一样工作。 (其他人复制这个的注释,在一个地方你需要将 veg_mag 修改为 vec_mag。)我不得不尝试一下找到正确的 bulletSpeed 值,然后子弹击中它们的移动目标点。谢谢!!
  • @Philipp - 谢谢,修正了那个错字。
【解决方案2】:

一些物理洞察力

1 ) 对于目标是“点对象”

所以你必须解决向量方程

位置bullet [ time=t1 > t0 ] == Positiontarget [ time=t1 > t0 ] -- (Eq 1)

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

位置object [ t ] = 位置object [ t0 ] + 速度object * ( t - t0 )

现在,子弹能够到达目标的条件是方程 1 有 x 和 y 的解。让我们写下 x 的方程:

Xbullet [ t0 ] + SpeedXbullet * ( t - t0 ) = Xtarget [ t0 ] + SpeedXtarget * ( t - t0 )

所以对于碰撞时间我们有

( t碰撞 - t0 ) = (xtarget [ t 0 ] - xbullet [ t0 ] ) / (SpeedXbullet - SpeedXtarget) -- (Eq 2)

由于我们需要 t > t0 的解决方案,这意味着有一个截距就足够了>

符号 ( xtarget[ t0 ] - xbullet[ t0 ] ) = 符号 ( SpeedXbullet - SpeedXtarget ) -- (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 中,您可以看到对于给定的 SpeedXtarget存在无限解(正如其他答案中已经指出的那样)对于 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碰撞 - t0 ] - 位置子弹 [ t 0 ] ]

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 ]

所以,等式 4 变为,

(Xtarget[t碰撞 - t0] - Xbullet[t0])2 + (Ytarget[t碰撞 - t0] - Ybullet[t0])22 -- (Eq 5)

注意 { Xbullet[t0] , Ybullet[t0} 是塔位置。

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

(Xtarget[t0] + SpeedXtarget * (tt0) - Xbullet[t0])2 + (Y目标[t0] + SpeedY目标 * (tt0) - Ybullet[t0])22 -- (方程 6)

调用初始距离:

Dxt0 = Xtarget[t0] - Xbullet[t0]

Dyt0 = Ytarget[t0] - Ybullet[t0]

等式 6 变为

(Dtx0 + SpeedXtarget * (tt0) )2 + (Dty0 + SpeedYtarget * (tt0))22 -- (Eq 7)

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

正在调用

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

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


T碰撞最大值 = t0 - ( H +/- Sqrt (BulletRange2 * Speedtarget2 - H2) ) / Speedtarget 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  

SpeedXbullet = ( Xtarget [ t0 ] - Xbullet [ t0 ] ) / ( t碰撞 - t0 ) + SpeedX目标

SpeedYbullet = ( Ytarget [ t0 ] - Ybullet [ t0 ] ) / ( t碰撞 - t0 ) + SpeedY目标

2 ) 对于目标和塔是“广泛的对象”

现在,对于目标是半径为 R 的圆的情况进行概括是微不足道的。你得到的,相当于子弹的“扩展范围”。该扩展名只是 R。

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

如果您还想考虑大炮的半径,同样的考虑也适用,给出“双扩展范围

NewBulletRange = BulletRange + R目标 + R

无限范围子弹

如果您决定某些特殊项目符号不应具有范围(和检测)限制,则仍然存在屏幕边框限制。但处理起来有点困难。如果您需要这种弹丸,请发表评论,我会尝试做一些数学运算。

【讨论】:

    【解决方案3】:

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

    A(time) = (sprite.positionX + sprite.speedX * time, sprite.positionX + sprite.speedX * time)
    

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

    bullet.speedX^2 + bullet.speedY^2 = bullet.definedSpeed^2
    

    那么你也可以为子弹计算一条直线:

    B(time) = (bullet.positionX + bullet.speedX * time, bullet.positionX + bullet.speedX * time)
    

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

    A(time) = B(time)
    

    然后由您用给定值求解这些方程,并为time 寻找最小值。

    【讨论】:

    • 如果在实时求解线性方程组时会消耗过多的 CPU 时间(这可能取决于同时飞行的子弹数量),您可能可以预先计算一组解决方案,并且只实时解决极端情况。假设您的目标将以已知速度移动,则根据(例如)它们可以从(N、NE、E 等)接近的八个不同方向计算结果。当射手检测到目标时,只需查看您是否为具有该速度和方位的目标预先计算了结果。如果没有,计算它。
    • @bta:当然,在编写代码之前通过将那些已知变量视为常数来解决纸上的方程是有意义的。
    【解决方案4】:

    计算射手和目标之间的距离:dist = sqrt((xt - xs)^2 + (yt - ys)^2)
    将 x 和 y 距离除以上述一个:nx = (xt - xs)/dist; ny = (yt - ys)/dist;(向量的归一化)
    将结果乘以一个因子以获得每个时间单位的 n 个像素,即。每个方向的速度。它应该在想要的方向上保持恒定的速度。

    【讨论】:

    • 这根本不考虑目标的速度,因此除非目标直接朝向或远离射手移动,否则会错过。
    【解决方案5】:

    我假设目标将以恒定速度沿直线移动。

    如果子弹的方向都是可变的(即您尝试计算子弹的speedXspeedY),则有无限多的解决方案。

    如果你设置一个固定的方向,你只需将子弹和目标的两条线相交。根据目标当前点与交点之间的距离(以及目标的速度),您可以计算出目标到达该交点所需的时间。

    从子弹的原点到交点的距离(和之前计算的时间)可以计算出子弹的速度。

    【讨论】:

    • 规范中缺少某些内容。我猜这是子弹的射程。如果 OP 指定 RANGE,则解的数量也是无限的,但子弹会有一个 MINIMUM 速度(即与子弹相交并在该 RANGE 处瞄准的那个)
    猜你喜欢
    • 2014-09-22
    • 1970-01-01
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 2012-03-23
    相关资源
    最近更新 更多