4

Example image (This graph is not accurate but the idea or the parabola is like in there)

In the below image, green curved line is the projectile motion of a bullet, gray line is the shadow motion, red circle is the starting position and the blue circle is the destination. Currently I am able to achieve the shadow moving, but unfortunately the projectile motion is not right just like the second image below. Please see the link this is how I currently move the bullet.

enter image description here

Example GIF (This is the result I wanted!)

In the below GIF, when the bullet is fired, the bullet has a projectile motion and still moving towards the cross-hair center point.

enter image description here

To move the bullet to its destination

In the below code, is the method to move the bullet to its destination with a constant given speed of 6 meters. To be able to achieve the above image trajectory I need to calculate the speed by a given distance.

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

To draw the projected trajectory

There's no problem here, this is base on the 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();

Getting the trajectory point

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

The graphical results (As you can see the I am not getting the desired results..)

enter image description here

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

enter image description here

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

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

enter image description here

Community
  • 1
  • 1
ronscript
  • 397
  • 1
  • 8
  • 33
  • What's wrong or incomplete in the answer you received? Please post your current code including your implementations of the previous answers and specify exactly how this doesn't fit your expectations. – BeyelerStudios Aug 01 '16 at 07:00
  • I've added the code, some explanations and the graphical results. – ronscript Aug 01 '16 at 11:26
  • Are you looking for something like http://stackoverflow.com/a/23204945/3182664 ? – Marco13 Aug 01 '16 at 11:42
  • I dont think it's the same, I've change the title to "How to calculate projected trajectory by a given distance and angle of touch point?" – ronscript Aug 01 '16 at 16:02
  • I don't have time to add a full answer until this evening. In the meantime, can you include your implementation of `getTrajectoryPoint`? – DMGregory Aug 02 '16 at 17:52
  • @DMGregory I've already added the `getTrajectoryPoint` method. – ronscript Aug 02 '16 at 18:05
  • @DMGregory you can re-review details I've added what you've requested and revise some details. – ronscript Aug 02 '16 at 18:42

1 Answers1

4

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

And that middle term that's linear in t is the initial vertical velocity. (The first term is the initial height and the last term is the acceleration due to gravity)

Community
  • 1
  • 1
DMGregory
  • 1,307
  • 1
  • 11
  • 20
  • i'm a little bit confused `worldPos` and `screenPos`. Is the z pos is just a normal holder of height? and how do I move the arc bullet, do I need to apply force or linear velocity and also what should be the value of targetOffset. Sorry for late reply thanks. – ronscript Aug 05 '16 at 14:42
  • Yes, `z` is storing the height off the floor plane `(x,y)`. You move the bullet using the code above, positioning it each frame based on the current position of its shadow and the computed vertical movement. You don't use force or velocity to it in your 2D physics sim because its movement is 3D. The calculation of `targetOffset` is shown above in section 3. – DMGregory Aug 05 '16 at 16:23
  • when I increment the `bullet.verticalVelocity` to `bullet.worldPos.y`, the bullet is just following the shadow and the gravity is not applied. I imagine the result would be the arc I wanted right? – ronscript Aug 05 '16 at 18:21
  • 1
    The instructions above do not say to increment `bullet.worldPos.y` using `verticalVelocity`. The vertical arc happens purely in the `z` axis. – DMGregory Aug 05 '16 at 18:24
  • my mistake, currently I am using `OrthograpicCamera` this is the java doc https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/OrthographicCamera.html do you recommend to customize it? cause I am new to camera projection – ronscript Aug 05 '16 at 19:23
  • I'm not super familiar with libgdx, so you may want to ask a separate question if you want more details on achieving a particular projection. – DMGregory Aug 05 '16 at 19:27
  • sure! I will migrate the question to game-dev. – ronscript Aug 05 '16 at 19:30
  • http://gamedev.stackexchange.com/questions/127720/how-to-achieve-the-top-down-oblique-projection-in-libgdx-camera-with-a-given-x new question – ronscript Aug 05 '16 at 19:52
  • In the mean time, what camera do you recommend to use? Do you think I could use `OrthogrpichCamera` to achieve the oblique projection? – ronscript Aug 05 '16 at 19:55
  • so currently the bullet is just following the shadow, right? because in the mean time there is no projection applied. – ronscript Aug 05 '16 at 20:10
  • If your projection doesn't include the z coordinate then yes, it's a 1:1 match to the shadow. You can manually fudge the projection by adding bullet.worldPos.z to bullet.worldPos.y before rendering. – DMGregory Aug 05 '16 at 20:32
  • for now I manually add the worldPos.z to worldPos.y but still same direction, what am I missing? I'm using worldPosition x & y to render – ronscript Aug 05 '16 at 20:46
  • Sounds like your gravity constant may be too low – DMGregory Aug 05 '16 at 20:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/120280/discussion-between-ronscript-and-dmgregory). – ronscript Aug 05 '16 at 20:49
  • on section 5 how do I set and get the launch angle, because currently on my observation, the projectile launch angle is zero? – ronscript Aug 06 '16 at 16:10
  • This is not an angle-based method. To get steeper arcs, increase your gravity constant (Earth gravity isn't necessarily the right choice for every game or effect) – DMGregory Aug 06 '16 at 16:14
  • 1
    Or, reduce your projectile speed (the horizontal component) so they need to kill more time in the air. This latter change impacts gameplay though, so if you like the way the tuning is now and just want higher arcs, gravity fiddling is the way to go, since it only affects the visual effect. – DMGregory Aug 06 '16 at 16:29