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:
@@ -75,15 +75,19 @@ static akerr_ErrorContext *ss_static_movement(akgl_Actor *obj, float32_t dt)
|
||||
* @brief The blob: walk, and turn around at a wall or a ledge.
|
||||
*
|
||||
* This one stays inside the physics step -- it walks on the ground under the
|
||||
* map's gravity like the player does, and uses the same swept resolution.
|
||||
* map's gravity like the player does, and the library resolves it the same way.
|
||||
* All this hook decides is which way to face next.
|
||||
*/
|
||||
static akerr_ErrorContext *ss_blob_movement(akgl_Actor *obj, float32_t dt)
|
||||
{
|
||||
ss_ActorData *data = NULL;
|
||||
ss_Contact contact;
|
||||
SDL_FRect ahead;
|
||||
float32_t probe_x = 0.0f;
|
||||
float32_t probe_y = 0.0f;
|
||||
float32_t step = 0.0f;
|
||||
bool floor_ahead = false;
|
||||
bool wall_ahead = false;
|
||||
bool grounded = false;
|
||||
PREPARE_ERROR(errctx);
|
||||
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj");
|
||||
@@ -99,7 +103,18 @@ static akerr_ErrorContext *ss_blob_movement(akgl_Actor *obj, float32_t dt)
|
||||
/* Signs the acceleration from the movement bits just set. */
|
||||
PASS(errctx, akgl_actor_logic_movement(obj, dt));
|
||||
|
||||
PASS(errctx, ss_collide_resolve(obj, &ss_blob_body, dt, &contact));
|
||||
step = 1.0f;
|
||||
if ( data->facing < 0.0f ) {
|
||||
step = -1.0f;
|
||||
}
|
||||
|
||||
/* Three probes, all taken from where the last step left the blob. The blob
|
||||
* turns on a wall or a ledge, and a contact from the resolver answers
|
||||
* neither question: it says something pushed back, not which side of the
|
||||
* blob it was on, and it says nothing at all about a floor that is missing. */
|
||||
PASS(errctx, akgl_collision_shape_bounds(&obj->shape, (obj->x + step), obj->y, &ahead));
|
||||
PASS(errctx, akgl_collision_box_blocked(&ss_collision, &ahead, AKGL_COLLISION_LAYER_STATIC, &wall_ahead));
|
||||
PASS(errctx, ss_grounded(&obj->shape, obj->x, obj->y, &grounded));
|
||||
|
||||
/* One pixel past the leading edge of the box and one pixel below its feet:
|
||||
* no floor there means the next step walks off. */
|
||||
@@ -109,12 +124,13 @@ static akerr_ErrorContext *ss_blob_movement(akgl_Actor *obj, float32_t dt)
|
||||
} else {
|
||||
probe_x = obj->x + ss_blob_body.x + ss_blob_body.w + 1.0f;
|
||||
}
|
||||
PASS(errctx, ss_collide_solid_at(probe_x, probe_y, &floor_ahead));
|
||||
PASS(errctx, akgl_collision_solid_at(&ss_collision, probe_x, probe_y, &floor_ahead));
|
||||
|
||||
if ( (contact.blocked_x == true) || ((contact.grounded == true) && (floor_ahead == false)) ) {
|
||||
if ( (wall_ahead == true) || ((grounded == true) && (floor_ahead == false)) ) {
|
||||
data->facing = -data->facing;
|
||||
/* The resolution already zeroed `tx` on a blocked axis; zero it on the
|
||||
* ledge path too, or the blob keeps its old momentum through the turn. */
|
||||
/* The library's response removes the component of `tx` going into a
|
||||
* wall, but nothing removes it on the ledge path -- and without this the
|
||||
* blob keeps its old momentum through the turn. */
|
||||
obj->tx = 0.0f;
|
||||
}
|
||||
SUCCEED_RETURN(errctx);
|
||||
@@ -165,7 +181,10 @@ akerr_ErrorContext *ss_actors_bind(void)
|
||||
}
|
||||
|
||||
PASS(errctx, find_actor("blob1", &ss_game.hazards[0]));
|
||||
PASS(errctx, ss_collide_settle(ss_game.hazards[0], &ss_blob_body));
|
||||
PASS(errctx, akgl_collision_shape_box(&ss_game.hazards[0]->shape, &ss_blob_body, 0.0f));
|
||||
ss_game.hazards[0]->shape_override = true;
|
||||
PASS(errctx, akgl_collision_settle(&ss_collision, &ss_game.hazards[0]->shape,
|
||||
&ss_game.hazards[0]->x, &ss_game.hazards[0]->y, 0));
|
||||
ss_hazard_data[0].home_x = ss_game.hazards[0]->x;
|
||||
ss_hazard_data[0].home_y = ss_game.hazards[0]->y;
|
||||
ss_hazard_data[0].facing = -1.0f;
|
||||
|
||||
Reference in New Issue
Block a user