aboutsummaryrefslogtreecommitdiff
path: root/demo/sandbox.scm
diff options
context:
space:
mode:
authorGene Pasquet <dev@etenil.net>2026-04-08 00:30:11 +0100
committerGene Pasquet <dev@etenil.net>2026-04-08 00:30:11 +0100
commitf8cc4a748bb8b6431a1023a876745b1bb473eb19 (patch)
treeaf708ac1138ee17d35d9b1ba46ec8b56acaccedb /demo/sandbox.scm
parentcfddc2f180552afdb080968f847018c5a223b41a (diff)
Support entity groups
Diffstat (limited to 'demo/sandbox.scm')
-rw-r--r--demo/sandbox.scm281
1 files changed, 164 insertions, 117 deletions
diff --git a/demo/sandbox.scm b/demo/sandbox.scm
index ad2e056..d7bd53f 100644
--- a/demo/sandbox.scm
+++ b/demo/sandbox.scm
@@ -1,7 +1,8 @@
(import scheme
(chicken base)
(chicken random)
- (only srfi-1 drop iota take)
+ (only srfi-1 iota take)
+ (only srfi-197 chain)
(prefix sdl2 "sdl2:")
(prefix sdl2-ttf "ttf:")
(prefix sdl2-image "img:")
@@ -11,147 +12,193 @@
downstroke-physics
downstroke-assets
downstroke-entity
- downstroke-scene-loader)
-
-(define *demo-t* 0.0)
-
-(define +static-skip+
- '(jump acceleration gravity velocity-x velocity-y
- tile-collisions-x tile-collisions-y on-solid))
-
-;; Mid-level shelf only: immovable solid AABBs (same layout as the old tilemap shelf).
-(define (make-shelf-platform gw gh tw th)
- (let* ((shelf-tile-id 20)
- (shelf-c0 10)
- (shelf-n 10)
- (x0 (* shelf-c0 tw))
- (y (- gh (* 6 th))))
- (map (lambda (i)
- (list #:type 'static-tile
- #:x (+ x0 (* i tw)) #:y y #:width tw #:height th
- #:tile-id shelf-tile-id #:solid? #t #:immovable? #t
- #:gravity? #f #:vx 0 #:vy 0 #:on-ground? #f
- #:skip-pipelines +static-skip+))
- (iota shelf-n))))
-
-;; Floor only on the tilemap; shelf is entities (see make-shelf-platform).
+ downstroke-scene-loader
+ downstroke-tween
+ downstroke-prefabs)
+
+;; ── Constants ────────────────────────────────────────────────────────────────
+
+(define +game-width+ 600)
+(define +game-height+ 400)
+
+(define +demo-bot-cycle-ms+ 2600.0)
+(define +demo-bot-half-cycle-ms+ (/ +demo-bot-cycle-ms+ 2.0))
+(define +demo-bot-jump-interval-ms+ 720.0)
+
+;; ── Mutable demo state ──────────────────────────────────────────────────────
+
+(define *demo-t* 0.0)
+(define *shelf-endpoints* #f)
+(define *shelf-tween* #f)
+(define *shelf-origin* #f)
+
+;; ── Tween helpers ────────────────────────────────────────────────────────────
+
+(define (other-endpoint x endpoints)
+ (let ((lo (car endpoints))
+ (hi (cdr endpoints)))
+ (if (< (abs (- x lo)) (abs (- x hi))) hi lo)))
+
+(define (make-ping-pong-tween leader endpoints)
+ (make-tween leader
+ props: `((#:x . ,(other-endpoint (entity-ref leader #:x 0) endpoints)))
+ duration: 3500 ease: 'sine-in-out))
+
+(define (scene-replace-group-origin! scene gid new-origin)
+ (scene-entities-set! scene
+ (map (lambda (e)
+ (if (and (entity-ref e #:group-origin? #f)
+ (eq? (entity-ref e #:group-id) gid))
+ new-origin
+ e))
+ (scene-entities scene))))
+
+(define (advance-shelf-tween! scene dt)
+ (when (and *shelf-tween* *shelf-origin*)
+ (let ((gid (entity-ref *shelf-origin* #:group-id)))
+ (receive (tw2 e0) (tween-step *shelf-tween* *shelf-origin* dt)
+ (set! *shelf-tween* (if (tween-finished? tw2)
+ (make-ping-pong-tween e0 *shelf-endpoints*)
+ tw2))
+ (set! *shelf-origin* e0)
+ (scene-replace-group-origin! scene gid e0)))))
+
+;; ── Tilemap builder ──────────────────────────────────────────────────────────
+
(define (make-sandbox-tilemap ts tw th gw gh)
- (let* ((ncols (inexact->exact (ceiling (/ gw tw))))
- (nrows (inexact->exact (ceiling (/ gh th))))
- (floor-tile 20)
- (air (map (lambda (_) (map (lambda (_) 0) (iota ncols))) (iota nrows)))
- (floor-row (map (lambda (_) floor-tile) (iota ncols)))
- (map-data (append (take air (- nrows 1)) (list floor-row)))
- (layer (make-layer name: "ground"
- width: ncols height: nrows
- map: map-data)))
- (make-tilemap width: ncols height: nrows
- tilewidth: tw tileheight: th
- tileset-source: ""
- tileset: ts
- layers: (list layer)
- objects: '())))
+ (let* ((ncols (inexact->exact (ceiling (/ gw tw))))
+ (nrows (inexact->exact (ceiling (/ gh th))))
+ (empty-row (map (lambda (_) 0) (iota ncols)))
+ (floor-row (map (lambda (_) 20) (iota ncols)))
+ (map-data (append (map (lambda (_) empty-row) (iota (- nrows 1)))
+ (list floor-row))))
+ (make-tilemap
+ width: ncols height: nrows
+ tilewidth: tw tileheight: th
+ tileset-source: "" tileset: ts
+ layers: (list (make-layer name: "ground"
+ width: ncols height: nrows
+ map: map-data))
+ objects: '())))
+
+;; ── Entity factories ─────────────────────────────────────────────────────────
+
+(define (make-box x y tw th)
+ (list #:type 'box
+ #:x x #:y y #:width tw #:height th
+ #:vx 0 #:vy 0
+ #:gravity? #t #:on-ground? #f
+ #:solid? #t #:immovable? #f
+ #:tile-id 29))
(define (spawn-boxes tw th)
(map (lambda (i)
- (list #:type 'box
- #:x (+ 30 (* i 55)) #:y (+ 10 (* (pseudo-random-integer 4) 20))
- #:width tw #:height th
- #:vx 0 #:vy 0
- #:gravity? #t #:on-ground? #f
- #:solid? #t #:immovable? #f
- #:tile-id 29))
+ (make-box (+ 30 (* i 55))
+ (+ 10 (* (pseudo-random-integer 4) 20))
+ tw th))
(iota 8)))
-;; #:demo-id offsets phase; #:demo-since-jump accumulates ms for jump cadence.
(define (make-demo-bot x y tw th id)
(list #:type 'demo-bot
- #:x x #:y y
- #:width tw #:height th
+ #:x x #:y y #:width tw #:height th
#:vx 0 #:vy 0
#:gravity? #t #:on-ground? #f
#:solid? #t #:immovable? #f
#:tile-id 1
- #:demo-id id
- #:demo-since-jump 0))
+ #:demo-id id #:demo-since-jump 0))
+
+;; ── Per-entity physics ──────────────────────────────────────────────────────
+
+(define (run-physics e tm)
+ (chain e
+ (apply-gravity _)
+ (apply-velocity-x _)
+ (resolve-tile-collisions-x _ tm)
+ (apply-velocity-y _)
+ (resolve-tile-collisions-y _ tm)))
(define (update-demo-bot e dt tm)
- (let* ((id (entity-ref e #:demo-id 0))
- (cycle 2600.0)
- (phase (modulo (+ *demo-t* (* id 400.0)) cycle))
- (vx (if (< phase (/ cycle 2.0)) 3.0 -3.0))
- (e (entity-set e #:vx vx))
- ;; Set last frame by final pass: detect-on-solid after entity–entity resolve.
- (ground? (entity-ref e #:on-ground? #f))
- (since (+ (entity-ref e #:demo-since-jump 0) dt))
- (jump-every 720.0)
- (do-jump? (and ground? (>= since jump-every)))
- (since (if do-jump? 0 since))
- (e (entity-set e #:demo-since-jump since))
- (e (apply-jump e do-jump?))
- (e (apply-acceleration e))
- (e (apply-gravity e))
- (e (apply-velocity-x e))
- (e (resolve-tile-collisions-x e tm))
- (e (apply-velocity-y e))
- (e (resolve-tile-collisions-y e tm)))
- e))
-
-(define (update-box e tm)
- (let* ((e (apply-gravity e))
- (e (apply-velocity-x e))
- (e (resolve-tile-collisions-x e tm))
- (e (apply-velocity-y e))
- (e (resolve-tile-collisions-y e tm)))
- e))
+ (let* ((id (entity-ref e #:demo-id 0))
+ (phase (modulo (+ *demo-t* (* id 400.0)) +demo-bot-cycle-ms+))
+ (vx (if (< phase +demo-bot-half-cycle-ms+) 3.0 -3.0))
+ (e (entity-set e #:vx vx))
+ (ground? (entity-ref e #:on-ground? #f))
+ (since (+ (entity-ref e #:demo-since-jump 0) dt))
+ (jump? (and ground? (>= since +demo-bot-jump-interval-ms+)))
+ (since (if jump? 0 since)))
+ (chain (entity-set e #:demo-since-jump since)
+ (apply-jump _ jump?)
+ (apply-acceleration _)
+ (run-physics _ tm))))
+
+(define (integrate-entity e dt tm)
+ (case (entity-type e)
+ ((demo-bot) (update-demo-bot e dt tm))
+ ((box) (run-physics e tm))
+ (else
+ (if (and (entity-ref e #:group-origin? #f)
+ (entity-ref e #:gravity? #f))
+ (run-physics e tm)
+ e))))
+
+;; ── Scene builder ───────────────────────────────────────────────────────────
+
+(define (init-shelf-tween! shelf-list tw)
+ (let* ((origin (car shelf-list))
+ (x-left (entity-ref origin #:x 0))
+ (x-right (+ x-left (* 6 tw))))
+ (set! *shelf-origin* origin)
+ (set! *shelf-endpoints* (cons x-left x-right))
+ (set! *shelf-tween* (make-ping-pong-tween origin *shelf-endpoints*))))
+
+(define (make-sandbox-scene game)
+ (let* ((reg (load-prefabs "demo/assets/sandbox-groups.scm" (engine-mixins) '()))
+ (ts (game-load-tileset! game 'tileset "demo/assets/monochrome_transparent.tsx"))
+ (tw (tileset-tilewidth ts))
+ (th (tileset-tileheight ts))
+ (tex (create-texture-from-tileset (game-renderer game) ts))
+ (gw (game-width game))
+ (gh (game-height game))
+ (tm (make-sandbox-tilemap ts tw th gw gh))
+ (shelf-list (instantiate-group-prefab reg 'shelf-platform
+ (* 10 tw) (- gh (* 6 th))))
+ (raft-list (instantiate-group-prefab reg 'collision-raft
+ 120 (- gh (* 14 th))))
+ (bots (list (make-demo-bot 80 80 tw th 0)
+ (make-demo-bot 220 60 tw th 1)
+ (make-demo-bot 380 100 tw th 2))))
+ (init-shelf-tween! shelf-list tw)
+ (make-scene
+ entities: (append shelf-list raft-list (spawn-boxes tw th) bots)
+ tilemap: tm
+ tileset: #f
+ camera: (make-camera x: 0 y: 0)
+ tileset-texture: tex
+ camera-target: #f
+ background: '(32 34 40))))
+
+;; ── Game ─────────────────────────────────────────────────────────────────────
(define *game*
(make-game
- title: "Demo: Physics Sandbox" width: 600 height: 400
+ title: "Demo: Physics Sandbox"
+ width: +game-width+ height: +game-height+
create: (lambda (game)
- (let* ((ts (game-load-tileset! game 'tileset
- "demo/assets/monochrome_transparent.tsx"))
- (tw (tileset-tilewidth ts))
- (th (tileset-tileheight ts))
- (tex (create-texture-from-tileset (game-renderer game) ts))
- (gw (game-width game))
- (gh (game-height game))
- (tm (make-sandbox-tilemap ts tw th gw gh))
- (shelf (make-shelf-platform gw gh tw th))
- (bots
- (list (make-demo-bot 80 80 tw th 0)
- (make-demo-bot 220 60 tw th 1)
- (make-demo-bot 380 100 tw th 2)))
- (entities (append shelf (spawn-boxes tw th) bots))
- (scene (make-scene
- entities: entities
- tilemap: tm
- tileset: #f
- camera: (make-camera x: 0 y: 0)
- tileset-texture: tex
- camera-target: #f
- background: '(32 34 40))))
- (game-scene-set! game scene)))
+ (game-scene-set! game (make-sandbox-scene game)))
update: (lambda (game dt)
(set! *demo-t* (+ *demo-t* dt))
(let* ((scene (game-scene game))
(tm (scene-tilemap scene)))
+ (advance-shelf-tween! scene dt)
+ (scene-update-entities scene (lambda (e) (integrate-entity e dt tm)))
+ (scene-sync-groups! scene)
+ (scene-resolve-collisions scene)
(scene-update-entities scene
(lambda (e)
- (cond
- ((eq? (entity-type e) 'demo-bot)
- (update-demo-bot e dt tm))
- ((eq? (entity-type e) 'box)
- (update-box e tm))
- (else e))))
- (scene-resolve-collisions scene)
- (let ((post (scene-entities scene)))
- (scene-update-entities scene
- (lambda (e)
- (if (entity-ref e #:gravity? #f)
- (detect-on-solid e tm post)
- e))))))))
+ (if (entity-ref e #:gravity? #f)
+ (detect-on-solid e tm (scene-entities scene))
+ e)))))))
(game-run! *game*)