Wednesday, January 27, 2016

java - How do I limit the rotation speed of a mouse-following object?


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





No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...