forked from tas/major_tom
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:
@@ -76,11 +76,14 @@ static void resolve_tilemap_y(Body *body, const Tilemap *map) {
|
||||
for (int tx = left; tx <= right; tx++) {
|
||||
uint32_t flags = tilemap_flags_at(map, tx, ty);
|
||||
if (!(flags & TILE_SOLID)) {
|
||||
/* Check one-way platforms: only block when falling down */
|
||||
if ((flags & TILE_PLATFORM) && body->vel.y > 0) {
|
||||
/* Check one-way platforms: block when falling or resting on top.
|
||||
* 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 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) {
|
||||
body->pos.y = tile_top - body->size.y;
|
||||
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
|
||||
* near zero (just landed or standing), check one pixel below.
|
||||
* 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) {
|
||||
int left = world_to_tile(body->pos.x);
|
||||
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);
|
||||
float body_bottom = body->pos.y + body->size.y;
|
||||
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->vel.y = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user