diff options
| author | Gene Pasquet <dev@etenil.net> | 2026-04-06 03:41:09 +0100 |
|---|---|---|
| committer | Gene Pasquet <dev@etenil.net> | 2026-04-06 03:41:09 +0100 |
| commit | 78a924defabc862a7cfa5476091152c1ef5333ee (patch) | |
| tree | 5e7e13ca27848dfe87ecf3eb82689d8e9488beb3 /docs | |
| parent | c4ebbbdd1a0bd081a2ed9447ba8188d97ae54717 (diff) | |
Fixes, updated license
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/api.org | 413 | ||||
| -rw-r--r-- | docs/guide.org | 30 |
2 files changed, 417 insertions, 26 deletions
diff --git a/docs/api.org b/docs/api.org index 3925bc2..f18b80a 100644 --- a/docs/api.org +++ b/docs/api.org @@ -22,7 +22,8 @@ The engine module provides the top-level game lifecycle and state management. (preload #f) (create #f) (update #f) - (render #f)) + (render #f) + (debug? #f)) #+end_src Creates and initializes a game object. All parameters are optional keywords. @@ -38,6 +39,7 @@ Creates and initializes a game object. All parameters are optional keywords. | ~create~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called once at startup | | ~update~ | procedure/false | #f | Hook: ~(lambda (game dt) ...)~ called each frame | | ~render~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called after render-scene! | +| ~debug?~ | boolean | #f | Enable debug overlay drawing (collision boxes) | The game object is the central hub. Use it to store/retrieve assets, manage scenes, and access the current input state. @@ -152,7 +154,8 @@ Auto-generated by defstruct. Use keyword arguments: (entities '()) (tilemap #f) (camera #f) - (tileset-texture #f)) + (tileset-texture #f) + (camera-target #f)) #+end_src Creates a scene record representing the current level state. @@ -163,6 +166,7 @@ Creates a scene record representing the current level state. | ~tilemap~ | tilemap/false | #f | Tile grid and collisions | | ~camera~ | camera/false | #f | Viewport position | | ~tileset-texture~ | SDL2 texture/false | #f | Rendered tileset image | +| ~camera-target~ | symbol/false | #f | Tag symbol of the entity to follow (see ~scene-camera-target-set!~) | ** ~make-camera~ @@ -267,6 +271,15 @@ Returns a list of all entities whose ~#:tags~ list contains the given tag. Retur - ~scene-tilemap~, ~scene-tilemap-set!~ - ~scene-camera~, ~scene-camera-set!~ - ~scene-tileset-texture~, ~scene-tileset-texture-set!~ +- ~scene-camera-target~, ~scene-camera-target-set!~ + +** ~tilemap-tile-at~ + +#+begin_src scheme +(tilemap-tile-at tilemap col row) +#+end_src + +Returns the tile ID at grid position ~(col, row)~ across all layers. Returns ~0~ if the position is out of bounds or all layers have a 0 tile at that cell. Used internally by the physics module; also useful for manual tile queries. * Entity (~downstroke-entity~) @@ -360,7 +373,7 @@ All entities can have these keys. Not all are required: (import downstroke-physics) #+end_src -The physics module implements the main collision and movement pipeline. The physics pipeline runs automatically before the user's ~update:~ hook. +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). ** Physics Pipeline Order @@ -525,6 +538,51 @@ Returns true if the action was released in this frame (held previously but not n Initializes an input state record from a configuration. All actions start as unpressed. +** ~input-any-pressed?~ + +#+begin_src scheme +(input-any-pressed? state config) +#+end_src + +Returns true if any action in the configuration was pressed this frame. Useful for "press any key to continue" prompts. + +** ~input-state->string~ + +#+begin_src scheme +(input-state->string state config) +#+end_src + +Returns a human-readable string of all currently held actions, e.g., ~"[Input: left a]"~. Useful for debug displays. + +** ~set-facing-from-vx~ + +#+begin_src scheme +(set-facing-from-vx entity vx) +#+end_src + +Sets ~#:facing~ to ~1~ if ~vx > 0~, ~-1~ if ~vx < 0~, and returns the entity unchanged if ~vx = 0~. Convenience helper for keeping the facing direction in sync with horizontal velocity. + +** ~apply-input-to-entity~ + +#+begin_src scheme +(apply-input-to-entity entity held?) +#+end_src + +Applies an input map stored on the entity to compute a new ~#:vx~. The entity must have an ~#:input-map~ key: an alist of ~(action . (dvx . dvy))~ pairs. ~held?~ is a predicate ~(lambda (action) ...)~ (typically ~(lambda (a) (input-held? input a))~). Also updates ~#:facing~ from the resulting velocity. + +Example: + +#+begin_src scheme +;; Entity with embedded input map: +(list #:type 'player + #:input-map '((left . (-3 . 0)) (right . (3 . 0))) + #:move-speed 1 + ...) + +;; In update: +(apply-input-to-entity player (lambda (a) (input-held? input a))) +#+end_src + ** Example: Player Movement #+begin_src scheme @@ -584,6 +642,203 @@ Returns an SDL2 flip list based on the entity's ~#:facing~ field. Returns ~'(hor Renders a single line of text to the screen at the given pixel coordinates. ~color~ is an SDL2 color struct. Positions are in screen (viewport) space, not world space. Does not cache; call once per frame for each text element. +** Debug Drawing + +Debug drawing displays collision boxes and tile grid overlays to visualize physics during development. Enable with the ~debug?:~ keyword on ~make-game~. + +*** ~render-debug-scene!~ + +#+begin_src scheme +(render-debug-scene! renderer scene) +#+end_src + +Renders debug overlays for the entire scene: tile grid boundaries and entity collision boxes. Call this in your render hook to see what the physics engine sees. + +*** ~draw-debug-tiles~ + +#+begin_src scheme +(draw-debug-tiles renderer camera tilemap) +#+end_src + +Draws a grid outline around all non-zero tiles in the tilemap. Useful for understanding tilemap layout and collision geometry. Color: purple (~+debug-tile-color+~). + +*** ~draw-debug-entities~ + +#+begin_src scheme +(draw-debug-entities renderer camera scene) +#+end_src + +Draws colored bounding boxes around all entities in the scene. Also draws attack hitboxes when the entity's ~#:attack-timer~ is active. Entity colors are: + +| Type | Color | Value | +|------|-------|-------| +| Player | Blue (~+debug-player-color+~) | rgb(64, 128, 255) | +| Enemy | Red (~+debug-enemy-color+~) | rgb(220, 40, 40) | +| Attack Hitbox | Green (~+debug-attack-color+~) | rgb(0, 200, 80) | +| Tile | Purple (~+debug-tile-color+~) | rgb(140, 0, 220) | + +*** Example: Enable Debug Mode + +#+begin_src scheme +(define my-game + (make-game + title: "My Game" width: 600 height: 400 + debug?: #t)) ;; Enable debug overlay + +(game-run! my-game) +#+end_src + +With debug mode enabled, the game renders the normal scene first, then overlays collision boxes and tile boundaries on top. This is useful for finding collision bugs and understanding the physics layout. + +** Menus + +The ~draw-menu-items~ helper renders a list of menu options with a cursor indicator. + +*** ~draw-menu-items~ + +#+begin_src scheme +(draw-menu-items renderer font items cursor x y-start y-step + #!key (label-fn identity) (color #f) (prefix "> ") (no-prefix " ")) +#+end_src + +Renders a vertical menu with a cursor indicator on the currently selected item. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| ~renderer~ | renderer | (required) | SDL2 renderer | +| ~font~ | TTF font | (required) | Font for rendering text | +| ~items~ | list | (required) | List of menu items (any values) | +| ~cursor~ | integer | (required) | Index of highlighted item (0-based) | +| ~x~ | integer | (required) | X position in screen space | +| ~y-start~ | integer | (required) | Y position of first item | +| ~y-step~ | integer | (required) | Pixel spacing between items | +| ~label-fn~ | procedure | ~identity~ | Function to convert item to string (called for each item) | +| ~color~ | SDL2 color | white (255, 255, 255) | Text color | +| ~prefix~ | string | "~> ~" | Prefix for highlighted item | +| ~no-prefix~ | string | "~ ~" | Prefix for non-highlighted items | + +*** Example: Menu State + +#+begin_src scheme +(import downstroke-engine + downstroke-world + (prefix sdl2 "sdl2:") + (prefix sdl2-ttf "ttf:")) + +(define menu-items '(("Start Game") ("Options") ("Quit"))) +(define cursor (make-parameter 0)) + +(define my-game + (make-game + title: "Menu Game" width: 600 height: 400 + + preload: (lambda (game) + (let ((font (ttf:open-font "assets/font.ttf" 24))) + (game-asset-set! game 'menu-font font))) + + create: (lambda (game) + (game-add-state! game 'menu + (make-game-state + render: (lambda (game) + (let ((font (game-asset game 'menu-font))) + (draw-menu-items (game-renderer game) font menu-items + (cursor) + 100 100 40)))))))) + +(game-run! my-game) +#+end_src + +** Sprite Fonts + +Sprite fonts render text using a bitmap tileset instead of system fonts. Characters are stored as tiles in your tileset image, making them pixel-perfect and zero-dependency on TTF files. + +*** ~make-sprite-font*~ + +#+begin_src scheme +(make-sprite-font* #!key tile-size spacing ranges) +#+end_src + +Creates a sprite font from character ranges. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| ~tile-size~ | integer | (required) | Pixel width/height of each character tile | +| ~spacing~ | integer | 1 | Pixels between characters when rendered | +| ~ranges~ | list | (required) | List of ~(start-char end-char first-tile-id)~ triples | + +The ~ranges~ parameter defines which tile IDs correspond to which characters. For example, ~(list (#\A #\M 917) (#\N #\Z 966) (#\0 #\9 868))~ maps characters A–M to tile IDs 917–929, N–Z to 966–978, and 0–9 to 868–877. + +Characters are automatically uppercased when rendered (all lookups use uppercase). + +*** ~sprite-font-char->tile-id~ + +#+begin_src scheme +(sprite-font-char->tile-id font ch) +#+end_src + +Returns the tile ID for a character, or ~#f~ if not found. The character is automatically uppercased before lookup. + +*** ~sprite-text-width~ + +#+begin_src scheme +(sprite-text-width font text) +#+end_src + +Computes the pixel width of a string when rendered with the given font. Useful for centering text. Formula: ~(* n tile-size) + (* (- n 1) spacing)~ where ~n~ is the string length. + +*** ~draw-sprite-text~ + +#+begin_src scheme +(draw-sprite-text renderer tileset-texture tileset font text x y) +#+end_src + +Renders sprite-font text to the screen. ~tileset-texture~ is the SDL2 texture of your tileset image, and ~tileset~ is the tilemap structure containing tile layout information. Characters not in the font are silently skipped. + +| Parameter | Type | Description | +|-----------|------|-------------| +| ~renderer~ | renderer | SDL2 renderer | +| ~tileset-texture~ | SDL2 texture | Rendered tileset image | +| ~tileset~ | tileset struct | Tileset from loaded tilemap | +| ~font~ | sprite-font struct | Font created with ~make-sprite-font*~ | +| ~text~ | string | Text to render | +| ~x, y~ | integers | Screen position in pixels | + +*** Example: Sprite Font for Score Display + +#+begin_src scheme +(import downstroke-engine + downstroke-scene-loader + downstroke-renderer) + +;; Create a sprite font with A-Z at tiles 917–942, 0-9 at tiles 868–877 +(define score-font + (make-sprite-font* + tile-size: 16 + spacing: 1 + ranges: (list + (list #\A #\Z 917) + (list #\0 #\9 868)))) + +(define my-game + (make-game + title: "Score Game" width: 600 height: 400 + + create: (lambda (game) + (game-load-scene! game "assets/level.tmx")) + + render: (lambda (game) + (let ((scene (game-scene game))) + ;; Display "SCORE: 1500" using sprite font + (draw-sprite-text (game-renderer game) + (scene-tileset-texture scene) + (tilemap-tileset (scene-tilemap scene)) + score-font + "SCORE: 1500" + 10 10))))) + +(game-run! my-game) +#+end_src + * Assets (~downstroke-assets~) #+begin_src scheme @@ -716,6 +971,22 @@ Releases all audio resources. Call at shutdown or in a cleanup hook. The animation module provides simple frame-based sprite animation. +** ~animation-frames~ + +#+begin_src scheme +(animation-frames anim) +#+end_src + +Returns the ~#:frames~ list from an animation plist. Each element is a 0-indexed tile ID. + +** ~animation-duration~ + +#+begin_src scheme +(animation-duration anim) +#+end_src + +Returns the ~#:duration~ value from an animation plist (ticks per frame). + ** ~set-animation~ #+begin_src scheme @@ -753,6 +1024,76 @@ Example: Converts a frame index to a tile ID (1-indexed). Used internally by ~animate-entity~. +* Tilemap (~downstroke-tilemap~) + +#+begin_src scheme +(import downstroke-tilemap) +#+end_src + +The tilemap module parses Tiled TMX and TSX files using the expat XML library, and provides struct accessors for tile and map data. + +** ~load-tilemap~ + +#+begin_src scheme +(load-tilemap filename) +#+end_src + +Loads and parses a TMX file. Automatically resolves and loads the referenced TSX tileset and its image. Returns a ~tilemap~ struct. + +** ~load-tileset~ + +#+begin_src scheme +(load-tileset filename) +#+end_src + +Loads and parses a TSX tileset file. Loads the image referenced in the tileset. Returns a ~tileset~ struct. + +** ~tileset-tile~ + +#+begin_src scheme +(tileset-tile tileset tile-id) +#+end_src + +Returns a ~tile~ struct for the given 1-indexed tile ID. The tile struct has ~tile-id~ and ~tile-rect~ (an SDL2 rect) fields with the source rectangle in the tileset image. + +** ~tileset-rows~ + +#+begin_src scheme +(tileset-rows tileset) +#+end_src + +Returns the number of rows in the tileset image (computed from ~tilecount / columns~). + +** Tileset Accessors (auto-generated by defstruct) + +- ~tileset-tilewidth~, ~tileset-tileheight~ — tile dimensions in pixels +- ~tileset-spacing~ — pixel gap between tiles in the source image +- ~tileset-tilecount~ — total number of tiles +- ~tileset-columns~ — number of tile columns +- ~tileset-image-source~ — path to the tileset image file +- ~tileset-image~ — loaded SDL2 surface + +** Tilemap Accessors (auto-generated by defstruct) + +- ~tilemap-width~, ~tilemap-height~ — map dimensions in tiles +- ~tilemap-tilewidth~, ~tilemap-tileheight~ — tile dimensions in pixels +- ~tilemap-tileset~ — the embedded ~tileset~ struct +- ~tilemap-layers~ — list of ~layer~ structs +- ~tilemap-objects~ — list of ~object~ structs (from the Tiled object layer) + +** Layer Accessors (auto-generated by defstruct) + +- ~layer-name~ — layer name string +- ~layer-width~, ~layer-height~ — layer dimensions in tiles +- ~layer-map~ — 2D list of tile GIDs (rows × columns) + +** Object Accessors (auto-generated by defstruct) + +- ~object-name~, ~object-type~ — object name and type (strings from XML) +- ~object-x~, ~object-y~ — position in pixels +- ~object-width~, ~object-height~ — size in pixels +- ~object-properties~ — alist of custom properties from Tiled + * Scene Loader (~downstroke-scene-loader~) #+begin_src scheme @@ -785,6 +1126,30 @@ Example: ) #+end_src +** ~game-load-tilemap!~ + +#+begin_src scheme +(game-load-tilemap! game key filename) +#+end_src + +Loads a TMX tilemap file, stores it in the game asset registry under ~key~, and returns the tilemap struct. + +** ~game-load-tileset!~ + +#+begin_src scheme +(game-load-tileset! game key filename) +#+end_src + +Loads a TSX tileset file, stores it in the game asset registry under ~key~, and returns the tileset struct. + +** ~game-load-font!~ + +#+begin_src scheme +(game-load-font! game key filename size) +#+end_src + +Opens a TTF font at ~size~ points, stores it in the game asset registry under ~key~, and returns the font. Wraps ~ttf:open-font~. + ** ~create-tileset-texture~ #+begin_src scheme @@ -793,54 +1158,54 @@ Example: Creates an SDL2 texture from the tileset image embedded in a tilemap struct. Useful when you need the texture independently of ~game-load-scene!~. -** ~make-prefab-registry~ +** ~load-prefabs~ #+begin_src scheme -(make-prefab-registry name1 constructor1 name2 constructor2 ...) +(load-prefabs filename engine-mixin-table user-hooks) #+end_src -Creates a hash-table mapping symbol names to entity constructor functions. Constructors have the signature ~(lambda (x y w h) entity)~. +Loads a prefab definition file and returns a ~prefab-registry~ struct. The file must contain a Scheme expression with ~mixins~ and ~prefabs~ sections (see ~docs/entities.org~). + +| Parameter | Type | Description | +|-----------|------|-------------| +| ~filename~ | string | Path to the prefab data file (e.g., ~"assets/prefabs.scm"~) | +| ~engine-mixin-table~ | alist | Extra engine-level mixins to merge; pass ~'()~ if none | +| ~user-hooks~ | alist | Extra instantiation hooks; pass ~'()~ if none | Example: #+begin_src scheme -(define (make-player x y w h) - (list #:type 'player #:x x #:y y #:width w #:height h - #:vx 0 #:vy 0 #:gravity? #t #:tile-id 29)) +(define *prefabs* (load-prefabs "assets/prefabs.scm" '() '())) +#+end_src -(define (make-enemy x y w h) - (list #:type 'enemy #:x x #:y y #:width w #:height h - #:vx -2 #:vy 0 #:gravity? #t #:tile-id 5)) +** ~reload-prefabs!~ -(define prefabs - (make-prefab-registry - 'player make-player - 'enemy make-enemy)) +#+begin_src scheme +(reload-prefabs! registry) #+end_src +Reloads the prefab file that the registry was originally loaded from. Returns a new registry. Useful for hot-reloading prefab data during development without restarting the game. + ** ~instantiate-prefab~ #+begin_src scheme (instantiate-prefab registry type x y w h) #+end_src -Looks up a constructor by type in the registry and calls it with the given position and size. Returns the entity plist, or ~#f~ if the type is not registered. +Looks up a prefab by type symbol in the registry and returns a fresh entity plist at the given position and size. Returns ~#f~ if the type is not registered. If the resulting entity has an ~#:on-instantiate~ hook, it is called with the entity before returning. ** ~tilemap-objects->entities~ #+begin_src scheme -(tilemap-objects->entities tilemap instantiate-fn) +(tilemap-objects->entities tilemap registry) #+end_src -Converts the TMX object list (from Tiled) into entity plists. Each object's type (string from XML) is converted to a symbol and passed to ~instantiate-fn~. Filters out ~#f~ results (unregistered types). - -~instantiate-fn~ should have the signature ~(lambda (type x y w h) entity-or-#f)~, typically the curried version of ~instantiate-prefab~: +Converts the TMX object list (from Tiled) into entity plists using the prefab registry. Each object's type string is converted to a symbol and passed to ~instantiate-prefab~. Objects whose type has no registered prefab are silently filtered out. Example: #+begin_src scheme -(let* ((prefabs (make-prefab-registry 'player make-player 'enemy make-enemy)) - (inst (lambda (t x y w h) (instantiate-prefab prefabs t x y w h))) - (entities (tilemap-objects->entities tilemap inst))) +(let* ((registry (load-prefabs "assets/prefabs.scm" '() '())) + (entities (tilemap-objects->entities tilemap registry))) (scene-entities-set! scene entities)) #+end_src diff --git a/docs/guide.org b/docs/guide.org index af6319e..ca9cfbf 100644 --- a/docs/guide.org +++ b/docs/guide.org @@ -214,9 +214,33 @@ Key points: See =demo/platformer.scm= in the engine source for a complete working example. +* Development Tips + +** Debug Mode + +During development, enable ~debug?: #t~ on ~make-game~ to visualize collision boxes and the tile grid. This helps you understand what the physics engine "sees" and debug collision problems: + +#+begin_src scheme +(define my-game + (make-game + title: "My Game" width: 600 height: 400 + debug?: #t)) ;; Enable collision visualization + +(game-run! my-game) +#+end_src + +With debug mode enabled, you'll see: + +- **Purple outlines** around all non-zero tiles +- **Blue boxes** around player entities +- **Red boxes** around enemy entities +- **Green boxes** around active attack hitboxes + +This is invaluable for tuning collision geometry and understanding why entities are clipping or not colliding as expected. + * Demo Overview -Downstroke includes five complete demo games that showcase different features: +Downstroke includes seven complete demo games that showcase different features: | Demo | File | What it shows | |------|------|--------------| @@ -225,6 +249,8 @@ Downstroke includes five complete demo games that showcase different features: | Physics Sandbox | =demo/sandbox.scm= | Entity-entity collision, multi-entity physics, auto-respawn | | Shoot-em-up | =demo/shmup.scm= | No tilemap, entity spawning/removal, manual AABB collision | | Audio | =demo/audio.scm= | Sound effects, music toggle, text rendering | +| Sprite Font | =demo/spritefont.scm= | Bitmap text rendering using non-contiguous tileset ranges | +| Menu | =demo/menu.scm= | State machine menus, keyboard navigation, TTF text rendering | Each demo is self-contained and serves as a working reference for a particular game mechanic. @@ -235,7 +261,7 @@ If you cloned the downstroke source, you can build everything: #+begin_src bash cd /path/to/downstroke make # Compile all engine modules -make demos # Build all 5 demo executables +make demos # Build all demo executables ./bin/demo-platformer ./bin/demo-topdown # etc. |
