简体   繁体   中英

8-direction consistent movement speed in all directions, how to get angle of movement from direction keys?

Top of the morning to ye people on various surfaces of earth.

The problem: How to get the angle of direction from the arrow keys.

Preamble: The standard way to move in a basic top-down game is to add to x and/or y from within an update method, depending on the direction.

if (moveUp)     playerY -= delta * speed;
if (moveDown)   playerY += delta * speed;
if (moveLeft)   playerX -= delta * speed;
if (moveRight)  playerX += delta * speed;

This is elegant for 4-direction movement (I believe) because no matter what key combinations are pressed, the direction of movement will be consistent. Eg pressing up-down-left will move left, as up and down cancel out. But when moving diagonally, the steps will be too large. If speed is 20, moving left will move left by 20 per second, up will move up by 20 per second. But moving up-left will move by a little over 28.24 per second.

The solution here is to use cos and sin to get the new x and y, which is easy once you know the angle:

playerX += Math.cos(Math.toRadians(angle)) * delta * speed;
playerY -= Math.sin(Math.toRadians(angle)) * delta * speed; //y is inverted

But, for me at least, this raises a new problem: what's the angle? In my KeyListener I'm currently setting/clearing booleans for each arrow key. I can use a bulky set of if statements:

if (moveUp)     angle = 90;
if (moveDown)   angle = 270;
if (moveRight)  angle = 0;
if (moveLeft)   angle = 180;
if (moveUp && moveLeft)     angle = 135;
if (moveUp && moveRight)    angle = 45;
if (moveDown && moveLeft)   angle = 225;
if (moveDown && moveRight)  angle = 315;
//...etc... for all combinations

For the life of me, I cannot find a sexy way to get the movement angle from what direction keys are pressed down. It strikes me like this should be a common problem, game design 101, but intense googling hasn't led me to anything (made harder by the fact that it's difficult to put the problem into words). In all instances of examples, either they just retained the diagonal-is-faster functionality (as with my first snippet), or know the angle ahead of time (ie. move towards the mouse), or are 2D side scrollers.

Surely there's a sexy mathy way (or something) to work it out in a few lines? Or am I approaching this completely wrong?

Edit: Post-answer code (as posted by korona below):

double x=0, y=0;
if (moveLeft) x -= 1;
if (moveRight) x += 1;
if (moveUp) y -= 1;
if (moveDown) y += 1;

double length = Math.sqrt(x * x + y * y);
if (length != 0) {
    x /= length;
    y /= length;

    x *= delta*speed;
    y *= delta*speed;

    playerX += x;
    playerY += y;
}

Use a 2-dimensional vector. Something like this:

movement = new Vector2D();

if (moveLeft) movement.x += 1;
if (moveRight) movement.x -= 1;
if (moveUp) movement.y -= 1;
if (moveDown) movement.y += 1;

movement.normalize();   // Caps the movement vector at a length of one, even when it's at an odd angle
movement *= desiredMovementSpeed * frameDeltaTime;  // Plug in suitable values here
// FIXME: Do some collision detection here, probably
playerX += movement.x;
playerY += movement.y;

I assume there's a suitable 2D vector class available for you. If not, normalizing a vector is as easy as dividing all of its components by its length, as such:

length = sqrt(this.x * this.x + this.y * this.y);
this.x /= length;
this.y /= length;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM