aboutsummaryrefslogtreecommitdiff
path: root/docs/guide.org
diff options
context:
space:
mode:
authorGene Pasquet <dev@etenil.net>2026-04-05 23:12:54 +0100
committerGene Pasquet <dev@etenil.net>2026-04-05 23:12:54 +0100
commitb99ada53b715def5492c7d04c0d327fa7048e5d3 (patch)
tree9e94dbc8ff863ef09ef18f4be31fb45e085572a4 /docs/guide.org
parent027053b11a3a5d861ed2fa2db245388bd95ac246 (diff)
Complete implementation
Diffstat (limited to 'docs/guide.org')
-rw-r--r--docs/guide.org261
1 files changed, 261 insertions, 0 deletions
diff --git a/docs/guide.org b/docs/guide.org
new file mode 100644
index 0000000..5119cea
--- /dev/null
+++ b/docs/guide.org
@@ -0,0 +1,261 @@
+#+TITLE: Downstroke — Getting Started
+#+AUTHOR: Downstroke Project
+
+* Introduction
+
+Downstroke is a 2D tile-driven game engine for Chicken Scheme, built on SDL2. It is inspired by Phaser 2: a minimal game is about 20 lines of Scheme.
+
+The engine handles SDL2 initialization, the event loop, input, rendering, and physics. You provide lifecycle hooks to customize behavior. This guide walks you through building your first game with Downstroke.
+
+* Installation
+
+** System Dependencies
+
+Downstroke requires the following system libraries:
+
+- =SDL2=
+- =SDL2_mixer=
+- =SDL2_ttf=
+- =SDL2_image=
+- =expat=
+
+On Debian/Ubuntu:
+#+begin_src bash
+sudo apt-get install libsdl2-dev libsdl2-mixer-dev libsdl2-ttf-dev libsdl2-image-dev libexpat1-dev
+#+end_src
+
+On macOS with Homebrew:
+#+begin_src bash
+brew install sdl2 sdl2_mixer sdl2_ttf sdl2_image expat
+#+end_src
+
+** Chicken Eggs
+
+Install the Downstroke egg along with its dependencies:
+
+#+begin_src bash
+chicken-install downstroke sdl2 sdl2-image defstruct matchable states
+#+end_src
+
+* Hello World — Your First Game
+
+Create a file called =mygame.scm=:
+
+#+begin_src scheme
+(import downstroke/engine)
+
+(define *game*
+ (make-game title: "Hello World" width: 640 height: 480))
+
+(game-run! *game*)
+#+end_src
+
+Build and run:
+#+begin_src bash
+csc mygame.scm -o mygame
+./mygame
+#+end_src
+
+You should see a black window titled "Hello World". Press =Escape= or close the window to quit. The =game-run!= function handles SDL2 initialization, window creation, the event loop, and cleanup automatically. The engine also provides a default =quit= action (Escape key and window close button).
+
+* Moving Square — First Entity
+
+Now let's add an entity you can move with the keyboard. Create =square.scm=:
+
+#+begin_src scheme
+(import scheme
+ (chicken base)
+ (prefix sdl2 "sdl2:")
+ downstroke/engine
+ downstroke/world
+ downstroke/entity
+ downstroke/input)
+
+(define *game*
+ (make-game
+ title: "Moving Square" width: 640 height: 480
+
+ create: (lambda (game)
+ (game-scene-set! game
+ (make-scene
+ entities: (list (list #:type 'box #:x 300 #:y 200
+ #:width 32 #:height 32
+ #:vx 0 #:vy 0))
+ tilemap: #f
+ camera: (make-camera x: 0 y: 0)
+ tileset-texture: #f)))
+
+ update: (lambda (game dt)
+ (let* ((input (game-input game))
+ (scene (game-scene game))
+ (box (car (scene-entities scene)))
+ ;; Read input and update velocity
+ (box (entity-set box #:vx (cond ((input-held? input 'left) -3)
+ ((input-held? input 'right) 3)
+ (else 0))))
+ (box (entity-set box #:vy (cond ((input-held? input 'up) -3)
+ ((input-held? input 'down) 3)
+ (else 0))))
+ ;; Apply velocity to position
+ (box (entity-set box #:x (+ (entity-ref box #:x 0)
+ (entity-ref box #:vx 0))))
+ (box (entity-set box #:y (+ (entity-ref box #:y 0)
+ (entity-ref box #:vy 0)))))
+ (scene-entities-set! scene (list box))))
+
+ render: (lambda (game)
+ (let* ((scene (game-scene game))
+ (box (car (scene-entities scene)))
+ (x (inexact->exact (floor (entity-ref box #:x 0))))
+ (y (inexact->exact (floor (entity-ref box #:y 0))))
+ (w (entity-ref box #:width 32))
+ (h (entity-ref box #:height 32)))
+ (sdl2:set-render-draw-color! (game-renderer game) 255 200 0 255)
+ (sdl2:render-fill-rect! (game-renderer game)
+ (sdl2:make-rect x y w h))))))
+
+(game-run! *game*)
+#+end_src
+
+Run it:
+#+begin_src bash
+csc square.scm -o square
+./square
+#+end_src
+
+Press arrow keys to move the yellow square around. Here are the key ideas:
+
+- **Scenes**: =make-scene= creates a container for entities, tilemaps, and the camera. It holds the game state each frame.
+- **Entities**: Entities are plists (property lists). They have no class; they're pure data. Access properties with =entity-ref=, and update with =entity-set= (which returns a *new* plist — always bind the result).
+- **Input**: =input-held?= returns =#t= if an action is currently pressed. Actions are symbols like ='left=, ='right=, ='up=, ='down= (from the default input config).
+- **Update & Render**: The =update:= hook runs first and updates entities. The =render:= hook runs after the default rendering pipeline and is used for custom drawing like this colored rectangle.
+- **Rendering**: Since our tilemap is =#f=, the default renderer draws nothing; all drawing happens in the =render:= hook using SDL2 functions.
+
+* Adding a Tilemap and Physics
+
+For a real game, you probably want tilemaps, gravity, and collision detection. Downstroke includes a full physics pipeline. Here's the pattern:
+
+#+begin_src scheme
+(import scheme
+ (chicken base)
+ downstroke/engine
+ downstroke/world
+ downstroke/entity
+ downstroke/input
+ downstroke/physics
+ downstroke/scene-loader
+ downstroke/sound)
+
+(define *game*
+ (make-game
+ title: "Platformer Demo" width: 600 height: 400
+
+ preload: (lambda (game)
+ ;; Initialize audio and load sounds (optional)
+ (init-audio!)
+ (load-sounds! '((jump . "assets/jump.wav"))))
+
+ create: (lambda (game)
+ ;; Load the tilemap from a TMX file (made with Tiled editor)
+ (let* ((scene (game-load-scene! game "assets/level.tmx"))
+ ;; Create a player entity
+ (player (list #:type 'player
+ #:x 100 #:y 50
+ #:width 16 #:height 16
+ #:vx 0 #:vy 0
+ #:gravity? #t
+ #:on-ground? #f
+ #:tile-id 1)))
+ ;; Add player to the scene
+ (scene-add-entity scene player)))
+
+ update: (lambda (game dt)
+ ;; Typical pattern for platformer physics
+ (let* ((input (game-input game))
+ (scene (game-scene game))
+ (tm (scene-tilemap scene))
+ (player (car (scene-entities scene)))
+ ;; Set horizontal velocity from input
+ (player (entity-set player #:vx
+ (cond
+ ((input-held? input 'left) -3)
+ ((input-held? input 'right) 3)
+ (else 0))))
+ ;; Jump on button press if on ground
+ (_ (when (and (input-pressed? input 'a)
+ (entity-ref player #:on-ground? #f))
+ (play-sound 'jump)))
+ (player (apply-jump player (input-pressed? input 'a)))
+ ;; Run physics pipeline
+ (player (apply-acceleration player))
+ (player (apply-gravity player))
+ (player (apply-velocity-x player))
+ (player (resolve-tile-collisions-x player tm))
+ (player (apply-velocity-y player))
+ (player (resolve-tile-collisions-y player tm))
+ (player (detect-ground player tm)))
+ ;; Update camera to follow player
+ (let ((cam-x (max 0 (- (entity-ref player #:x 0) 300))))
+ (camera-x-set! (scene-camera scene) cam-x))
+ ;; Replace entities in scene
+ (scene-entities-set! scene (list player))))))
+
+(game-run! *game*)
+#+end_src
+
+Key points:
+
+- **=game-load-scene!=** loads a TMX tilemap file (created with the Tiled editor), creates the tileset texture, and builds the scene. It returns the scene so you can add more entities.
+- **=init-audio!=** and **=load-sounds!=** initialize the audio subsystem and load sound files. Call these in the =preload:= hook.
+- **=play-sound=** plays a loaded sound effect.
+- **Physics pipeline**: Functions like =apply-gravity=, =apply-velocity-x=, =resolve-tile-collisions-x= form the physics pipeline. Apply them in order each frame to get correct platformer behavior.
+- **Tile collisions**: =resolve-tile-collisions-x= and =resolve-tile-collisions-y= snap entities to tile edges, preventing clipping.
+- **Ground detection**: =detect-ground= sets the =#:on-ground?= flag so you know if a jump is valid.
+
+See =demo/platformer.scm= in the engine source for a complete working example.
+
+* Demo Overview
+
+Downstroke includes five complete demo games that showcase different features:
+
+| Demo | File | What it shows |
+|------|------|--------------|
+| Platformer | =demo/platformer.scm= | Gravity, jump, tile collision, camera follow, sound effects |
+| Top-down | =demo/topdown.scm= | 8-directional movement, no gravity, tilemap, camera follow |
+| 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 |
+
+Each demo is self-contained and serves as a working reference for a particular game mechanic.
+
+* Build & Run
+
+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
+./bin/demo-platformer
+./bin/demo-topdown
+# etc.
+#+end_src
+
+* Next Steps
+
+You now know how to:
+
+- Create a game with =make-game= and =game-run!=
+- Add entities to scenes
+- Read input with =input-held?= and =input-pressed?=
+- Apply physics to entities
+- Load tilemaps with =game-load-scene!=
+- Play sounds with =load-sounds!= and =play-sound=
+
+For more details:
+
+- **Full API reference**: See =docs/api.org= for all functions and keyword arguments.
+- **Entity model**: See =docs/entities.org= to learn about plist keys, tags, prefabs, and mixins.
+- **Physics pipeline**: See =docs/physics.org= for the full physics specification and collision model.
+
+Happy coding!