5.1.1 The Game Loop
At the very core of Chickadee there is an event loop. This loop, or
“kernel”, is responsible for ensuring that the game is updated at
the desired interval, handling input devices, rendering the current
state of the game world, and handling errors if they occur. The
kernel implements what is known as a “fixed timestep” game loop,
meaning that the game simulation will be advanced by a fixed interval
of time and will never vary from frame to frame, unlike some other
styles of game loops. The appropriately named run-game
and
abort-game
procedures are the entry and exit points to the
Chickadee game loop.
If you are using chickadee play
to launch your game, then
calling run-game
is already taken care of for you.
- Procedure: run-game [#:window-title "Chickadee!"] [#:window-width 640] [#:window-height 480] [#:window-fullscreen?
#f
] [#:window-resizable? #f
] [#:update-hz 60] [#:clear-color] [#:load] [#:update] [#:draw] [#:quit] [#:key-press] [#:key-release] [#:text-input] [#:mouse-press] [#:mouse-release] [#:mouse-move] [#:controller-add] [#:controller-remove] [#:controller-press] [#:controller-release] [#:controller-move] [#:window-keyboard-enter] [#:window-keyboard-leave] [#:window-mouse-enter] [#:window-mouse-leave] [#:window-show] [#:window-hide] [#:window-minimize] [#:window-maximize] [#:window-move] [#:window-resize] [#:error]
-
Run the Chickadee game loop.
A new graphical window will be opened with window-width x
window-height as its dimensions, window-title as its
title, and in fullscreen mode if window-fullscreen? is
#t
. If window-resizable? is #t
then the window
can be resized by the user. The screen color will be set to
clear-color, or a pleasant light blue, by default.
- load: Called with zero arguments when the game window has opened
but before the game loop has started. Can be used to perform
initialization that requires an open window and OpenGL context such as
loading textures.
- update: Called update-hz times per second with one
argument: The amount of time to advance the game simulation.
- draw: Called each time a frame should be rendered with a single
argument known as the
alpha
value. See the documentation for
run-game*
for an explanation of this value.
- quit: Called with zero arguments when the user tries to close
the game window. The default behavior is to exit the game.
- key-press: Called with four arguments when a key is pressed on
the keyboard:
- key: The symbolic name of the key that was pressed. For
example:
backspace
.
- modifiers: A list of the symbolic names of modifier keys that
were being held down when the key was pressed. Possible values
include
ctrl
, alt
, and shift
.
- repeat?:
#t
if this is a repeated press of the same key.
- key-release: Called with three arguments when a key is released
on the keyboard:
- key: The symbolic name of the key that was released.
- modifiers: A list of the symbolic names of modifier keys that
were being held down when the key was released.
- text-input: Called with a single argument, a string of text,
when printable text is typed on the keyboard.
- mouse-press: Called with four arguments when a mouse button is
pressed:
- button: The symbolic name of the button that was pressed, such
as
left
, middle
, or right
.
- clicks: The number of times the button has been clicked in a row.
- x: The x coordinate of the mouse cursor.
- y: The y coordinate of the mouse cursor.
- mouse-release: Called with three arguments when a mouse button
is released:
- button: The symbolic name of the button that was released.
- x: The x coordinate of the mouse cursor.
- y: The y coordinate of the mouse cursor.
- mouse-move: Called with five arguments when the mouse is moved:
- x: The x coordinate of the mouse cursor.
- y: The y coordinate of the mouse cursor.
- dx: The amount the mouse has moved along the x axis since the
last mouse move event.
- dy: The amount the mouse has moved along the y axis since the
last mouse move event.
- buttons: A list of the buttons that were pressed down when the
mouse was moved.
- mouse-wheel: Called with two arguments when the mouse wheel is
scrolled:
- x: The scroll amount along the X axis.
- y: The scroll amount along the Y axis.
- controller-add: Called with a single argument, an SDL game
controller object, when a game controller is connected.
- controller-remove: Called with a single argument, an SDL game
controller object, when a game controller is disconnected.
- controller-press: Called with two arguments when a button on a
game controller is pressed:
- controller: The controller that triggered the event.
- button: The symbolic name of the button that was pressed.
Possible buttons are:
-
a
-
b
-
x
-
y
-
back
-
guide
-
start
-
left-stick
-
right-stick
-
left-shoulder
-
right-shoulder
-
dpad-up
-
dpad-down
-
dpad-left
-
dpad-right
- controller-release: Called with two arguments when a button on a
game controller is released:
- controller: The controller that triggered the event.
- button: The symbolic name of the button that was released.
- controller-move: Called with three arguments when an analog
stick or trigger on a game controller is moved:
- controller: The controller that triggered the event.
- axis: The symbolic name of the axis that was moved. Possible
values are:
-
left-x
-
left-y
-
right-x
-
right-y
-
trigger-left
-
trigger-right
- window-keyboard-enter: Called with zero arguments when the
window gains keyboard focus.
- window-keyboard-leave: Called with zero arguments when the
window loses keyboard focus.
- window-mouse-enter: Called with zero arguments when the window
gains mouse focus.
- window-mouse-leave: Called with zero arguments when the window
loses mouse focus.
- window-show: Called with zero arguments when the window is
shown after having been hidden.
- window-hide: Called with zero arguments when the window is
hidden.
- window-minimize: Called with zero arguments when the window is
minimized.
- window-maximize: Called with zero arguments when the window is
maximized.
- window-move: Called with two arguments when the window is moved
within the desktop environment.
- x: The x coordinate of the top-left corner of the window, in
pixels.
- y: The y coordinate of the top-left corner of the window, in
pixels.
Desktop environments use the top-left corner as the origin rather than
the bottom-left like Chickadee does, hence the discrepancy here.
- window-resize: Called with zero arguments when the window is
resized.
- width: The new width in pixels.
- height: The new height in pixels.
- error: Called with two arguments when an error occurs:
- exception: The exception object.
- stack: The call stack at the point of the exception.
If no error handler is specified, exceptions will simply be re-raised.
To stop the game loop, simply call abort-game
.
- Procedure: abort-game
Stop the currently running Chickadee game loop.
The above explanation of the game loop was partially a lie. It’s true
that there is a game loop at the center of Chickadee, but
run-game
is not it’s true entry point. There exists an even
lower level procedure, run-game*
, in the (chickadee
game-loop)
module that run-game
uses under the hood.
On its own, run-game*
does not do very much at all. In order
to actually respond to input events, update game state, or render
output, the developer must provide an engine. run-game
is such
an engine, and it’s likely all a developer will need. However, what
if a developer wanted to use all of the useful Chickadee features to
make a terminal roguelike game instead? Chickadee doesn’t come with a
terminal rendering engine, but the developer could write one without
having to write their own core game loop.
- Procedure: run-game* [#:init] [#:update] [#:render] [#:time] [#:error] [#:update-hz 60]
-
Start the game loop. This procedure will not return until
abort-game
is called.
The core game loop is generic and requires four additional procedures
to operate:
- init: Called just before game loop is started. Use it to
perform game initialization.
- update: Called update-hz times per second to advance the
game simulation. This procedure is called with a single argument: The
amount of time that has passed since the last update, in milliseconds.
- render: Called each iteration of the loop to render the game to
the desired output device. This procedure is called with a single
argument: A value in the range [0, 1] which represents how much time
has past since the last game state update relative to the upcoming
game state update, as a percentage. Because the game state is updated
independent of rendering, it is often the case that rendering is
occuring between two updates. If the game is rendered as it was
during the last update, a strange side-effect will occur that makes
animation appear rough or “choppy”. To counter this, the
alpha value can be used to perfrom a linear interpolation of a
moving object between its current position and its previous position.
This odd trick has the pleasing result of making the animation look
smooth again, but requires keeping track of previous state.
- time: Called to get the current time in seconds. This procedure
is called with no arguments.
- error: Called when an error from the update or
render procedures reaches the game loop. This procedure is
called with three arguments: The call stack, the error key, and the
error arguments. If no error handler is provided, the default
behavior is to simply re-throw the error.
- Procedure: elapsed-time
Return the current value of the system timer in seconds.