Delete the sidescroller's collision, and let the library do it
collision.c was 383 lines that re-implemented akgl_physics_simulate's own arithmetic, because movementlogicfunc runs before gravity, drag and the move and so had to predict where the actor would end up. The whole file goes. What replaces it is three lines in main.c, a shape on the player and the blob, and a `collidable` property on the map's terrain layer -- which also retires SS_TERRAIN_LAYER_ID, a compiled-in layer id that broke whenever somebody reordered layers in Tiled. grounded stays a probe rather than becoming a contact test. A contact says something pushed back this step; grounded asks whether there is a floor to push off, and that stays true through the frame after landing. The blob keeps its own wall and ledge probes for the same reason: the resolver does not say which side of the blob it was pushed from, and says nothing at all about a floor that is missing. The summary line now carries the player's resting position, because exiting 0 was not evidence that collision ran. It reports 136.0,160.0 grounded after 240 autoplay frames -- feet at y=192, flush on the surface of terrain row 12. The tutorial's collision section is rewritten around the library's, keeping the quarter-of-a-pixel story as what a predicting resolver costs. The docs harness learns `json excerpt=`, so the map property the chapter quotes is checked against the map. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
This commit is contained in:
@@ -44,6 +44,23 @@
|
||||
#define SS_PATH_MAX 1024
|
||||
|
||||
ss_Game ss_game;
|
||||
akgl_CollisionWorld ss_collision;
|
||||
|
||||
akerr_ErrorContext *ss_grounded(akgl_CollisionShape *shape, float32_t x, float32_t y, bool *dest)
|
||||
{
|
||||
SDL_FRect feet;
|
||||
PREPARE_ERROR(errctx);
|
||||
|
||||
FAIL_ZERO_RETURN(errctx, shape, AKERR_NULLPOINTER, "shape");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "dest");
|
||||
|
||||
/* One pixel down, and only one: a taller probe reports a floor the actor is
|
||||
* still falling towards, and a jump that fires off it looks like the player
|
||||
* jumped out of thin air. */
|
||||
PASS(errctx, akgl_collision_shape_bounds(shape, x, y + 1.0f, &feet));
|
||||
PASS(errctx, akgl_collision_box_blocked(&ss_collision, &feet, AKGL_COLLISION_LAYER_STATIC, dest));
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The sprites, loaded in this order.
|
||||
@@ -240,7 +257,17 @@ static akerr_ErrorContext *load_level(char *assetdir)
|
||||
(akgl_physics->gravity_y / akgl_physics->drag_y));
|
||||
}
|
||||
|
||||
PASS(errctx, ss_collide_bind(akgl_gamemap));
|
||||
/*
|
||||
* Collision is opt-in: a backend with no world attached moves an actor and
|
||||
* consults nothing, which is what this game used to rely on and then spend
|
||||
* 383 lines of its own working around. The cell arguments are placeholders
|
||||
* -- akgl_collision_bind_tilemap overwrites them from the map's own tile
|
||||
* size, and reads which layers are solid off each layer's `collidable`
|
||||
* property rather than a layer id compiled into the game.
|
||||
*/
|
||||
PASS(errctx, akgl_collision_world_init(&ss_collision, NULL, (float32_t)SS_TILE_SIZE, (float32_t)SS_TILE_SIZE));
|
||||
PASS(errctx, akgl_collision_bind_tilemap(&ss_collision, akgl_gamemap));
|
||||
akgl_physics->collision = &ss_collision;
|
||||
|
||||
player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
|
||||
FAIL_ZERO_RETURN(errctx, player, AKERR_KEY, "The map placed no actor called player");
|
||||
@@ -346,6 +373,14 @@ static akerr_ErrorContext *run(int frames)
|
||||
* driver, where there is nothing to sync to. */
|
||||
SDL_Delay(16);
|
||||
}
|
||||
|
||||
/* Recorded here and not in main(): shutdown_game() runs in main()'s CLEANUP
|
||||
* block and releases the actor pool, so ss_game.player is stale by the time
|
||||
* the summary is printed. */
|
||||
if ( ss_game.player != NULL ) {
|
||||
ss_game.final_x = ss_game.player->x;
|
||||
ss_game.final_y = ss_game.player->y;
|
||||
}
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
@@ -446,11 +481,20 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
} FINISH_NORETURN(errctx);
|
||||
|
||||
/*
|
||||
* The position is in the summary because exiting 0 is not evidence that
|
||||
* collision ran. The map is 240 pixels tall and gravity is 900 px/s^2: an
|
||||
* unresolved player is hundreds of pixels below the floor within a second,
|
||||
* and this line is what makes that visible in a CI log instead of passing.
|
||||
*/
|
||||
SDL_Log(
|
||||
"sidescroller: %d frames, %d of %d coins, %d deaths",
|
||||
"sidescroller: %d frames, %d of %d coins, %d deaths, player at %.1f,%.1f%s",
|
||||
ss_game.frame,
|
||||
ss_game.coins_taken,
|
||||
SS_COIN_COUNT,
|
||||
ss_game.deaths);
|
||||
ss_game.deaths,
|
||||
ss_game.final_x,
|
||||
ss_game.final_y,
|
||||
((ss_game.grounded == true) ? " grounded" : " airborne"));
|
||||
return ss_failed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user