Fix on_ground flickering on one-way platform tiles

The platform collision check in resolve_tilemap_y required vel.y > 0
(strictly falling), so once landing zeroed vel.y the check would fail
next frame. Gravity would add a tiny downward velocity, the platform
would catch it again, and the cycle repeated — causing animation
jitter and repeated landing dust particles.

Change the condition to vel.y >= 0 so platforms resolve when resting.
Extend the ground-stick anti-flicker probe to also detect TILE_PLATFORM
tiles (was TILE_SOLID only), with a proximity guard to preserve
jump-through-from-below behavior.
This commit is contained in:
Thomas
2026-03-01 18:33:45 +00:00
parent 302bcc592d
commit 1961cd353b

View File

@@ -76,11 +76,14 @@ static void resolve_tilemap_y(Body *body, const Tilemap *map) {
for (int tx = left; tx <= right; tx++) { for (int tx = left; tx <= right; tx++) {
uint32_t flags = tilemap_flags_at(map, tx, ty); uint32_t flags = tilemap_flags_at(map, tx, ty);
if (!(flags & TILE_SOLID)) { if (!(flags & TILE_SOLID)) {
/* Check one-way platforms: only block when falling down */ /* Check one-way platforms: block when falling or resting on top.
if ((flags & TILE_PLATFORM) && body->vel.y > 0) { * vel.y >= 0 prevents on_ground flickering when standing still —
* gravity adds a tiny downward vel each frame, the platform
* resolves it back to zero, and the cycle would repeat. */
if ((flags & TILE_PLATFORM) && body->vel.y >= 0) {
float tile_top = tile_to_world(ty); float tile_top = tile_to_world(ty);
float body_bottom = body->pos.y + body->size.y; float body_bottom = body->pos.y + body->size.y;
/* Only resolve if we just crossed into the tile */ /* Only resolve if we just crossed into the tile or are resting on it */
if (body_bottom - body->vel.y * DT <= tile_top + 2.0f) { if (body_bottom - body->vel.y * DT <= tile_top + 2.0f) {
body->pos.y = tile_top - body->size.y; body->pos.y = tile_top - body->size.y;
body->vel.y = 0; body->vel.y = 0;
@@ -134,17 +137,31 @@ void physics_update(Body *body, float dt, const Tilemap *map) {
/* Ground-stick probe: if not detected as on_ground but vel.y is /* Ground-stick probe: if not detected as on_ground but vel.y is
* near zero (just landed or standing), check one pixel below. * near zero (just landed or standing), check one pixel below.
* Prevents flickering between grounded/airborne each frame due to * Prevents flickering between grounded/airborne each frame due to
* sub-pixel gravity accumulation. */ * sub-pixel gravity accumulation. Checks both solid tiles and
* one-way platforms. */
if (!body->on_ground && body->vel.y >= 0 && body->vel.y < s_gravity * dt * 2.0f && map) { if (!body->on_ground && body->vel.y >= 0 && body->vel.y < s_gravity * dt * 2.0f && map) {
int left = world_to_tile(body->pos.x); int left = world_to_tile(body->pos.x);
int right = world_to_tile(body->pos.x + body->size.x - 1); int right = world_to_tile(body->pos.x + body->size.x - 1);
int probe = world_to_tile(body->pos.y + body->size.y + 1.0f); int probe = world_to_tile(body->pos.y + body->size.y + 1.0f);
float body_bottom = body->pos.y + body->size.y;
for (int tx = left; tx <= right; tx++) { for (int tx = left; tx <= right; tx++) {
if (tilemap_is_solid(map, tx, probe)) { uint32_t pf = tilemap_flags_at(map, tx, probe);
if (pf & TILE_SOLID) {
body->on_ground = true; body->on_ground = true;
body->vel.y = 0; body->vel.y = 0;
break; break;
} }
/* One-way platforms: only stick if feet are at or above the
* platform top. Prevents snapping to a platform the player
* is below (e.g. jumping up through it). */
if (pf & TILE_PLATFORM) {
float tile_top = tile_to_world(probe);
if (body_bottom <= tile_top + 2.0f) {
body->on_ground = true;
body->vel.y = 0;
break;
}
}
} }
} }
} }