Xarkox
Xarkox

Reputation: 43

Physics based 2D movement: Changing direction of an object over time

I am currently creating a 2D space game in Java, in which you control a ship in, well, space. The game does not use any external libraries.

The ship is supposed to move towards the cursor. However, when moving the cursor, the old force does not magically disappear; the ship changes its course, over time, to eventually move in the desired direction.

However, I have run into an issue regarding the movement of the ship. Basically, what I want to achieve is crudely illustrated by this image:

enter image description here

The image shows how the ship is supposed to move during one game tick. To explain further:

Now I've explained what I want to achieve, now I will instead describe what I so far have achieved and how it works.

Basically, the "ship" is an image sitting in the center of the screen. When you "move" the ship, the ship stays put; what moves is the rest of the play area.

The current "position" of the ship relative to the coordinate system that represents the play area are the integers xPos and yPos.

Now for some sample code that shows how the system works:

 int xPos;
 int yPos;

 public void updateMovement() {
     xPos += xSpeed;
     yPos += ySpeed; 
 }

 public void moveForward() {
     double yTempSpeed = ySpeed;
     double xTempSpeed = xSpeed;
     yTempSpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
     xTempSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
     double resultVector = Math.sqrt(xTempSpeed * xTempSpeed + yTempSpeed * yTempSpeed);
     if (resultVector < 2) {
         ySpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
         xSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
 }

This code successfully sets the Ship's max speed to the desired value, however, this does not work (the ships' course does not change) in the event where the resulting "vector" is larger than 2, i.e. when the speed is already at it's maximum and the targetAngle is too close to the angle which the ship is currently traveling (+- Pi / 2).

How would I go about changing the current angle based on this implementation?

Upvotes: 4

Views: 1545

Answers (3)

Alnitak
Alnitak

Reputation: 339786

You may find the following (Swift) code useful, although you would need to handle the per-frame integration of the ship's linear and angular velocities yourself:

func moveShipTowards(location: CGPoint) {
    if let ship = shipNode? {

        let distanceVector = CGVector(origin: ship.position, point: location)
        let targetAngle = distanceVector.angle
        let shipAngle = ship.zRotation
        var dø = targetAngle - shipAngle

        // convert to shortest arc
        if dø > π {
            dø -= 2.0 * π
        } else if dø < -π {
            dø += 2.0 * π
        }

        // resulting angular velocity
        ship.physicsBody.angularVelocity = 12 * dø.clampedTo(π)

        var velocityUnitVector = CGVector(cosf(ship.zRotation), sinf(ship.zRotation))
        var magnitude = distanceVector.length.clampedTo(400)

        ship.physicsBody.velocity = velocityUnitVector * magnitude
    }
}

It handles having the ship deccelerate as the target point is approached. Looking at it just now, it doesn't appear to handle acceleration properly though.

Upvotes: 0

meister_reineke
meister_reineke

Reputation: 364

  public void moveForward() {
     ySpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
     xSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
     double currentSpeed = Math.sqrt(xTempSpeed * xTempSpeed + yTempSpeed * yTempSpeed);
     if (currentSpeed > maxSpeed) {
         //the resulting speed is allways <= maxspeed (normed to that)
         ySpeed *= maxSpeed/currentSpeed;
         xSpeed *= maxSpeed/currentSpeed;
 }

hope this is what you needed... although it is quite unrealistic, that a spacecraft has a maximum Speed, but in terms of "playability" i would do the same.

Upvotes: 1

misko321
misko321

Reputation: 493

What about normalizing speed of the ship, not to let it actually exceed your speed limit (=2):

//it's good to put all constants out of a function in one place
//to make it easier if you ever wanted to change it
private final int MAX_SPEED = 2;
private final double ACCEL_FACTOR = 0.01;

public void moveForward() {
 ySpeed += ACCEL_FACTOR * Math.sin(Math.toRadians(targetAngle));
 xSpeed += ACCEL_FACTOR * Math.cos(Math.toRadians(targetAngle));

 //normalize ship speed, i.e. preserve ratio of xSpeed/ySpeed
 //but make sure that xSpeed^2 + ySpeed^2 <= MAX_SPEED^2

 //your code goes here
 //...

}

Read about vector normalization. This way, the changes of ship speed will be applied normally (at this moment speed can be >= MAX_SPEED), but after normalization it will never get greater than MAX_SPEED, so your if instruction is not even needed.

Upvotes: 0

Related Questions