如何计算从A点到B点的给定距离和角度的抛射物运动的初始速度?

时间:2016-08-01 06:44:16

标签: libgdx box2d projectile

示例图片 (此图表不准确,但想法或抛物线就像在那里)

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

enter image description here

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

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

enter image description here

将子弹移至目的地

在下面的代码中,是将子弹移动到目的地的方法,其恒定的给定速度为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;
}

图形结果 (正如您所见,我无法获得所需的结果..)

enter image description here

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

enter image description here

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

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

enter image description here

1 个答案:

答案 0 :(得分:2)

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

1。坐标系

因为您试图在2D显示中模仿3D移动,所以有用的初始步骤是将世界空间屏幕空间 的概念分开p>

  • 世界空间是一个虚拟坐标系,用于模拟游戏的世界,并在其中模拟其机制。要模拟高度偏离2D地平面的射弹的弧形运动,您需要使用3D坐标系(x,y,z)。

    为了尽量减少与您当前方案的冲突,我们假设z是指向&#34; up&#34;离开您当前正在使用的(x,y)楼层平面。如果您不需要对通过彼此上下交互的对象进行建模,那么您可以仅使用(x,y)组件在2D中模拟物理,隐式将z坐标视为0。

    < / LI>
  • 屏幕空间是您实际用于渲染的坐标系。要获取3D世界空间坐标并将其转换为屏幕的2D平面,您需要应用projection。对于像你所展示的那种游戏,你可能想要使用自上而下的倾斜投影,有点类似......

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

    这将代表你的世界没有透视(因此地板上的圆圈变成了屏幕上的一个圆圈,并且向上跳1米会取代角色,就像步行1m&#34; North&#34;)一样。

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

2。将问题分为两部分

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

  • 阴影沿着地板的2D平面(x,y,0)移动。由于你的物理学是2D,因此将阴影视为真实的物理学是有意义的。弹丸,由你的Box2D物理控制,并根据它的运动进行碰撞。

    即。当射弹的阴影与目标的足迹相交时,射弹就会击中目标。

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

  • 弹道子弹是穿过空中的部分。我们可以将其视为纯粹的视觉效果,没有相关的物理碰撞。 (看看为什么,当角色向下射击时看看示例gif:子弹最初在他身后飞过 - 但是当他们试图射击时我们不希望子弹击中玩家后面的敌人在他们的角色前面向下)

3。阴影

这与你可能已经在游戏中处理直发子弹的方式非常相似。只需指向从枪口到目标的方向并设置其速度即可。没有重力的基本2D物理学将从那里开始。

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,我建议保持点火速度不变,如上所述。这将使武器的行为更加一致&amp;可预测的球员。为了进一步射击真正的武器,我们经常将它向上瞄准,牺牲一些垂直的水平速度来抵抗重力,因此当我们的目标移开时,我们的击中时间会非线性地增长。在这个游戏的情况下,尽管弹道弧实际上只是一种人为的视觉效果,但是添加这种物理真实性是否真的让它更好地发挥作用值得商榷。我试试没有,看看你是否想念它。 ;)

4。弹道子弹的弧线

我们如何设计电弧的样式有几种选择。我们可以尝试始终达到特定高度,或保持特定的发射速度等。接下来,我将建议使用恒定的重力值,并选择刚好足以触及时的初始向上速度。阴影到达input.touchPoint。这将倾向于在附近的目标上产生更浅的弧线/更直的射击,并且在远距离射击时产生更高的光圈。

首先,一些可调参数:

// 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);

这里我使用Euler积分来建模弧,这很简单,但如果你的帧速率低/不均匀,可能会显示近似误差。对于这种视觉效果可能不是什么大问题,但是如果你想要更高的精度,你可以跟踪火焰时间或空中时间,并使用下面h(t)的参数方程来精确追踪弧线

5。推导(可选)

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

我们知道我们想要一个在时间t =持续时间达到零的抛物线,由于重力而具有曲率。这给了我们一个带有以下因式形式的二次方....

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中线性的中间项是初始垂直速度。 (第一项是初始高度,最后一项是由重力引起的加速度)