examples: Simplify text example.
[chickadee.git] / chickadee.scm
CommitLineData
98dc87a0 1;;; Chickadee Game Toolkit
d60cca3c 2;;; Copyright © 2016, 2018 David Thompson <davet@gnu.org>
98dc87a0
DT
3;;;
4;;; Chickadee is free software: you can redistribute it and/or modify
5;;; it under the terms of the GNU General Public License as published
6;;; by the Free Software Foundation, either version 3 of the License,
7;;; or (at your option) any later version.
8;;;
9;;; Chickadee is distributed in the hope that it will be useful, but
10;;; WITHOUT ANY WARRANTY; without even the implied warranty of
11;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12;;; General Public License for more details.
13;;;
14;;; You should have received a copy of the GNU General Public License
15;;; along with this program. If not, see
16;;; <http://www.gnu.org/licenses/>.
17
18(define-module (chickadee)
e7bb72c9 19 #:export (run-game
517d1297 20 abort-game))
98dc87a0 21
d60cca3c
DT
22\f
23;;;
24;;; Error handling
25;;;
98dc87a0 26
d60cca3c 27(define (call-with-error-handling handler thunk)
e7bb72c9
DT
28 (if handler
29 (let ((stack #f))
30 (catch #t
31 (lambda ()
32 (thunk)
33 #f)
34 (lambda (key . args)
35 (handler stack key args)
36 #t)
37 (lambda (key . args)
38 (set! stack (make-stack #t 3)))))
39 (begin
7d64cb37 40 (thunk)
e7bb72c9 41 #f)))
d60cca3c
DT
42
43(define-syntax-rule (with-error-handling handler body ...)
0e67d24f
DT
44 (call-with-error-handling handler (lambda () body ...)))
45
46(define (default-error-handler stack key args)
47 (apply throw key args))
d60cca3c
DT
48
49\f
50;;;
0e67d24f 51;;; Game loop kernel
d60cca3c
DT
52;;;
53
54(define game-loop-prompt-tag (make-prompt-tag 'game-loop))
55
56(define (abort-game)
57 (abort-to-prompt game-loop-prompt-tag #f))
58
e7bb72c9 59(define* (run-game #:key update render time error
0e67d24f 60 (update-hz 60))
d60cca3c
DT
61 (let ((timestep (round (/ 1000 update-hz))))
62 (call-with-prompt game-loop-prompt-tag
63 (lambda ()
64 ;; Catch SIGINT and kill the loop.
65 (sigaction SIGINT
66 (lambda (signum)
67 (abort-game)))
68 ;; A simple analogy is that we are filling up a bucket
69 ;; with water. When the bucket fills up to a marked
70 ;; line, we dump it out. Our water is time, and each
71 ;; time we dump the bucket we update the game. Updating
72 ;; the game on a fixed timestep like this yields a
73 ;; stable simulation.
74 (let loop ((previous-time (time))
75 (buffer 0))
76 (let* ((current-time (time))
77 (delta (- current-time previous-time)))
78 (let update-loop ((buffer (+ buffer delta)))
79 (if (>= buffer timestep)
7d64cb37
DT
80 ;; Short-circuit the update loop if an error
81 ;; occurred, and reset the current time to now in
82 ;; order to discard the undefined amount of time
83 ;; that was spent handling the error.
84 (if (with-error-handling error (update timestep))
85 (loop (time) 0)
c2f9c961
DT
86 (begin
87 (usleep 1)
88 (update-loop (- buffer timestep))))
d60cca3c
DT
89 (begin
90 ;; We render upon every iteration of the loop, and
91 ;; thus rendering is decoupled from updating.
92 ;; It's possible to render multiple times before
93 ;; an update is performed.
7d64cb37
DT
94 (if (with-error-handling error (render (/ buffer timestep)))
95 (loop (time) 0)
96 (loop current-time buffer))))))))
d60cca3c
DT
97 (lambda (cont callback)
98 #f))))