3
\$\begingroup\$

I am trying to make a 2D top-down game where player controls a spaceship. The spaceship (a rocket) is just a sprite following the mouse cursor with a fixed speed and is always pointing toward the cursor location. I calculate rotation using atan2(yDistance, xDistance). I've already implemented movement to cursor and rotation toward cursor;

float XDistance = Xplayer - Gdx.input.getX();
float YDistance = Yplayer - (Gdx.graphics.getHeight() - Gdx.input.getY());

rotation = (float) Math.atan2(YDistance, XDistance);

if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
    if (XDistance >= playerSpeed | YDistance >= playerSpeed | XDistance <= -playerSpeed | YDistance <= -playerSpeed) {
        Xplayer -= (float) (playerSpeed * Math.cos(rotation) * deltaTime);
        Yplayer -= (float) (playerSpeed * Math.sin(rotation) * deltaTime);
    }
    MathUtils.clamp(Xplayer, 0, Gdx.graphics.getWidth());
    MathUtils.clamp(Yplayer, 0, Gdx.graphics.getHeight());
    sprite.setPosition(Xplayer - sprite.getOriginX(), Yplayer - sprite.getOriginY());
    sprite.setRotation((float) Math.toDegrees(rotation));

This algorithm makes U-turn (and every rotation in general) instant. I want to achieve a smoother rotation. Instead of instant rotation, I'd like to implement either a curve or simply a bit slower, non instant rotation (while still moving towards cursor current location).

Also, instead of fixed velocity, I am trying to implement acceleration and deceleration, but to no success (for now).

I've already googled lots of various algorithms, but never found the right one.

Update:

public class PlayerEntity {

private float movementSpeedMax;
private float rotationSpeedMax;
private float decay;
private float destinationX;
private float destinationY;

// player
private float playerX;
private float playerY;
private float rotation;

// global
private float _dx;
private float _dy;

private float _vx;
private float _vy;

private float actualRotation = 0;

public PlayerEntity(){
    movementSpeedMax = 1000;
    rotationSpeedMax = 15;
    decay = .95f;
    destinationY = 500;
    destinationX = 500;
    playerX = 0;
    playerY = 0;
    rotation = 0;
    _dx = 0;
    _dy = 0;
    _vx = 0;
    _vy = 0;
    actualRotation = 0;
}

public int getX() {
    return (int) playerX;
}

public int getY() {
    return (int) playerY;
}

public float getAngle() {
    return  rotation;
}

public void updateRotation() {
    // calculate rotation
    _dx = playerX - destinationX;
    _dy = playerY - destinationY;

    // which way to rotate
    float rotateTo = getDegrees(getRadians(_dx, _dy));

    // keep rotation positive, between 0 and 360 degrees
    if (rotateTo > rotation + 180) {
        rotateTo -= 360;
    }
    if (rotateTo < rotation - 180) {
        rotateTo += 360;
    }

    // ease rotation
    actualRotation = (rotateTo - rotation)/ rotationSpeedMax;

    // update rotation

    if (actualRotation < 180);
    rotation += actualRotation;

}

/**
 * Calculate Position
 */
public void updatePosition() {
    // check if mouse is down
    if (Gdx.input.isButtonPressed(Input.Keys.LEFT)) {
        // update destination
        destinationX = Gdx.input.getX();
        destinationY = Gdx.graphics.getHeight() - Gdx.input.getY();

        // update velocity
        _vx += (destinationX - playerX) / movementSpeedMax;
        _vy += (destinationY - playerY) / movementSpeedMax;
    }

    // apply decay (drag)

    _vx *= decay;
    _vy *= decay;

    // if close to target, slow down turn speed
    if (getLength(_dx, _dy) < 50) {
        actualRotation *= .25f;
    }

    if (getDistanceCorrect(destinationX, playerX, destinationY, playerY) < 5) {
        _vx = 0;
        _vy = 0;
    }

    // update position
    playerX += _vx;
    playerY += _vy;
}

private float getLength(float delta_x, float delta_y) {
    return (float) Math.sqrt((delta_x * delta_x) + (delta_y * delta_y));
}

private float getDistanceCorrect(float x1, float y1, float x2, float y2) {
    return (float) Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
}

private float getRadians(float delta_x, float delta_y) {
    float r = (float) Math.atan2(delta_y, delta_x);
    if (delta_y < 0) {
        r += (2 * Math.PI);
    }
    return r;

}

private float getDegrees(float radians) {
    return (float) Math.floor(radians / (Math.PI / 180));
}

}

I've created this class (based on someone's solution on actionscript). However, this implementation has very annoying bug. If the character rotates clockwise, he will instantly rotate counter-clockwise by ~360. I need help in solving this problem.

This instant rotation happens after one full rotation (rotation direction doesn't matter).

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

I suggest moving the target slowly rather than the angle. Try this:

Vector2 Target;

// Interp is a value between 0 and 1. When 0, the target never moves.
// When 1, the target moves instantaneously. Intermediate values cause the target
// to move at different rates.
void UpdateTarget(float interp)
{
    Target = interp * Mouse.Position + (1 - interp) * Target;
}

The ship can then still turn instantaneously toward the target, but the target will smoothly change with respect to the mouse.

\$\endgroup\$
0
1
\$\begingroup\$

I think any linear function would be fine for this. You can keep two variables one for your spaceship's current angle spaceshipAngle = Math.PI*k, and one for the current angle that the user inputs with his cursor cursorAngle = Math.PI*p. And what you do is

if (Math.Abs(Math.PI*k, Math.PI*p) < Math.PI)
{
    if (Math.PI*k < Math.PI*p) 
        k += 0.05f;
    else
        k -= 0.05f;
}
else
{
    if (Math.PI*k < Math.PI*p) 
        {
            spaceshipAngle += 2*Math.PI;
            k -= 0.05f;
        } 
    else
        {
            cursorAngle += 2*Math.PI;
            k+= 0.05f;
        }
}

here the "function" is k = k + 0.05 / k = k - 0.05

Note: If your spaceship looks to the top, it has Math.PI/2 angle, if it looks to the bottom it has Math.PI*3/2 angle, this is how you need to calculate angles for this code to work properly. I have not tested this out so you might see some interesting bugs (for example i don't think this will ever stabilize to an angle even if you don't move your cursor, so you need to fix that), but i think this is mathematically correct.

\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .