Collision errors with velocity.rotate()

Note: This is likely only useful for strict tilemap-based maps, such as top-down RPGs.

If you have a HaxeFlixel project that uses FlxTilemap as the background or “walls” of your environment, you may encounter the issue I had. My tilemap’s tiles are 16x16 pixels width and height, and the player’s sprite is the same. This can result in some odd behaviour when using the movement code found in the HaxeFlixel tutorial project.

Specifically, when moving in certain directions right up against a “wall” in your tilemap, the player can stop when they should continue moving and have the room to do so. In my problem, it only occurred when there was a wall tile to the right of the player’s current facing direction - that is, if the player moved upwards, they would collide with a wall tile to their top-right corner.

Here’s the code as used in the tutorial:

1
2
velocity.set(SPEED, 0);
velocity.rotate(FlxPoint.weak(0, 0), newAngle);

As explained in the tutorial, newAngle is an Int containing the angle we want the player to face, going clockwise up to 360 (it also works for down to -360). 0 degrees equals right, so facing downwards would be 90 and so on. velocity.rotate uses this angle to calculate the X and Y components of the new player velocity, so they move in the correct direction. This is useful to ensure players don’t go faster than they should when moving in diagonal directions, for instance. It also allows for easier analogue movement, if you choose to enable that in your game.

However, that bit of code is also the cause of our problem. velocity is a floating point value, meaning it can hold decimal values. It may be a bug in the current implementation (using HaxeFlixel version 4.1.5), but when calculating the new velocity of the player using nice clean integer angles like 90 or 180, we should get something like velocity.x == 100 and velocity.y == 0. What happens instead is that the component that should be returning 0 instead returns a tiny fraction such as velocity.y == 0.00000001. This causes the game engine to see a collision along certain tiles, where the game looks as though it shouldn’t be.

To fix this, there are two options.

The first and easier option is simply to round the velocity value to an integer. We can do this using Haxe’s Math.round() function, like so:

1
2
velocity.x = Math.round(velocity.x);
velocity.y = Math.round(velocity.y);

The second is instead of using the above code snippet and setting newAngle to the correct value for each directional input, I set the velocity itself in that input code. Here’s my current code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (up)
{
    facing = FlxObject.UP;
    velocity.set(0, -SPEED);
}
else if (down)
{
    facing = FlxObject.DOWN;
    velocity.set(0, SPEED);
}
else if (left)
{
    facing = FlxObject.LEFT;
    velocity.set(-SPEED, 0);
}
else if (right)
{
    facing = FlxObject.RIGHT;
    velocity.set(SPEED, 0);
}

We test, and…

Voilà! Ghost collisions are gone. Thanks to users from the Haxe official Discord channel for solving the problem and assisting me.