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

标签 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

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

下图是自顶向下(倾斜)的图形投影。

enter image description here

最佳答案

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



1.坐标系

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


世界空间是虚拟坐标系,您可以在其中布置游戏世界,并在其中模拟其力学机制。要对离2D地平面高度的弹丸的飞弧运动进行建模,您需要使用3D坐标系(x,y,z)。

为了最大程度地减少与当前方案的冲突,假设z是您当前使用的(x,y)地板平面“向上”指向的方向。如果您不需要对彼此上方/下方通过的对象进行建模,则可以仅使用(x,y)分量继续在2D中模拟物理,将z坐标隐式地视为0。
屏幕空间是您实际用于渲染的坐标系。要获取3D世界空间坐标并将其转换为屏幕的2D平面,您需要应用projection。对于您所展示的游戏,您可能需要使用自上而下的倾斜投影,例如...。

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

这将代表您的世界,并且没有任何缩影(因此,地板上的圆圈变成屏幕上的圆圈,向上跳跃1m会替换与向北行走1m相同的角色)。


如果您想获得更逼真的外观,可以将worldPos.y和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的,因此建议您保持上面的射击速度不变。这将使玩家的武器行为更加一致和可预测。为了进一步射击真实武器,我们通常将其向上瞄准,牺牲一些水平速度以垂直或反重力,因此当目标移开时,命中时间呈非线性增长。在本游戏中,弹道实际上只是一种人造的视觉效果,因此,添加这种物理逼真的效果是否使它玩起来更具争议。我会尝试一下,看看是否想念它。 ;)

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

关于libgdx - 如何计算从点A到点B的给定距离和角度的弹丸运动的初始速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38692488/

相关文章:

android - 如何在LibGDX中使用多线程?

java - libgdx中使用scene2d时如何实现box2d灯光

java - Libgdx 角色运行动画未显示

java - Libgdx 切换屏幕

libgdx - Android 设备可以使用的最大 Sprite 表大小是多少?

c++ - 在 Box2d 中删除正文 - C++

java - libGDX Box2DDebugRenderer 绘制框太大和太小

c++ - SFML:射弹停止

c# - 如何移动 3D 射弹 Unity

emacs - 如何使用projectile-find-test-file