0
\$\begingroup\$

I'm creating a small game to learn how NodeJS & Sails work (and by extension Waterline, Socket.io, Express, and all their components).

I'm using websockets to make the server calculate the coordinates. Everything is handled server-side.
The client only receives serialized data in order to redraw the whole canvas.

For debugging purposes, I decided not to redraw but simply "draw" (leaving the old canvas visible) to show you the result.

See below for situation, issue and code.

The issue

I'm having trouble when calculating coordinates.

What I need is a simple forward/backwards system, and when pressing left or right keys, change the direction angle so the character can move in the canvas.

Kinda like the good'ol Asteroids retro games.

The issue is the following:

enter image description here

The drawn loops you see shouldn't exist. In fact, they should be circles. I drew this by simply pressing either up or down key, combined with one of left or right keys.

I think there is an issue in the calculation system...

This shouldn't be a performance issue because each tick occurs at the same interval, so even if it was a performance issue, it should only be related to delay when redrawing the canvas, not such an offset in coordinates calculation...

Any idea ?

Info about code api

  • moves contains a javascript object like this:
    { left:false, right: false, up: false, down: false }
    It is used to check whether the user is "pressing" associated arrow keys.
    It is updated each time there is a keyUp or keyDown event client-side.

  • user.pick contains the "physical object" to be rendered in the canvas, which contain x, y coordinates, and information about movement speed and rotation speed.

Here's the code that handles the coordinates calculation.

// Change angle if "left" or "right" is pushed
if (moves.left || moves.right) {
    // let PI2 = 2 * PI;
    let angle = user.pick.angle;

    if (moves.left) {
        angle += user.pick.angleSpeed;
    } else if (moves.right) {
        angle -= user.pick.angleSpeed;
    }

    // Use this to avoid having huge integers to manage
    if (angle < 0) {
        angle = 360 - angle;
    }
    if (angle > 360) {
        angle = angle - 360;
    }

    user.pick.angle = parseInt(angle);
}

// Up and down allow us to move either forwards or backwards.
if (moves.up || moves.down) {

    let moveRatio = moves.up ? 1 : -1;

    let angleRadians = user.pick.angle * (PI / 180);
    let x = user.pick.x + moveRatio * user.pick.speed * Math.sin(angleRadians);
    let y = user.pick.y + moveRatio * user.pick.speed * Math.cos(angleRadians);

    // Avoids collisions with canvas walls
    if (x > 0 && x < user.map.width) {
        user.pick.x = parseInt(x);
    }
    if (y > 0 && y < user.map.height) {
        user.pick.y = parseInt(y);
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ At a glance, there's a bug in how you handle negative angles — it should be angle + 360 not 360 - angle. For example, let angle be -30. Our expected output is +330. This code gives us 360 - (-30) = 390, then the next if triggers and we get 390 - 360 = 30, so our angle has been inverted. I don't think this is the whole cause of the error you observe, but it's low-hanging fruit to fix. \$\endgroup\$ – DMGregory Nov 23 '16 at 20:14
  • \$\begingroup\$ You're totally right, I couldn't figure out why there was this bug. I fixed this by using a modulo instead, which is much simpler (but still it drives me crazy as I'm not an expert mathematician). You can see this in my big and exhaustive answer :) \$\endgroup\$ – Alex Rock Nov 24 '16 at 10:03
0
\$\begingroup\$

After having received help in private, let me post the answer here (and thanks a lot for the ones that helped me on Twitter :) )

First change: angle udpate

A friend of mine who is a mathematician told me that the angle security could be handled with a simple modulo.

He gave me the hint to remove this code:

if (angle < 0) {
    angle = 360 - angle;
}
if (angle > 360) {
    angle = angle - 360;
}

And instead, replace it with this code:

if (angle <= 0 || angle >= 360) {
    angle %= 360;
}

And it works very well!

Because the issue with the first code was that when the angle < 0, it oscillated between 0 and -speed (meaning -10 in my case), so the pointer was moving down and not rotating.

Second change (and the most important): values rounding

The same friend, and also @voidqk on Twitter, told me to stop rounding values.

This is a great advice because I was not rounding values at all. I was parsing them to int.

Meaning I had this:

console.info(
    parseInt(1.2), // 1
    parseInt(1.6), // 1
    parseInt(-1.2), // -1
    parseInt(-1.6) // -1
);

This was totally incorrect.

I replaced everything with Math.round() and now it rounds values in a proper way:

console.info(
    Math.round(1.2), // 1
    Math.round(1.6), // 2
    Math.round(-1.2), // -1
    Math.round(-1.6) // -2
);

The parseInt() method introduced an incorrect offset in negative coordinates, whereas Math.round() does the job very well and now when I move my pointer and push the left arrow key, I can now draw circles :D

Proper circles

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