Mosaic-based collision detection errors if the player is smaller than the tiles

I have implemented a tilemap where each tile is in a position x and not negative. (So ​​for the moment think like chess)

At the moment a tile is 16, 16 pixels in size

Tile 0.0 it's in the lower left corner

Some tiles must be colidable. I have never implemented something like that and during the investigation I came across: https://jonathanwhiting.com/tutorial/collision/

The general idea + handling of collision response:

let mut new_pos = Point2 :: new (curr_pos.x + x_offset, curr_pos.y);

let's collision_on_x = collide_with_world (& new_pos, & world);

yes! colision_on_x {
transform.translate_x (x_offset);
}

new_pos.y = curr_pos.y + y_offset;

let's collision_on_y = collide_with_world (& new_pos, & world);

yes! colision_on_y {
transform.translate_y (y_offset);
}

Collision detection

fn collide_with_world (new_pos: & Point2, World: & World) -> bool {
// MAKE ALL this size of i16 so we can grow in any direction!
let mut left_tile: u16 = (new_pos.x / TILE_SIZE_IN_PIXELS as f32) as u16;
// -1 so we do not crash until we're exactly on the thing
let mut right_tile: u16 = ((new_pos.x + PLAYER_WIDTH - 1.0) / TILE_SIZE_IN_PIXELS as f32) as u16;
let mut top_tile: u16 = ((new_pos.y + PLAYER_HEIGHT - 1.0) / TILE_SIZE_IN_PIXELS as f32) as u16;
let mut bottom_tile: u16 = (new_pos.y / TILE_SIZE_IN_PIXELS as f32) as u16;

println! ("new_pos: {:?}, left {}, right {}, top {}, bottom {}", (new_pos.x, new_pos.y), left_tile, right_tile, top_tile, bottom_tile);

// Useless as long as we use unsigned coordinates.
// if left_tile < 0 {
    //    left_tile = 0;
    //}

    if right_tile > WORLD_WIDTH_IN_TILES {
right_tile = WORLD_WIDTH_IN_TILES;
}

// Useless as long as we use unsigned coordinates.
// if bottom_tile < 0 {
    //    bottom_tile = 0;
    //}

    if top_tile > WORLD_HEIGHT_IN_TILES {
top_tile = WORLD_HEIGHT_IN_TILES;
}

let's mut any_collision = false;

for i in left_tile .. = right_tile {
for j in bottom_tile .. = top_tile {
let tile = world.get_tile (i, j);
println! ("tile in {:?} is {:?}", (i, j), tile);

match tile
Some (tile) => {
if tile == WALL {
println! ("hit a wall in {:?}!", (i, j));
any_collision = true;
}
}
None => (),
}
}
}

returns any_collision;
}

Basically I took (or tried to take) the optimized version of the linked blog post.

Now to the problems:

Yes PLAYER_WIDTH Y PLAYER_HEIGHT are both 16.0 – the collision works well

But note that I had to put -1.0 in calculating the right_tile Y top_tile, otherwise, the collision of UP and RIGHT would occur between a pixel and the start (so that the player never touches the walls at the top or right)

Only this change makes me a little suspicious: it seems that I made a mistake there.

The real problems start if the player is smaller than a chip:

In the following screenshots the following rules apply:

*) the PLAYER_WIDTH Y PLAYER_HEIGHT are both 8
*) Greenish tiles are the only walls!
*) All screenshots were taken in ONE run, no parameters were changed during this run!

one) Trying to hit the right tab -> Trying to hit the right tile -> overlay
two) Trying to hit the top tile -> Trying to hit the top tile -> you can not get closer than 4 pixels (half the height of the player)
3) Trying to hit the left tab -> Trying to hit the left tile -> can not get closer than 4 pixels (half the width of the player)
4) Trying to hit the top tile -> Trying to collide with the upper tile -> suddenly overlapping, even if a different tile was not hit?
5) Trying to hit the bottom tile -> Trying to hit the bottom tile -> can not get closer than 4 pixels

I can not grab 2 and 4 at this time. That's what's driving me crazy and here I am.

Someone smarter than me, please take a look!