如何计算从A点到B点给定距离和角度的弹丸运动初速度?

How to calculate initial velocity of projectile motion by a given distance and angle from point A to B?

示例图 (这张图不准确,但想法或抛物线就像那里)

下图中,绿色曲线是子弹的抛射运动,灰色线是阴影运动,红圈是起始位置,蓝圈是终点。目前我能够实现阴影移动,但不幸的是弹丸运动不正确,就像下面的第二张图一样。请参阅 link 这是我目前移动子弹的方式。

示例GIF (这就是我想要的结果!)

在下面的GIF中,当子弹发射时,子弹有抛射运动并且仍然朝着十字准线中心点移动。

将子弹移动到目的地

在下面的代码中,是将子弹以6米的恒定给定速度移动到目的地的方法。为了能够实现上面的图像轨迹,我需要计算给定距离的速度。

float bulletSpeed = 6; // In the mean time the speed is constant, but this should be calculated base on distance and angle
Vector2 touchPoint = new Vector2(input.touchpoint.x, input.touchpoint.y);
Vector2 targetDirection = touchPoint.cpy().sub(bulletPosition).nor();

Vector2 targetVelocity = targetDirection.cpy().scl(bulletSpeed);
float targetAngle = targetDirection.angle();
float targetDistance = touchPoint.dst(bulletPosition);

body.setLinearVelocity(targetVelocity);

绘制投影轨迹

这里没有问题,这是基于iforce2d projected trajectory example

startingPosition.set(originPoint); // Bullet Position
startingVelocity.set(targetDirection); // Calculated target direction

shape.setProjectionMatrix(CameraManager.getInstance().getCamera().combined);
shape.begin(ShapeRenderer.ShapeType.Point);
    for (int i = 0; i < 180; i++) { // three seconds at 60fps
        Vector2 trajectoryPosition = getTrajectoryPoint(startingPosition, startingVelocity, i);
        shape.point(trajectoryPosition.x, trajectoryPosition.y, 0);
    }
shape.end();

获取轨迹点

public Vector2 getTrajectoryPoint(Vector2 startingPosition, Vector2 startingVelocity, float n) {
    Vector2 gravity = WorldManager.getWorld().getGravity();
    float t = 1 / 60.0f; // seconds per time step (at 60fps)
    Vector2 stepVelocity = startingVelocity.cpy().scl(t); // m/s
    Vector2 stepGravity = gravity.cpy().scl(t * t); // m/s/s
    Vector2 trajectoryPoint = new Vector2();
    trajectoryPoint.x = (startingPosition.x + n * stepVelocity.x + 0.5f * (n * n + n) * stepGravity.x);
    trajectoryPoint.y = (startingPosition.y + n * stepVelocity.y + 0.5f * (n * n + n) * stepGravity.y);
    return trajectoryPoint;
}

图形结果 (如您所见,我没有得到想要的结果..)

可能的解决方案 (我找到了这个解决方案Calculating initial velocities given a trajectory parabola,但我很难将其转换为 box2d。)

更新 (8/6/2016) 投影参考的其他详细信息

下图中是自上而下(倾斜)的图形投影。

这个答案使用了游戏开发中常见的概念,因此将这个问题迁移到 GameDev.StackExchange 可能是合适的。


1。坐标系

因为您正试图在 2D 显示中模仿 3D 运动,所以一个有用的初始步骤是将您对 world space 和 [=91= 的概念分开]屏幕space

  • World Space 是虚拟坐标系,您的游戏世界在其中布局并在其中模拟其机制。要模拟距离 2D 地平面有一定高度的射弹的弧形运动,您需要使用 3D 坐标系 (x, y, z)。

    为了尽量减少与您当前方案的冲突,假设 z 是指向您当前使用的 (x,y) 平面的 "up" 方向。如果您不需要对 object 相互传递 over/under 进行建模,那么您可以仅使用 (x,y) 分量继续在 2D 中模拟您的物理,隐含地将 z 坐标视为 0。

  • Screen Space是你实际用于渲染的坐标系。要获取 3D 世界 space 坐标并将其转换为屏幕的 2D 平面,您需要应用 projection。对于您所展示的游戏,您可能需要使用 top-down 倾斜投影,例如....

    screenPos.x = (worldPos.x - cameraOffset.x) * zoomFactor; screenPos.y = (worldPos.y + worldPos.z - cameraOffset.y) * zoomFactor;

    这将代表你的世界,没有透视(所以地板上的圆圈在你的屏幕上变成圆圈,向上跳跃 1 米与步行 1 米相同 "North")。

如果您想要更逼真的外观,可以将 worldPos.y 和 worldPos.z 乘以一些小于 1.0 的系数来模拟透视缩短(因此地板上的圆在屏幕上变成椭圆)

2。将问题分为两部分

