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:
@@ -5,7 +5,6 @@
|
||||
* The game is four translation units and this is the seam between them:
|
||||
*
|
||||
* main.c startup, asset loading, the frame loop, teardown
|
||||
* collision.c the tile queries and the swept resolution libakgl does not have
|
||||
* player.c the player's hooks and its control bindings
|
||||
* actors.c the coins, the patrolling blob, the flying moth
|
||||
*
|
||||
@@ -23,8 +22,10 @@
|
||||
#include <akerror.h>
|
||||
|
||||
#include <akgl/actor.h>
|
||||
#include <akgl/collision.h>
|
||||
#include <akgl/error.h>
|
||||
#include <akgl/game.h>
|
||||
#include <akgl/physics.h>
|
||||
#include <akgl/tilemap.h>
|
||||
#include <akgl/types.h>
|
||||
|
||||
@@ -38,16 +39,6 @@
|
||||
#define SS_VIEW_HEIGHT 240 /* Camera height; the whole map is this tall */
|
||||
#define SS_WINDOW_SCALE 2 /* Integer upscale from the view to the window */
|
||||
|
||||
/*
|
||||
* Which layer of the map is solid.
|
||||
*
|
||||
* akgl_TilemapLayer records Tiled's numeric layer `id` and does *not* record the
|
||||
* layer's name -- akgl_tilemap_load_layers reads `id`, `opacity`, `visible`, `x`,
|
||||
* `y` and `type`, and nothing else. So a game cannot ask for "the layer called
|
||||
* terrain"; it matches on the id Tiled assigned, which for level1.tmj is 2.
|
||||
*/
|
||||
#define SS_TERRAIN_LAYER_ID 2
|
||||
|
||||
/* The player's collision box, as an offset into its 32x32 sprite frame. The art
|
||||
* does not fill the frame edge to edge, and a box the full width of the frame
|
||||
* catches on doorways the character visibly clears. */
|
||||
@@ -81,19 +72,6 @@
|
||||
*/
|
||||
#define SS_AUTOPLAY_KBID 11
|
||||
|
||||
/**
|
||||
* @brief What one call to ss_collide_resolve found.
|
||||
*
|
||||
* `blocked_x` and `blocked_y` say the sweep stopped the actor on that axis this
|
||||
* step; `grounded` says there is solid terrain one pixel under where the actor
|
||||
* will be when the step finishes, which is the test a jump is gated on.
|
||||
*/
|
||||
typedef struct {
|
||||
bool blocked_x;
|
||||
bool blocked_y;
|
||||
bool grounded;
|
||||
} ss_Contact;
|
||||
|
||||
/**
|
||||
* @brief Per-actor game data, hung off akgl_Actor::actorData.
|
||||
*
|
||||
@@ -120,17 +98,31 @@ typedef struct {
|
||||
bool grounded; /**< Last step's verdict; what gates the next jump. */
|
||||
bool autoplay; /**< Drive the player from a script instead of the keyboard. */
|
||||
int frame; /**< Frames drawn so far. */
|
||||
float32_t final_x; /**< Where the player finished, stamped before teardown for the summary line. */
|
||||
float32_t final_y;
|
||||
} ss_Game;
|
||||
|
||||
extern ss_Game ss_game;
|
||||
|
||||
/* collision.c -- everything akgl_physics_arcade_collide would have done. */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_collide_bind(akgl_Tilemap *map);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_collide_solid_at(float32_t x, float32_t y, bool *dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_collide_box_blocked(SDL_FRect *box, bool *dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_collide_predict(akgl_Actor *obj, float32_t dt, float32_t *dx, float32_t *dy);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_collide_resolve(akgl_Actor *obj, SDL_FRect *body, float32_t dt, ss_Contact *dest);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_collide_settle(akgl_Actor *obj, SDL_FRect *body);
|
||||
/**
|
||||
* @brief The collision world, owned by main.c.
|
||||
*
|
||||
* The physics backend resolves through it after every sub-move; the game only
|
||||
* touches it to ask questions -- a ledge probe, a wall probe, a spawn point that
|
||||
* needs lifting clear. Everything that used to be in collision.c is behind it.
|
||||
*/
|
||||
extern akgl_CollisionWorld ss_collision;
|
||||
|
||||
/**
|
||||
* @brief Is there solid geometry one pixel under this shape?
|
||||
*
|
||||
* The predicate a jump is gated on, and it is deliberately *not* the same
|
||||
* question as "did the last step's resolution stop me falling". A contact says
|
||||
* something pushed back this step; this says there is a floor to push off, which
|
||||
* stays true through the frame after landing and is what makes a jump feel like
|
||||
* it registered.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_grounded(akgl_CollisionShape *shape, float32_t x, float32_t y, bool *dest);
|
||||
|
||||
/* player.c */
|
||||
akerr_ErrorContext AKERR_NOIGNORE *ss_player_bind(akgl_Actor *obj);
|
||||
|
||||
Reference in New Issue
Block a user