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

script本

示例图片 (此图不准确,但此处的想法或抛物线很像)

在下图中,绿色曲线是子弹的弹丸运动,灰色线是阴影运动,红色圆圈是起始位置,蓝色圆圈是终点。目前,我能够实现阴影移动,但是不幸的是,弹丸运动不像下面的第二幅图像那样正确。请查看链接,这是我当前移动项目符号的方式。

在此处输入图片说明

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投影轨迹示例的

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;
}

图形结果 (如您所见,我没有得到期望的结果。)

在此处输入图片说明

Possible solution (I found this solution Calculating initial velocities given a trajectory parabola, but I'm having hard time converting this to box2d.)

在此处输入图片说明

Update (8/6/2016) Additional details for projection reference

In the below image is the graphical projection of top-down (oblique).

在此处输入图片说明

DMGregory

This answer uses concepts common in game development, so it may be suitable to migrate this question to GameDev.StackExchange.


1. Coordinate systems

Because you're trying to imitate 3D movement in a 2D display, a helpful initial step is to separate your concepts of world space and screen space

  • World Space is the virtual coordinate system in which your game's world is laid out and in which its mechanics are simulated. To model arcing movement of a projectile that has a height off the 2D ground plane, you'd want to use a 3D coordinate system (x, y, z).

    To minimize conflicts with your current scheme, let's say z is the direction pointing "up" off the (x,y) floor plane you're currently using. If you don't need to model objects passing over/under each other, then you can keep simulating your physics in 2D with just the (x,y) components, implicitly treating the z coordinate as 0.

  • Screen Space is the coordinate system you actually use for rendering. To take a 3D world space coordinate and convert it to the 2D plane of your screen, you'll need to apply a projection. For a game like the one you've shown, you may want to use a top-down oblique projection, somemthing like....

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

    This will represent your world with no foreshortening (so a circle on the floor becomes a circle on your screen, and jumping 1m up displaces the character the same as walking 1m "North").

If you want a more realistic look, you can multiply worldPos.y & worldPos.z by some coefficients less than 1.0 to model foreshortening (so a circle on the floor becomes an ellipse on the screen)

2. Modelling the problem in two parts

With this distinction in mind, we can think of the projectile in your example as two separate parts:

  • The shadow travels along the 2D plane of the floor, (x, y, 0). Since your physics are 2D, it makes sense to treat the shadow as the "real" projectile, controlled by your Box2D physics, and base your collisions on its movement.

    ie. When the projectile's shadow intersects a target's footprint, the projectile has hit the target.

    (If you want higher fidelity, so a projectile can be lobbed over a short object to land on the floor on the other side, you'll either need to move to a 3D physics simulation, or use some height checks to selectively ignore collisions)

  • The ballistic bullet is the part that arcs through the air. We can think of this as a purely visual effect, with no associated physics collisions. (To see why, look at the example gif when the character fires downward: the bullet initially flies up behind him - but we wouldn't want the bullet to hit an enemy behind the player when they're trying to fire downward in front of their character)

3. The shadow

This behaves much the same way you're probably already handling straight-firing bullets in your game. Just point it in the direction from your muzzle to your target and set its velocity. Basic 2D physics without gravity will take it from there.

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

Since the game mechanics here are 2D, I'd recommend keeping the firing speed constant as you have it above. This will make the behaviour of the weapon more consistent & predictable for the player. To shoot a real weapon further we'd often aim it upward, sacrificing some horizontal velocity for vertical to counter gravity, so as our target moves away our time-to-hit grows non-linearly. In the case of this game though the ballistic arc is really just an artificial visual effect, so it's debatable whether adding this physical realism actually makes it play better. I'd try it without and see whether you miss it. ;)

4. The ballistic bullet's arc

There are a few choices for how we style the arc. We could try to always hit a particular height, or maintain a particular launch velocity, etc. For what follows, I'm going to suggest using a constant gravity value, and choosing an initial upward velocity just sufficient to touch down when the shadow reaches input.touchPoint. This will tend to give shallower arcs/straighter shots at nearby targets, and higher lobs when shooting far away.

First, some tunable constants:

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

Next, we spawn the bullet at the appropriate point above the spot on the floor where it should be fired:

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

Now, based on the values we calculated above for moving the shadow, we can calculate the initial upward velocity of the bullet:

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;

Now every frame (after each physics step) we can do the following to update the bullet to the appropriate position above its shadow:

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

Here I'm using Euler integration to model the arc, which is simple, but could show approximation errors if you have a low/uneven framerate. Probably not a big deal for this kind of visual effect, but if you want higher precision you can track the time-of-fire or time-in-air and use the parametric equation for h(t) below to trace the arc exactly.

5. Derivation (optional)

In case you're curious how we calculate the initial velocity above:

We know we want a parabola that hits zero at time t = duration, with a curvature due to gravity. That gives us a quadratic with the following factored form....

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

...for some unknown p. Expanding...

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

setting t = 0 gives us the initial launch height, which we can solve for p

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

Substituting into the expanded form of h(t) above, we get...

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

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章