考虑到这一区别,我们可以将您示例中的射弹视为两个独立的部分:

  • 阴影 沿着地板的二维平面 (x, y, 0) 移动。由于您的物理是 2D 的,因此将阴影视为 "real" 射弹是有意义的,由您的 Box2D 物理控制,并根据其运动进行碰撞。

    即。当弹丸的影子与目标的足迹相交时,弹丸已击中目标。

    (如果你想要更高的保真度,那么可以将射弹 抛过 短 object 降落在另一边的地板上,你要么需要移动到 3D 物理模拟,或使用一些高度检查来选择性地忽略碰撞)

  • 弹道子弹是在空中划出弧线的部分。我们可以认为这是一种纯粹的视觉效果,没有相关的物理碰撞。 (要了解原因,请查看角色向下开火时的示例 gif:子弹最初飞到他身后——但我们不希望子弹击中玩家身后的敌人,而当他们试图在他面前向下开火时他们的性格)

3。阴影

这与您可能已经在游戏中处理 straight-firing 子弹的方式非常相似。只需将它指向从你的枪口到你的目标的方向并设置它的速度。没有重力的基本二维物理将从那里开始。

float bulletSpeed = 6;
Vector2 touchPoint = ScreenToWorldPoint(input.touchpoint.x, input.touchpoint.y);
Vector2 targetOffset = touchPoint.cpy().sub(firingPosition);
Vector2 targetDirection = targetOffset.cpy().nor();

Vector2 targetVelocity = targetDirection.cpy().scl(bulletSpeed);

shadowBody.setLinearVelocity(targetVelocity);

由于这里的游戏机制是 2D 的,我建议保持发射速度恒定,如上所示。这将使武器的行为对玩家来说更加一致和可预测。为了将真正的武器射得更远,我们通常会将其向上瞄准,牺牲一些垂直方向的水平速度来对抗重力,因此随着我们的目标远离我们的 time-to-hit 增长 non-linearly。就这款游戏而言,虽然弹道弧线实际上只是一种人工视觉效果,但添加这种物理现实主义是否真的能让它玩得更好还有待商榷。我会在没有的情况下尝试,看看你是否想念它。 ;)

4。弹道子弹的弧度

我们有几种选择圆弧样式的方式。我们可以尝试始终达到特定的高度,或保持特定的发射速度等。接下来,我将建议使用恒定的重力值,并选择刚好足以在阴影到达时着陆的初始向上速度input.touchPoint。这往往会使 arcs/straighter 附近目标的射门较浅,而在较远的地方射击时会出现较高的吊球。

首先,一些可调常量:

// Tune this to get the arcs the height/sharpness you want.
float gravity = 9.8f;

// Tune this until it matches the height of your character's muzzle off the ground.
float launchHeight = 1.0f;

接下来,我们在地板上应该发射的位置上方的适当位置生成子弹:

bullet.worldPos.x = firingPosition.x;
bullet.worldPos.y = firingPosition.y;
bullet.worldPos.z = launchHeight;

现在,根据我们上面计算的移动阴影的值,我们可以计算出子弹的初始向上速度:

float distance = targetDirection.dot(targetOffset); // 2D range to target
float duration = distance/bulletSpeed;              // Time until hit

// Initialize its vertical (z) velocity to create the arc we want.
// See section 5 below for how this formula is derived.
bullet.verticalVelocity = duration * gravity/2 - launchHeight/duration;

现在每一帧(在每个物理步骤之后)我们都可以执行以下操作将子弹更新到其阴影上方的适当位置:

bullet.worldPos.x = shadow.worldPos.x;
bullet.worldPos.y = shadow.worldPos.y;
bullet.verticalVelocity -= gravity * deltaTime;
bullet.worldPos.z += bullet.verticalVelocity * deltaTime;

// Convert to screen space using a projection like the oblique style described above.
bullet.screenPos = Project(bullet.worldPos);

这里我用欧拉积分来模拟圆弧,很简单,但是可以显示如果你有 low/uneven 帧率,接近错误。对于这种视觉效果来说可能没什么大不了的,但如果你想要更高的精度,你可以跟踪 time-of-fire 或 time-in-air 并使用下面的 h(t) 的参数方程来精确跟踪弧线.

5。推导(可选)

如果你对我们如何计算上面的初始速度感到好奇:

我们知道我们想要一条抛物线,它在时间 t = duration 时达到零,并且由于重力而产生曲率。这给了我们一个具有以下因式的二次方程....

h(t) = -g/2 * (t - duration) * (t - p)

...对于一些未知的 p。正在扩展...

h(t) = (-g/2) * p * duration + (g/2)*(duration + p) * t - (g/2) * t*t

设置 t = 0 给我们初始发射高度,我们可以求解 p

h(0) = (-g/2) * p * duration
p = -2 * h(0) / (g * duration)

代入上述h(t)的展开形式,我们得到...

h(t) = h(0) + ((g/2)*duration - h(0)/duration) * t - (g/2) * t*t

t 中线性的中间项是初始垂直速度。 (第一项为初始高度,最后一项为重力加速度)