From 67bec040c5edeb0a4ccc6b33cf8004ce729a930f Mon Sep 17 00:00:00 2001 From: David Thompson Date: Wed, 8 Apr 2020 08:59:11 -0400 Subject: doc: Expand and improve Kernel manual section. --- doc/api.texi | 309 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 231 insertions(+), 78 deletions(-) diff --git a/doc/api.texi b/doc/api.texi index 18e0841..f553f25 100644 --- a/doc/api.texi +++ b/doc/api.texi @@ -9,78 +9,29 @@ @node Kernel @section Kernel -At the very core of Chickadee, in the @code{(chickadee game-loop)} -module, lies an event loop. This loop, or ``kernel'', is responsible -for ensuring that the game is updated at the desired interval, -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 @code{run-game*} and @code{abort-game} procedures are the entry -and exit points to the Chickadee game loop kernel. - -On its own, the kernel does not do very much at all. In order to -actually respond to input events, update game state, or render output, -the programmer must provide an engine. But don't worry, you don't -have to start from scratch! Chickadee comes with a simple engine that -uses SDL to create a graphical window and handle input devices, and -OpenGL to handle rendering. This default engine is enough for most -users to get started writing games quickly. More advanced users may -want to write a custom engine that uses a different I/O system. -Perhaps you are writing a text adventure or roguelike that reads from -and writes to a terminal instead of a graphical window. The game loop -kernel makes no assumptions. +This section of the manual covers the foundation of Chickadee: The +game loop and desktop environment interaction. -@deffn {Procedure} run-game* [#:update] [#:render] [#:time] [#:error] @ - [#:update-hz 60] - -Start the game loop. This procedure will not return until -@code{abort-game} is called. - -The core game loop is generic and requires four additional procedures -to operate: - -@itemize -@item -@var{update}: Called @var{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. -@item -@var{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 -@var{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. -@item -@var{time}: Called to get the current time in milliseconds. This -procedure is called with no arguments. -@item -@var{error}: Called when an error from the @var{update} or -@var{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. -@end itemize - -@end deffn +@menu +* The Game Loop:: The core event loop. +* Input Devices:: Mouse, keyboard, controller input. +* Window Manipulation:: Inspect and modify the graphical window. +* Live Coding:: Tips for building games from the REPL. +@end menu -@deffn {Procedure} abort-game -Stop the currently running Chickadee game loop. -@end deffn +@node The Game Loop +@subsection The Game Loop -Since most users will want to write 2D/3D games with hardware -accelerated graphics rendering, controlled via keyboard, mouse, or -game controller, Chickadee comes with an easy to use engine just for -this purpose in the @code{(chickadee)} module: @code{run-game}. +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 @code{run-game} and +@code{abort-game} procedures are the entry and exit points to the +Chickadee game loop. @deffn {Procedure} run-game [#:window-title "Chickadee!"] @ [#:window-width 640] [#:window-height 480] @ @@ -91,7 +42,7 @@ this purpose in the @code{(chickadee)} module: @code{run-game}. [#:controller-add] [#:controller-remove] [#:controller-press] @ [#:controller-release] [#:controller-move] [#:error] -Run the Chickadee game loop using the SDL engine in OpenGL mode. +Run the Chickadee game loop. A new graphical window will be opened with @var{window-width} x @var{window-height} as its dimensions, @var{window-title} as its @@ -351,6 +302,206 @@ The default behavior is to re-throw the error. @end deffn +To stop the game loop, simply call @code{abort-game}. + +@deffn {Procedure} abort-game +Stop the currently running Chickadee game loop. +@end deffn + +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 +@code{run-game} is not it's true entry point. There exists an even +lower level procedure, @code{run-game*}, in the @code{(chickadee +game-loop)} module that @code{run-game} uses under the hood. + +On its own, @code{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. @code{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. + +@deffn {Procedure} run-game* [#:update] [#:render] [#:time] [#:error] @ + [#:update-hz 60] + +Start the game loop. This procedure will not return until +@code{abort-game} is called. + +The core game loop is generic and requires four additional procedures +to operate: + +@itemize +@item +@var{update}: Called @var{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. +@item +@var{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 +@var{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. +@item +@var{time}: Called to get the current time in milliseconds. This +procedure is called with no arguments. +@item +@var{error}: Called when an error from the @var{update} or +@var{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. +@end itemize + +@end deffn + +@node Input Devices +@subsection Input Devices + +While @code{run-game} provides hooks for mouse/keyboard/controller +input events, it is often necessary to query input devices for their +current state. For example, it could be desirable to query the state +of the arrow keys every time the update hook is called to determine +which direction the player should move that frame. + +@deffn {Procedure} key-pressed? key +Return @code{#t} if @var{key} is currently being pressed. +@end deffn + +@deffn {Procedure} key-released? key +Return @code{#t} if @var{key} is @emph{not} currently being pressed. +@end deffn + +@deffn {Procedure} mouse-x +Return the current X coordinate of the mouse cursor. +@end deffn + +@deffn {Procedure} mouse-y +Return the current Y coordinate of the mouse cursor. +@end deffn + +@deffn {Procedure} mouse-button-pressed? button +Return @code{#t} if @var{button} is currently being pressed. +@end deffn + +@deffn {Procedure} mouse-button-released? button +Return @code{#t} if @var{button} is @emph{not} currently being +pressed. +@end deffn + +@deffn {Procedure} controller-axis controller axis +Return a floating point value in the range [-1, 1] corresponding to +how much @var{axis} (an analog stick or trigger) is being pushed on +@var{controller}. 0 is returned if @var{axis} is not being pushed at +all. +@end deffn + +@deffn {Procedure} controller-name controller +Return the name of @var{controller}. +@end deffn + +@deffn {Procedure} controller-button-pressed? controller button +Return @code{#t} if @var{button} on @var{controller} is currently +being pressed. +@end deffn + +@deffn {Procedure} controller-button-released? controller button +Return @code{#t} if @var{button} on @var{controller} is @emph{not} +currently being pressed. +@end deffn + +@node Window Manipulation +@subsection Window Manipulation + +@deffn {Procedure} current-window +Return the currently active game window. +@end deffn + +@deffn {Procedure} window? obj +Return @code{#t} if @var{obj} is a window object. +@end deffn + +@deffn {Procedure} window-title window +Return the title of @var{window}. +@end deffn + +@deffn {Procedure} window-width window +Return the width of @var{window} in pixels. +@end deffn + +@deffn {Procedure} window-height window +Return the height of @var{window} in pixels. +@end deffn + +@deffn {Procedure} window-x window +Retun the X coordinate of the upper-left corner of @var{window}. +@end deffn + +@deffn {Procedure} window-y window +Return the Y coordinate of the upper-left corner of @var{window}. +@end deffn + +@deffn {Procedure} hide-window! window +Hide @var{window}. +@end deffn + +@deffn {Procedure} show-window! window +Show @var{window}. +@end deffn + +@deffn {Procedure} maximize-window! window +Maximize @var{window}. +@end deffn + +@deffn {Procedure} minimize-window! window +Minimize @var{window}. +@end deffn + +@deffn {Procedure} raise-window! window +Make @var{window} visible over all other windows. +@end deffn + +@deffn {Procedure} restore-window! window +Restore the size and position of a minimized or maximized +@var{window}. +@end deffn + +@deffn {Procedure} set-window-border! window border? +Enable/disable the border around @var{window}. If @var{border?} is +@code{#f}, the border is disabled, otherwise it is enabled. +@end deffn + +@deffn {Procedure} set-window-title! window title +Change the title of @var{window} to @var{title}. +@end deffn + +@deffn {Procedure} set-window-size! window width height +Change the dimensions of @var{window} to @var{width} x @var{height} +pixels. +@end deffn + +@deffn {Procedure} set-window-position! window x y +Move the upper-left corner of @var{window} to pixel coordinates +(@var{x}, @var{y}). +@end deffn + +@deffn {Procedure} set-window-fullscreen! window fullscreen? +Enable or disable fullscreen mode for @var{window}. If +@var{fullscreen?} is @code{#f}, fullscreen mode is disabled, otherwise +it is enabled. +@end deffn + + +@node Live Coding @subsection Live Coding One of the biggest appeals of any Lisp dialect is the ability to use @@ -363,18 +514,22 @@ fairly easy to hook up a special kind of REPL yourself. First, create a cooperative REPL server (It's important to use Guile's cooperative REPL server instead of the standard REPL server in -@code{(system repl server)} to avoid thread synchronization issues): +@code{(system repl server)} to avoid thread synchronization issues). +Then, in the game loop's update procedure, call +@code{poll-coop-repl-server} and pass the REPL object. Here is a +template to follow: @example -(use-modules (system repl coop-server)) +(use-modules (chickadee) + (system repl coop-server)) (define repl (spawn-coop-repl-server)) -@end example -Then, in the game loop's update procedure, add this: +(define (update dt) + (poll-coop-repl-server repl) + ...) -@example -(poll-coop-repl-server repl) +(run-game #:update update ...) @end example To use the REPL, connect to it via port 37146. Telnet will do the @@ -382,8 +537,6 @@ trick, but using the @uref{https://www.nongnu.org/geiser/, Geiser} extension for Emacs is by far the best way to develop at the REPL with Guile. Use @code{M-x connect-to-guile} to connect to the REPL server. -Happy hacking! - @node Math @section Math -- cgit v1.2.3