diff options
Diffstat (limited to 'docs/api.org')
| -rw-r--r-- | docs/api.org | 109 |
1 files changed, 63 insertions, 46 deletions
diff --git a/docs/api.org b/docs/api.org index e60ef7d..673749f 100644 --- a/docs/api.org +++ b/docs/api.org @@ -23,6 +23,7 @@ The engine module provides the top-level game lifecycle and state management. (preload #f) (create #f) (update #f) + (engine-update 'default) (render #f) (debug? #f)) #+end_src @@ -39,7 +40,8 @@ Creates and initializes a game object. All parameters are optional keywords. | ~input-config~ | input-config | *default-input-config* | Keyboard/controller mappings | | ~preload~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called once before create | | ~create~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called once at startup | -| ~update~ | procedure/false | #f | Hook: ~(lambda (game dt) ...)~ called each frame | +| ~engine-update~ | procedure/false / ~'default~ | ~'default~ | Built-in physics pipeline (~default-engine-update~), ~#f~ to disable, or ~(lambda (game dt) ...)~ to replace | +| ~update~ | procedure/false | #f | Hook: ~(lambda (game dt) ...)~ called each frame *after* ~engine-update~ (see ~game-run!~) | | ~render~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called after render-scene! | | ~debug?~ | boolean | #f | Enable debug overlay drawing (collision boxes) | @@ -61,6 +63,18 @@ This affects everything uniformly: tiles, sprites, text, colored rectangles, and Only positive integers are accepted; fractional or zero values signal an error. +*** ~engine-update:~ and ~default-engine-update~ + +Omit ~engine-update:~ (or pass ~engine-update: 'default~) to use ~default-engine-update~: the standard per-frame physics pipeline on the current scene. Pass ~engine-update: #f~ if your game does not use the built-in pipeline (for example a shmup or menu that moves entities entirely in ~update:~). Pass a custom procedure ~(lambda (game dt) ...)~ to run your own integration step instead; it should read and write the scene via ~(game-scene game)~ and ~(game-scene-set! game scene)~ like ~default-engine-update~ does. + +#+begin_src scheme +(default-engine-update game dt) +#+end_src + +Runs the built-in pipeline once. Order: ~step-tweens~ → ~apply-acceleration~ → ~apply-gravity~ → ~apply-velocity-x~ → ~resolve-tile-collisions-x~ → ~apply-velocity-y~ → ~resolve-tile-collisions-y~ → ~detect-on-solid~ → ~resolve-entity-collisions~ (whole entity list) → ~sync-groups~ (whole entity list). Per-entity steps use signature ~(entity scene dt)~; bulk steps use ~(entities)~ with ~scene-transform-entities~. + +**Typical ~update:~ pattern:** treat ~update:~ as game logic only: read input, set *intent* on entities (~#:vx~, ~#:vy~, one-shot ~#:ay~ for jumps, flags), animation, and sound. Each frame, ~engine-update~ runs *before* ~update:~, so your hook sees positions and ~#:on-ground?~ after that frame’s integration and collisions. The ~#:vx~ / ~#:vy~ / ~#:ay~ you set in ~update:~ are applied starting on the following frame’s ~engine-update~. + *** Fullscreen Downstroke does not provide a built-in ~fullscreen:~ keyword, but you can make any game fullscreen by setting the SDL2 window flag after the game starts. Use the ~preload:~ hook (the window exists by then): @@ -90,15 +104,17 @@ Combine with ~scale:~ to get pixel-perfect fullscreen: set your logical resoluti Starts the main event loop. Initializes SDL2, opens the window (at ~width×scale~ by ~height×scale~ pixels), sets the logical render size when ~scale~ > 1, and runs the frame loop indefinitely until the user quits or the ~quit~ action is pressed. Never returns. -Lifecycle order within each frame: +Lifecycle order within each frame (while not quitting): 1. Collect SDL2 events 2. Update input state -3. Call ~update:~ hook (or active state's ~update~) -4. Set the renderer clear color from the current scene's ~background:~ (see ~make-scene~), then clear the framebuffer (~#f~ or invalid value uses opaque black) -5. Call ~render-scene!~ (if scene is set) -6. Call ~render:~ hook (or active state's ~render~) -7. Present renderer -8. Apply frame delay +3. Call ~engine-update-hook~ if set (~default-engine-update~ runs the physics pipeline: tweens, acceleration, gravity, velocity, collisions, ground detection, entity collisions, group sync) +4. Call ~update:~ hook (or active state's ~update~) — runs *after* physics, so logic sees resolved positions and ~#:on-ground?~ +5. Apply camera follow (~update-scene~ with ~camera-target~, if set) +6. Set the renderer clear color from the current scene's ~background:~ (see ~make-scene~), then clear the framebuffer (~#f~ or invalid value uses opaque black) +7. Call ~render-scene!~ (if scene is set) +8. Call ~render:~ hook (or active state's ~render~) +9. Present renderer +10. Apply frame delay ** ~game-camera~ @@ -269,11 +285,13 @@ Example: (define (increment-x entity) (entity-set entity #:x (+ 1 (entity-ref entity #:x 0)))) -(define (apply-gravity entity) - (entity-set entity #:vy (+ 1 (entity-ref entity #:vy 0)))) +;; Per-entity physics steps need scene and dt (e.g. from your update: hook): +(define dt 16) ; example: ms since last frame -(scene-map-entities scene increment-x apply-gravity) -; Each entity is passed through increment-x, then through apply-gravity +(scene-map-entities scene + increment-x + (lambda (e) (apply-gravity e scene dt))) +; Each entity is passed through increment-x, then apply-gravity with fixed scene/dt #+end_src ** ~scene-transform-entities~ @@ -432,7 +450,7 @@ Returns true if ~step-symbol~ appears in the entity’s ~#:skip-pipelines~ list The ~guard:~ clause is optional. When present, ~guard-expr~ is evaluated first; if it is false, the entity is returned unchanged and ~body ...~ does not run. When absent, the body applies to all entities (subject only to the skip-symbol check below). -Syntax for authors of per-entity pipeline steps: expands to a ~define~ that returns the **first** formal (the entity) unchanged when ~skip-symbol~ is listed in ~#:skip-pipelines~; otherwise, if a guard is present and fails, returns the entity unchanged; otherwise runs ~body ...~ inside ~(let () ...)~. Used throughout ~downstroke-physics~; other modules can use it for consistent skip behavior. The procedure name and skip symbol differ when needed (e.g. ~detect-on-solid~ vs ~on-solid~). +Syntax for authors of per-entity pipeline steps: expands to a ~define~ that returns the **first** formal (the entity) unchanged when ~skip-symbol~ is listed in ~#:skip-pipelines~; otherwise, if a guard is present and fails, returns the entity unchanged; otherwise runs ~body ...~ inside ~(let () ...)~. Used throughout ~downstroke-physics~ and ~step-tweens~ in ~downstroke-tween~; other modules can use it for consistent skip behavior. Extra formals after the entity are typically ~scene~ and ~dt~ so steps match ~(entity scene dt)~. The procedure name and skip symbol differ when needed (e.g. ~detect-on-solid~ vs ~on-solid~). ** Shared Entity Keys @@ -465,21 +483,24 @@ All entities can have these keys. Not all are required: (import downstroke-physics) #+end_src -The physics module provides functions for movement, collision detection, and ground sensing. Call them manually in your ~update:~ hook in the order that suits your game type (see ~docs/physics.org~ for examples). +The physics module provides functions for movement, collision detection, and ground sensing. With the default ~engine-update:~ hook (~default-engine-update~), they run automatically each frame in a fixed order. You can still call them yourself when building a custom ~engine-update:~ or when experimenting in the REPL (see ~docs/physics.org~). -** Physics Pipeline Order +** Physics Pipeline Order (~default-engine-update~) -The built-in physics functions are normally run in this order each frame (after reading input, before rendering): +The built-in pipeline runs this order each frame (inside ~engine-update~, after input, before ~update:~): -1. ~apply-jump~ — if jump pressed and on ground, set ~#:ay~ +1. ~step-tweens~ — advance ~#:tween~ (see ~downstroke-tween~) 2. ~apply-acceleration~ — consume ~#:ay~ into ~#:vy~ 3. ~apply-gravity~ — add gravity to ~#:vy~ 4. ~apply-velocity-x~ — move by ~#:vx~ -5. ~resolve-tile-collisions-x~ — snap against horizontal tile collisions +5. ~resolve-tile-collisions-x~ — snap against horizontal tile collisions (uses ~scene-tilemap~) 6. ~apply-velocity-y~ — move by ~#:vy~ 7. ~resolve-tile-collisions-y~ — snap against vertical tile collisions -8. ~detect-on-solid~ — set ~#:on-ground?~ if standing on a tile and/or another solid entity (optional third argument) -9. ~resolve-entity-collisions~ — push apart solid entities (whole list) +8. ~detect-on-solid~ — set ~#:on-ground?~ from tiles below and/or other solids in ~scene-entities~ +9. ~resolve-entity-collisions~ — push apart solid entities (whole list; use ~scene-transform-entities~) +10. ~sync-groups~ — align grouped entities to origins (whole list) + +Per-entity steps take ~(entity scene dt)~. Jumping is *not* a built-in step: set ~#:ay~ in your ~update:~ hook (e.g. ~(entity-set player #:ay (- *jump-force*))~) when the player jumps; ~apply-acceleration~ consumes it on the following ~engine-update~. Entities may list ~#:skip-pipelines~ to omit specific steps; see ~entity-skips-pipeline?~ under ~downstroke-entity~ and ~docs/physics.org~. @@ -488,15 +509,15 @@ Entities may list ~#:skip-pipelines~ to omit specific steps; see ~entity-skips-p ** ~apply-acceleration~ #+begin_src scheme -(apply-acceleration entity) +(apply-acceleration entity scene dt) #+end_src -Consumes ~#:ay~ (one-shot acceleration) into ~#:vy~ and clears ~#:ay~ to 0. Only applies if ~#:gravity?~ is true. +Consumes ~#:ay~ (one-shot acceleration) into ~#:vy~ and clears ~#:ay~ to 0. Only applies if ~#:gravity?~ is true. ~scene~ and ~dt~ are accepted for pipeline uniformity; this step does not use them. ** ~apply-gravity~ #+begin_src scheme -(apply-gravity entity) +(apply-gravity entity scene dt) #+end_src Adds the gravity constant (1 pixel/frame²) to ~#:vy~. Only applies if ~#:gravity?~ is true. @@ -504,7 +525,7 @@ Adds the gravity constant (1 pixel/frame²) to ~#:vy~. Only applies if ~#:gravit ** ~apply-velocity-x~ #+begin_src scheme -(apply-velocity-x entity) +(apply-velocity-x entity scene dt) #+end_src Updates ~#:x~ by adding ~#:vx~. Returns a new entity. @@ -512,7 +533,7 @@ Updates ~#:x~ by adding ~#:vx~. Returns a new entity. ** ~apply-velocity-y~ #+begin_src scheme -(apply-velocity-y entity) +(apply-velocity-y entity scene dt) #+end_src Updates ~#:y~ by adding ~#:vy~. Returns a new entity. @@ -528,15 +549,15 @@ Legacy function: updates both ~#:x~ and ~#:y~ by their respective velocities. ** ~resolve-tile-collisions-x~ #+begin_src scheme -(resolve-tile-collisions-x entity tilemap) +(resolve-tile-collisions-x entity scene dt) #+end_src -Detects and resolves collisions between the entity's bounding box and solid tiles along the X axis. Snaps the entity's ~#:x~ to the near/far tile edge and sets ~#:vx~ to 0. Returns a new entity. +Detects and resolves collisions between the entity's bounding box and solid tiles along the X axis using ~(scene-tilemap scene)~. If there is no tilemap, the step is skipped. Snaps the entity's ~#:x~ to the near/far tile edge and sets ~#:vx~ to 0. Returns a new entity. ** ~resolve-tile-collisions-y~ #+begin_src scheme -(resolve-tile-collisions-y entity tilemap) +(resolve-tile-collisions-y entity scene dt) #+end_src Detects and resolves collisions between the entity's bounding box and solid tiles along the Y axis. Snaps the entity's ~#:y~ to the near/far tile edge and sets ~#:vy~ to 0. Returns a new entity. @@ -544,18 +565,10 @@ Detects and resolves collisions between the entity's bounding box and solid tile ** ~detect-on-solid~ #+begin_src scheme -(detect-on-solid entity tilemap #!optional other-entities) +(detect-on-solid entity scene dt) #+end_src -Sets ~#:on-ground?~ to true if the entity is supported by a solid tile (probe below the feet) and/or, when ~other-entities~ is a list, by another solid's top surface (e.g. moving platforms). Omit the third argument for tile-only detection. Only applies if ~#:gravity?~ is true. Returns a new entity (the ~?~-suffix denotes call-site readability, not a boolean return value). Prefer calling after all collision resolution when using the entity list. - -** ~apply-jump~ - -#+begin_src scheme -(apply-jump entity jump-pressed?) -#+end_src - -If the jump button is pressed and the entity is on ground, sets ~#:ay~ to ~(- #:jump-force)~ (default 15 pixels/frame). On the next frame, ~apply-acceleration~ consumes this into ~#:vy~. Returns a new entity. +Sets ~#:on-ground?~ to true if the entity is supported by a solid tile (probe below the feet) and/or by another solid's top surface in ~(scene-entities scene)~ (e.g. moving platforms). Only applies if ~#:gravity?~ is true. Returns a new entity (the ~?~-suffix denotes call-site readability, not a boolean return value). Runs after tile and entity collision resolution in ~default-engine-update~. ** ~resolve-entity-collisions~ @@ -574,7 +587,7 @@ There is no scene-level wrapper; apply ~resolve-entity-collisions~ to the entity ** Physics Constants - ~*gravity*~ = 1 (pixels per frame per frame) -- ~*jump-force*~ = 15 (vertical acceleration on jump) +- ~*jump-force*~ = 15 (conventional magnitude for a one-frame jump impulse; set ~#:ay~ to ~(- *jump-force*)~ in ~update:~ when jumping — there is no ~apply-jump~ helper) * Input (~downstroke-input~) @@ -681,13 +694,17 @@ Example: #+begin_src scheme (define (update-player game dt) - (let ((input (game-input game)) - (player (car (scene-entities (game-scene game))))) - (if (input-pressed? input 'a) - (apply-jump player #t) + (let* ((input (game-input game)) + (player (car (scene-entities (game-scene game)))) + (jump? (and (input-pressed? input 'a) + (entity-ref player #:on-ground? #f)))) + (if jump? + (entity-set player #:ay (- *jump-force*)) player))) #+end_src +~#:on-ground?~ is set by ~detect-on-solid~ during ~engine-update~, before ~update:~ runs. ~*jump-force*~ is exported from ~downstroke-physics~ (default 15). + * Renderer (~downstroke-renderer~) #+begin_src scheme @@ -1063,7 +1080,7 @@ Releases all audio resources. Call at shutdown or in a cleanup hook. (import downstroke-tween) #+end_src -Time-based interpolation of numeric entity properties. Library-only — call from ~update:~; see ~docs/tweens.org~ for patterns with ~#:skip-pipelines~. +Time-based interpolation of numeric entity properties. ~step-tweens~ runs inside ~default-engine-update~ before acceleration/gravity; you can still call ~tween-step~ from ~update:~ for manual tweening — see ~docs/tweens.org~ for patterns with ~#:skip-pipelines~. ** ~make-tween~ @@ -1095,10 +1112,10 @@ Returns two values: updated tween struct and updated entity. ~dt~ is elapsed mil ** ~step-tweens~ #+begin_src scheme -(step-tweens entity dt) +(step-tweens entity scene dt) #+end_src -Pipeline step: auto-advances ~#:tween~ on an entity. No-op if ~#:tween~ is absent. Removes ~#:tween~ when the tween finishes. Skipped when ~'tweens~ is in ~#:skip-pipelines~. See ~docs/tweens.org~ for patterns. +Pipeline step: auto-advances ~#:tween~ on an entity. No-op if ~#:tween~ is absent. Removes ~#:tween~ when the tween finishes. Skipped when ~'tweens~ is in ~#:skip-pipelines~. ~scene~ is accepted for uniformity with other pipeline steps. See ~docs/tweens.org~ for patterns. ** Easing exports |
