2
\$\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\$
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\$
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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.