aboutsummaryrefslogtreecommitdiff
path: root/docs/guide.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/guide.org')
-rw-r--r--docs/guide.org50
1 files changed, 18 insertions, 32 deletions
diff --git a/docs/guide.org b/docs/guide.org
index 96663ea..d98b00e 100644
--- a/docs/guide.org
+++ b/docs/guide.org
@@ -5,7 +5,7 @@
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.
+The engine handles SDL2 initialization, the event loop, input, rendering, and — by default — a full physics pipeline each frame (~default-engine-update~). You provide lifecycle hooks to customize behavior; ~update:~ is where you usually set movement *intent* (~#:vx~, ~#:vy~, ~#:ay~) and game rules. This guide walks you through building your first game with Downstroke.
* Installation
@@ -89,18 +89,13 @@ Now let's add an entity you can move with the keyboard. Create =square.scm=:
(let* ((input (game-input game))
(scene (game-scene game))
(box (car (scene-entities scene)))
- ;; Read input and update velocity
+ ;; Intent only: default engine-update integrates #:vx / #:vy each frame
(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)))))
+ (else 0)))))
(game-scene-set! game
(update-scene scene entities: (list box)))))
@@ -129,7 +124,7 @@ 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. Optional =background:= ~(r g b)~ or ~(r g b a)~ sets the color used to clear the window each frame (default is black).
- **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.
+- **Update & Render**: Each frame, after input, the built-in =engine-update= integrates =#:vx= / =#:vy= into position (gravity and tile steps no-op without =#:gravity?= or a tilemap). Your =update:= hook runs *after* that and sets velocities for the following frame. The =render:= hook runs after the default scene render 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
@@ -171,32 +166,23 @@ For a real game, you probably want tilemaps, gravity, and collision detection. D
(scene-add-entity scene player)))
update: (lambda (game dt)
- ;; Typical pattern for platformer physics
+ ;; Game logic only — gravity, collisions, and #:on-ground? run in default-engine-update
(let* ((input (game-input game))
(scene (game-scene game))
- (tm (scene-tilemap scene))
(player (car (scene-entities scene)))
- ;; Set horizontal velocity from input
+ (jump? (and (input-pressed? input 'a)
+ (entity-ref player #:on-ground? #f)))
(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-on-solid player tm)))
- (game-scene-set! game
- (update-scene scene entities: (list player)))))))
+ (else 0)))))
+ (when jump? (play-sound 'jump))
+ (let ((player (if jump?
+ (entity-set player #:ay (- *jump-force*))
+ player)))
+ (game-scene-set! game
+ (update-scene scene entities: (list player)))))))
(game-run! *game*)
#+end_src
@@ -206,9 +192,9 @@ 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.
-- **On-solid check**: =detect-on-solid= sets the =#:on-ground?= flag from tiles below the feet and, if you pass other scene entities, from standing on solids (moving platforms, crates). Call it after collisions; use the flag next frame to gate jumps. (Despite the =?=-suffix, it returns an updated entity, not a boolean.)
+- **Automatic physics**: By default, =make-game= uses =default-engine-update=, which runs the full pipeline (tweens, acceleration, gravity, velocity, tile and entity collisions, ground detection, group sync) *before* your =update:= hook. Your =update:= sets =#:vx= and a one-frame =#:ay= for jumps; =*jump-force*= (from =downstroke-physics=, default 15) is a conventional jump impulse.
+- **=engine-update: #f=** disables that pipeline — use this for games that move entities entirely in =update:= (see =demo/shmup.scm=, =demo/scaling.scm=) or supply your own =engine-update:= procedure to replace =default-engine-update=.
+- **=detect-on-solid=** (inside the default pipeline) sets =#:on-ground?= from tiles below the feet and from other solids in the scene entity list. When your =update:= runs, that flag already reflects the current frame.
See =demo/platformer.scm= in the engine source for a complete working example.
@@ -300,7 +286,7 @@ 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
+- Rely on the default physics pipeline (or turn it off with =engine-update: #f=)
- Load tilemaps with =game-load-scene!=
- Play sounds with =load-sounds!= and =play-sound=