User Manual

Contents

Overview

trivial-gamekit is a very simple toolkit for making games in Common Lisp. Its goal is to be as simple as possible while allowing a user to make a complete game s/he can distribute.

It manages system resources for you by creating and initializing a window, abstracting away keyboard and mouse input and any other system-dependent configuration. trivial-gamekit also spawns a default game loop for you allowing to hook into it for drawing and other per frame activities via certain methods described below.

It strives to fully support Common Lisp’s interactive development approach where you change your application on the fly and allowed to gracefully recover from programming errors via restarts without crashing or rerunning an application.

Defining a game

gamekit’s central idea is a game object you define via defgame macro. It contains all the state gamekit needs to run your game. It is instantiated by #'start which also bootstraps the whole application and opens a window, runs your game logic, rendering, etc. Only single game object can be run at a time, so subsequently calling #'start for the same or different game objects is disallowed - you need to stop game execution first. To stop a running game you would need to call #'stop.

You can put game initialization code into #'post-initialize. Specifically, it is probably a best place to bind input via #'bind-cursor or #'bind-button or any gamepad-related functions as described below, but you are free to do any other preparations that require fully initialized game instance. If you need to release any unmanaged resources (e.g. foreign memory you allocated during a game or in #'post-initialize) to avoid leaks you can override #'pre-destroy function which is called after last game loop iteration.

#'start also invokes a default game loop. To hook into it you can use #'act and #'draw methods. #'act is used for updaing your game state and #'draw should be used exclusively for drawing/rendering. :draw-rate and :act-rate options of defgame can be used to specify rate for #'draw and #'act in invocations per second separately, meaning if you set :draw-rate to 60 your framerate would be set to 60 frames per second.

Math

For moving objects around, defining their place on a canvas and giving them a color we need a bit of a math applied. gamekit points and positions are represented as two-dimensional vectors created with #'vec2. Colors consist of four elements: red, green, blue and alpha, so they are represented by four-dimensional vecors created via #'vec4.

Some vector operations are exported: #'mult for element-wise multiplication, #'add for addition, #'subt for subtraction and #'div for element-wise division.

Element accessor methods for setting or getting values out of vectors are also exported: #'x to access first element, #'y for a second, #'z - third and #'w to access fourth.

Locating resources

Resources are very important for games. Textures, models, sounds, fonts, animations, tiles - you name it. gamekit has several routines prepared for you to ease the handling of those.

First of all, we need to tell gamekit where to find resources via #'define-image, #'define-sound and #'define-font macros. First argument to them is a symbol that would become an identificator for the resource and the second argument is a absoulute or relative path to the resource.

For absolute path, be sure to use helper functions like asdf:system-relative-pathname to avoid hardcoding paths to your resources in the code.

To use relative resource path, you first need to specify resource root directory. Resource roots are associated with Common Lisp packages. Any package can have associated filesystem directory to find resources in. Call #'register-resource-package as a toplevel form to bind a directory to a package. Absolute paths required here. Once done, you can specify relative path in define-* forms and gamekit would merge resource root pathname associated with a package of a symbol (first argument to define-*) with relative pathname you provided.

Resources are used by functions that requires them to operate, like #'draw-image, #'make-font, #'play-sound and others.

When defgame option :prepare-resources is set to nil, you would need to request resources manually by calling #'prepare-resources with resource ids you need. Preparation can take a while, so #'prepare-resources asynchonously requests resources and returns immediately for you to be notified later with #'notice-resources. This is useful, when you don’t want to wait until all assets are loaded and start rendering some loading or start screen right away and continue with game after all resources are ready. If you have a lot of resources and would like to get rid of unneeded ones to free some memory, you can use #'dispose-resources.

Sometimes you would want to ship a game with custom resources not supported by gamekit directly. To accomplish that, you can use define-text to include text files and define-binary to include any file basically, which would be represented as a simple array of bytes. To access those resources you would need to use #'get-text and #'get-binary correspondingly.

Drawing

gamekit provides simple to use but versatile drawing API. If you know how HTML5 canvas API is organized you will find gamekit’s one quite similar. Several functions with names starting with draw- exists to help you draw primitives like lines, rectangles, ellipses, arcs, polygons or even bezier curves. Images can be drawn via #'draw-image and text with customizable font can be put onto canvas via #'draw-text.

Oftentimes it is useful to know dimensions of the visual resources to position them correctly. #'image-width and #'image-height can help you with retreiving image dimensions while #'calc-text-bounds will allow you to calculate text bounding box.

Canvas transformation operations are supported too. You can scale, translate, rotate a canvas with #'scale-canvas, #'translate-canvas and #'rotate-canvas accordingly. If you need to keep canvas state for nested drawing operations you will appreciate existence of #'with-pushed-canvas macro, that keeps canvas state to the dynamic extent of its body. This means that upon returning from the macro canvas transformation state will be returned to its original state before the macro and all transformation operations inside its body would be canceled out.

All drawing operations should be performed inside #'draw.

Unlike many other drawing APIs gamekit uses bottom left corner as an origin (0,0) with y-axis pointing upwards which is more convenient mathematically.

All origins or positions are represented by two-dimensional (x,y) vectors created via #'vec2. Colors are passed around as four-dimensional vectors made with #'vec4 consisting of red, green, blue and alpha channels each within 0.0-1.0 range.

Playing audio

Audio can substantially boost game’s atmosphere, and gamekit is ready to serve you well in this regard too. #'play-sound will help with getting sounds to reach your users ears. On the other hand, #'stop-sound can be used to stop this process.

Listening to input

There’s no game without some sort of interaction with a player. gamekit allows you to grab keyboard and mouse input to listen for player actions. You can pass a callback to #'bind-button which will be called upon pressing or releasing keyboard/mouse buttons. Callback passed to #'bind-cursor is going to be invoked when user moves a mouse. Callbacks provided are not stacked together, meaning if you try to bind multiple callbacks to the same button only last callback is actually going to be invoked. Same goes for cursor input.

gamekit also support gamepads and exposing them as a generic xbox controllers. You can listen to gamepads being connected and disconnected with #'bind-any-gamepad. Unfortunately, gamekit doesn’t have a reliable way to numerate gamepads, so gamepad-related functions operate on opaque gamepad references and you need to manage player<->gamepad relationship yourself.

For listening to gamepad buttons you can use #'bind-gamepad-button and #'bind-gamepad-any-button. #'bind-gamepad-dpad can help to catch changes in d-pad state. #'bind-gamepad-stick and #'bind-gamepad-trigger can help tracking position of right and left sticks and values of left and right triggers accordingly.

Sometimes gamekit wouldn’t be able to recognize your gamepad. In this case you need to specify path to custom controller mapping file in SDL2 format in BODGE_GAMECONTROLLERDB environment variable and restart the game.

Building a distributable

Sharing a Common Lisp game in a binary form amongst users was always a bit of a pain. But fear no more! gamekit includes a mechanism for delivering your game packaged using only a single function - #'deliver. It will build an executable, pack all required resources, copy needed foreign libraries used by trivial-gamekit and compress all that into a shippable archive.

To gain access to this function you need additionally load :trivial-gamekit/distribution system, and then you would be able to find it in gamekit.distribution package.

(ql:quickload :trivial-gamekit/distribution)

For building these packages for Windows, GNU/Linux and macOS with just a single push to a github or gitlab respository check out Advanced Features section.

Troubleshooting

Restarts

Common Lisp is well known for its superior interactive development capabilities, and when things go left you often can fix problem live without restarting an image. gamekit strives to embrace this approach as much as possible. But beware, under the hood gamekit uses quite an intricate machinery and it still is possible to break it beyond live repairement.

Important thing to keep in mind: don’t invoke 'abort restart that quit threads. It’s too easy, because q button in slime or sly is bound to quit action in debugger which invokes most destroying restart possible. It often works in other applications, but in complex multithreaded environments it can be disastrous. It’s quite hard (if impossible) to restore gamekit state after any of its internal threads are killed. Just don’t press q.

sly and slime has other more useful button bindings available: c - for 'continue restart and a for topmost 'abort restart. gamekit binds those exact restarts to gracefully handle failures. 'continue will try to skip the offending block of code and 'abort will try to shutdown engine gracefully altogether after which you should be able to start your game with #'gamekit:start. The latter one is not very interactive-friendly obviously, and should be the last resort before restarting the whole image (lisp process).

Another restart that isn’t bound to a key but super useful is 'rerun-flow-block. Ivoking it will prompt gamekit to rerun a code the error was in. Fixing the error, reevaluating the code and invoking this restart should bring your game back to working state. This is the restart you might want to use the most.

'skip-flow-block restart does the same thing as 'continue - skips the code with the error.

Only one active system of type ‘appkit-system is allowed

Invoke (gamekit:stop) in REPL and then try running your game again by invoking #'gamekit:start with required params. If that didn’t help - reload a lisp image.

Deliver fails

Symbol Index

macro defgame (name (&rest classes) &body ((&rest slots) &rest opts))

Defines a game class that can be passed to #'start to run a game. name is the name of a class generated. classes are names of superclasses, slots - standard class slots and opts are class options. So, pretty much standard class definition except it does configure a class in certain ways specifically for gamekit use and allows passing additional options in opts apart from standard :documentation, :default-initargs and so others.

Additional options that can be passed in opts are:

  • :viewport-width - width of the window and canvas
  • :viewport-height - height of the window and canvas
  • :viewport-title - title of the window
  • :prepare-resources - boolean value that indicates whether gamekit should load resources automatically on startup or if not, user prefers to load them dynamically on request. Defaults to t.

Example:

(gamekit:defgame example ()
  ;; some game related state
  ((world :initform (make-instance 'world))
   (game-state))
  ;; options
  (:viewport-width 800)
  (:viewport-height 600)
  (:viewport-title "EXAMPLE")
  (:prepare-resources nil))

function start (classname &key (log-level info) (opengl-version '(3 3)) samples blocking viewport-resizable (viewport-decorated t) (autoscaled t) swap-interval properties)

Bootsraps a game allocating a window and other system resources. Instantiates game object defined with defgame which can be obtained via #'gamekit. Cannot be called twice - #'stop should be called first before running start again.

Example:

(gamekit:start 'example)

function stop (&key blocking)

Stops currently running game releasing acquired resources.

Example:

(gamekit:stop)

function gamekit ()

Returns instance of a running game or nil if no game is started yet.

Example:

 (gamekit:gamekit)

generic post-initialize (system)

This function is called after game instance is fully initialized, right before main game loop starts its execution. Put initialization code for your application into method of this function. For example, it would be logical to bind input via #'bind-cursor or #'bind-button here.

Example:

(defmethod gamekit:post-initialize ((this example))
  (init-game)
  (bind-input))

generic pre-destroy (system)

This function is called just before shutting down a game instance for you to free all acquired resources and do any other clean up procedures.

Example:

 (defmethod gamekit:pre-destroy ((this example))
   (release-foreign-memory)
   (stop-threads))

generic act (system)

Called every game loop iteration for user to add any per-frame behavior to the game. NOTE: all drawing operations should be performed in #'draw method.

Example:

 (defmethod gamekit:act ((this example))
   (report-fps))

generic draw (system)

Called every game loop iteration for frame rendering. All drawing operations should be performed in this method.

Example:

 (defmethod gamekit:draw ((this example))
   (gamekit:draw-text "Hello, Gamedev!" (gamekit:vec2 10 10)))

function viewport-width ()

Returns width of a gamekit viewport (window) if there’s an active gamekit instance (started via #'start) or nil otherwise.

Example:

  (gamekit:viewport-width)

function viewport-height ()

Returns height of a gamekit viewport (window) if there’s an active gamekit instance (started via #'start) or nil otherwise.

Example:

  (gamekit:viewport-height)

function vec2 (&optional (x 0.0) (y 0.0))

Makes a two-dimensional vector.

Example:

 (gamekit:vec2 0 0)

function vec3 (&optional (x 0.0) (y 0.0) (z 0.0))

Makes a three-dimensional vector.

Example:

 (gamekit:vec3 1 1 2)

function vec4 (&optional (x 0.0) (y 0.0) (z 0.0) (w 0.0))

Makes a four-dimensional vector.

Example:

 (gamekit:vec4 1 1 2 3)

function mult (arg0 &rest args)

Element-wise multiplication. Accepts both vectors and scalars.

Example:

 (gamekit:mult 2 (gamekit:vec2 1 1) 0.5)

function add (arg0 &rest args)

Element-wise addition. Accepts both vectors and scalars.

Example:

 (gamekit:add 1 (gamekit:vec2 1 1) -1)

function subt (arg0 &rest args)

Element-wise subtraction. Accepts both vectors and scalars.

Example:

 (gamekit:subt 1 (gamekit:vec2 1 1) (gamekit:vec2 -1 -1))

function div (arg0 &rest args)

Element-wise division. Accepts both vectors and scalars.

Example:

 (gamekit:div (gamekit:vec2 1 1) 2 (gamekit:vec2 0.5 0.5))

function x (vec)

Reads first element of a vector.

Example:

 (gamekit:x (gamekit:vec2 1 1))

function (setf x) (value vec)

Stores first element of a vector.

Example:

 (setf (gamekit:x (gamekit:vec2 1 1)) 0)

function y (vec)

Reads second element of a vector.

Example:

 (gamekit:y (gamekit:vec2 1 1))

function (setf y) (value vec)

Stores second element of a vector.

Example:

 (setf (gamekit:y (gamekit:vec2 1 1)) 0)

function z (vec)

Reads third element of a vector.

Example:

 (gamekit:z (gamekit:vec4 1 1 2 3))

function (setf z) (value vec)

Stores third element of a vector.

Example:

 (setf (gamekit:z (gamekit:vec4 1 1 2 3)) 0)

function w (vec)

Reads fourth element of a vector.

Example:

 (gamekit:w (gamekit:vec4 1 1 2 3))

function (setf w) (value vec)

Stores fourth element of a vector.

Example:

 (setf (gamekit:w (gamekit:vec4 1 1 2 3)) 0)

function register-resource-package (package-name path)

Associates resource package with filesystem path. For proper resource handling it is recommended to put it as a top-level form, so resources could be located at load-time.

First argument, a package name, must be a valid Common Lisp package name that could be used to locate package via #’find-package. Second argument is a filesystem path to a directory where resources can be found.

Example:

 (gamekit:register-resource-package :example-package
                                    "/home/gamdev/example-game/assets/")

macro define-image (name path &key use-nearest-interpolation)

Registers image resource by name that can be used by #'draw-image later. Second argument is a valid path to the resource. Only .png images are supported at this moment.

Name must be a symbol. Package of that symbol and its associated path (via #'register-resource-package) will be used to locate the resource, if relative path is given as an argument to this macro.

Example:

 (gamekit:define-image example-package::logo "images/logo.png")

macro define-sound (name path)

Registers sound resource by name that can be used by #'play-sound later. Second argument is a valid path to the resource. Formats supported: .wav, .ogg (Vorbis), .flac, .aiff.

Name must be a symbol. Package of that symbol and its associated path (via #'register-resource-package) will be used to locate the resource, if relative path is given as an argument to this macro.

Example:

 (gamekit:define-sound example-package::blop "sounds/blop.ogg")

macro define-font (name path)

Registers font resource by name that can be passed to #'make-font later. Second argument is a valid path to the resource. Only .ttf format is supported at this moment.

Name must be a symbol. Package of that symbol and its associated path (via #'register-resource-package) will be used to locate the resource, if relative path is given as an argument to this macro.

Example:

 (gamekit:define-font example-package::noto-sans "fonts/NotoSans-Regular.ttf")

macro define-text (name path &key encoding)

Registers text resource by name that can be retrieved with #'get-text later. Second argument is a valid path to the resource. You can specify encoding via :encoding keywrod argument. :utf-8 is used by default.

Name must be a symbol. Package of that symbol and its associated path (via #'register-resource-package) will be used to locate the resource, if relative path is given as an argument to this macro.

Example:

 (gamekit:define-text example-package::example-text "dialog.txt" :encoding :utf-8)

macro define-binary (name path)

Registers binary resource by name that can be retrieved with #'get-binary later. Second argument is a valid path to the resource.

Name must be a symbol. Package of that symbol and its associated path (via #'register-resource-package) will be used to locate the resource, if relative path is given as an argument to this macro.

Example:

 (gamekit:define-binary example-package::example-blob "blob.data")

function make-font (font-id size)

Makes a font instance that can be later passed to #'draw-text to customize text looks. font-id must be a valid resource name previously registered with define-font. Second argument is a font size in pixels.

Example:

 (gamekit:make-font 'example-package::noto-sans 32)

function prepare-resources (&rest resource-names)

Loads and prepares resources for later usage asynchronously. resource-names should be symbols used previously registered with define-* macros.

This function returns immediately. When resources are ready for use #'notice-resources will be called with names that were passed to this function.

gamekit by default will try to load and prepare all registered resources on startup which might take a substantial time, but then you don’t need to call #’prepare-resources yourself. If you prefer load resources on demand and have a faster startup time, pass nil to :prepare-resources option of a defgame macro which will disable startup resource autoloading.

Example:

 (gamekit:prepare-resources 'example-package::noto-sans
                            'example-package::blop
                            'example-package::logo)

function dispose-resources (&rest resource-names)

Disposes prepared resources asynchronously. resource-names should be symbols used previously registered with define-* macros.

This function returns immediately. Attempts to use disposed resources will raise an error. To use resources again you would need to load them with #'prepare-resources.

Example:

 (gamekit:dispose-resources 'example-package::noto-sans
                            'example-package::blop
                            'example-package::logo)

generic notice-resources (game &rest resource-names)

Called when resource names earlier requested with #'prepare-resources which indicates those resources are ready to be used.

Override this generic function to know when resources are ready.

Example:

 (defmethod gamekit:notice-resources ((this example) &rest resource-names)
   (declare (ignore resource-names))
   (gamekit:play-sound 'example-package::blop)
   (show-start-screen))

function get-text (resource-id)

Get text resource (a string) by id. resource-id must be a valid resource id previously registered with 'define-text.

 (gamekit:get-text 'example-package::example-text)

function get-binary (resource-id)

Get binary resource (a byte vector) by id. resource-id must be a valid resource id previously registered with 'define-binary.

 (gamekit:get-binary 'example-package::example-blob)

function draw-line (origin end paint &key (thickness 1.0))

Draws a line starting from coordinates passed as first argument to coordinates in second parameter. Third parameter is a color to draw a line with. :thickness is a scalar floating point value controlling pixel-width of a line.

Example:

 (gamekit:draw-line (gamekit:vec2 8 5) (gamekit:vec2 32 11)
                   (gamekit:vec4 1 0.5 0 1)
                   :thickness 1.5)

function draw-curve (origin end ctrl0 ctrl1 paint &key (thickness 1.0))

Draws a bezier curve from coordinates passed as first argument to coordinates in second parameter with two control points in third and fourth parameters accordingly. Fifth argument is a curve’s color. :thickness is a scalar floating point value controlling pixel-width of a curve.

Example:

 (gamekit:draw-line (gamekit:vec2 8 5) (gamekit:vec2 32 11)
                   (gamekit:vec2 0 5) (gamekit:vec2 32 0)
                   (gamekit:vec4 1 0.5 0 1)
                   :thickness 1.5)

function draw-rect (origin w h &key (fill-paint nil) (stroke-paint nil) (thickness 1.0) (rounding 0.0))

Draws a rectangle with origin passed in first argument, width and height - second and third arguments accordingly. :fill-paint key is a color to fill insides of a rectangle with. If you pass color to :stroke-paint, edges of the rectangle are going to be struck with it. :thickness controls pixel width of struck edges. Use :rounding in pixels to round rectangle corners.

Example:

 (gamekit:draw-rect (gamekit:vec2 0 0) 314 271
                   :fill-paint (gamekit:vec4 1 0.75 0.5 0.5)
                   :stroke-paint (gamekit:vec4 0 0 0 1)
                   :rounding 5.0)

function draw-circle (center radius &key (fill-paint nil) (stroke-paint nil) (thickness 1.0))

Draws a circle with center in first argument and radius in second argument. Provide color with :fill-paint paramater to fill the inner area of the circle with. If :stroke-paint color is provided, circle’s border is going to be struck with it. :thickness controls pixel width of struck border.

Example:

 (gamekit:draw-circle (gamekit:vec2 100 500) 3/4
                     :fill-paint (gamekit:vec4 1 1 1 1)
                     :stroke-paint (gamekit:vec4 0 0 0 1)
                     :thickness 3)

function draw-ellipse (center x-radius y-radius &key (fill-paint nil) (stroke-paint nil) (thickness 1.0))

Draws an ellipse with center provided in first argument, x and y radii as second and thrid arguments accordingly. Pass a color as :fill-paint paramater to fill the inner area of the ellipse with. If :stroke-paint color is provided, ellipse’s border will be struck with it. :thickness controls pixel width of struck border.

Example:

 (gamekit:draw-ellipse (gamekit:vec2 128 128) 16 32
                      :fill-paint (gamekit:vec4 0 0 0 1)
                      :stroke-paint (gamekit:vec4 1 1 1 1)
                      :thickness 1.1)

function draw-arc (center radius a0 a1 &key (fill-paint nil) (stroke-paint nil) (thickness 1.0))

Draws an arc from a0 to a1 angles (in radians) with center passed in first argument and radius in second. If provided, color in :fill-paint will be used to fill the area under an arc confined between a circle’s curve and a line connecting angle points. :fill-paint and :stroke-paint colors are, if provided, used to fill insides and stroke arc’s border correspondingly.

Example:

 (gamekit:draw-arc (gamekit:vec2 256 256) 128
                  (/ pi 4) (* (/ pi 2) 1.5)
                  :fill-paint (gamekit:vec4 0.25 0.5 0.75 1)
                  :stroke-paint (gamekit:vec4 0.75 0.5 0.25 1)
                  :thickness 2.0)

function draw-polygon (vertices &key (fill-paint nil) (stroke-paint nil) (thickness 1.0))

Draws a polygon connecting list of vertices provided in first argument. :fill-paint is a color to fill insides of a polygon and :stroke-paint color is used to stroke polygon edges. :thickness controls pixel-width of a stroke.

Example:

 (gamekit:draw-polygon (list (gamekit:vec2 10 10) (gamekit:vec2 20 20)
                            (gamekit:vec2 30 20) (gamekit:vec2 20 10))
                      :fill-paint (gamekit:vec4 0.25 0.5 0.75 1)
                      :stroke-paint (gamekit:vec4 0.75 0.5 0.25 1)
                      :thickness 3.0)

function draw-polyline (points paint &key (thickness 1.0))

Draws a polyline connecting list of vertices provided in first argument. Second argument is a color to stroke a line with. :thickness controls pixel width of a line.

Example:

 (gamekit:draw-polyline (list (gamekit:vec2 10 10) (gamekit:vec2 20 20)
                             (gamekit:vec2 30 20) (gamekit:vec2 20 10))
                       (gamekit:vec4 0.75 0.5 0.25 1)
                       :thickness 3.0)

function draw-image (position image-id &key origin width height)

Draws an image at coordinates specified in first argument. Second argument is image-id used in #'define-image earlier. Optional :origin key is a point within image to start drawing from, if you want to render only a part of image. :width and :height keys tell width and height of a subimage to draw. They are optional and could be skipped to draw a subimage with full height and width available.

Example:

 (gamekit:draw-image (gamekit:vec2 314 271) 'example-package::logo
                    :origin (gamekit:vec2 0 0)
                    :width 320
                    :height 240)

function draw-text (string origin &key (fill-color *black*) (font *font*))

Draws text on the canvas starting at coordinates passed as second argument. Use :fill-color key parameter to change text’s color. To change a font, pass object created with #'make-font via :font parameter.

Example:

 (gamekit:draw-text "Hello, Gamekit!" (gamekit:vec2 11 23)
                   :fill-color (gamekit:vec4 0 0 0 1)
                   :font (gamekit:make-font 'example-package::noto-sans 32))

function translate-canvas (x y)

Moves drawing origin to the specified position making the latter a new origin. All following draw operations will be affected by this change unless wrapped with with-pushed-canvas macro.

Example:

 (gamekit:translate-canvas 100 500)

function rotate-canvas (angle)

Rotates current canvas for specified number of radians. All following drawing operations will be affected by this change unless wrapped with with-pushed-canvas macro.

Example:

 (gamekit:rotate-canvas (/ pi 2))

function scale-canvas (x y)

Scales current canvas by x and y axes accordingly. All following drawing operations will be affected by this change unless wrapped with with-pushed-canvas macro.

Example:

 (gamekit:scale-canvas 0.5 1.5)

macro with-pushed-canvas (nil &body body)

Saves current canvas transformations (translations, rotations, scales) before entering its body and restores previous transformations upon exit from the body. All transformation operations within this macro don’t affect outer canvas transformations outside of a body of this macro.

Example:

 (gamekit:translate-canvas 400 300)
 (gamekit:with-pushed-canvas ()
   (gamekit:rotate-canvas (/ pi 4)))

function image-width (image-id)

Returns width of an image by its id (defined with #'define-image).

Can only be called when gamekit instance is active (started via #'start).

Example:

 (gamekit:image-width 'example-package::logo)

function image-height (image-id)

Returns height of an image by its id (defined with #'define-image).

Can only be called when gamekit instance is active (started via #'start).

Example:

 (gamekit:image-height 'example-package::logo)

function calc-text-bounds (text &optional (font *font*))

Calculates text bounds with the font provided or the default one otherwise and returns several values: origin as vec2, width, height and calculated advance

Example:

(gamekit:calc-text-bounds "hello there")

function play-sound (sound-id &key looped-p)

Plays a sound defined earlier with define-sound. Pass t to :looped-p key to play sound in a loop.

Example:

 (gamekit:play-sound 'example-package::blop
                     :looped-p t)

function stop-sound (sound-id)

Stops a playing sound by provided sound id.

Example:

 (gamekit:stop-sound 'example-package::blop)

function bind-button (key state action)

Binds action to specified key state. When key state changes to the one specified, action callback is invoked with no arguments. #'bind-button function should be called when there’s active game exists started earlier with #'start. state can be either :pressed, :released or :repeating.

Actions are not stacked together and would be overwritten for the same key and state.

Can only be called when gamekit instance is active (started).

Supported keys:

  :space :apostrophe :comma :minus :period :slash
  :0 :1 :2 :3 :4 :5 :6 :7 :8 :9
  :semicolon :equal
  :a :b :c :d :e :f :g :h :i :j :k :l :m
  :n :o :p :q :r :s :t :u :v :w :x :y :z
  :left-bracket :backslash :right-bracket
  :grave-accent :world-1 :world-2
  :escape :enter :tab :backspace :insert :delete
  :right :left :down :up
  :page-up :page-down :home :end
  :caps-lock :scroll-lock :num-lock :print-screen :pause
  :f1 :f2 :f3 :f4 :f5 :f6 :f7 :f8 :f9 :f10 :f11 :f12
  :f13 :f14 :f15 :f16 :f17 :f18 :f19 :f20 :f21 :f22 :f23 :f24 :f25
  :keypad-0 :keypad-1 :keypad-2 :keypad-3 :keypad-4
  :keypad-5 :keypad-6 :keypad-7 :keypad-8 :keypad-9
  :keypad-decimal :keypad-divide :keypad-multiply
  :keypad-subtract :keypad-add :keypad-enter :keypad-equal
  :left-shift :left-control :left-alt :left-super
  :right-shift :right-control :right-alt :right-super
  :menu

  :mouse-left :mouse-right :mouse-middle

Example

(gamekit:bind-button :enter :pressed
                     (lambda ()
                       (start-game-for *player*)))

function bind-cursor (action)

Binds action callback to a cursor movement event. Everytime user moves a cursor callback will be called with x and y of cursor coordinates within the same coordinate system canvas is defined in: bottom left corner as (0,0) origin and y-axis pointing upwards.

Actions doesn’t stack together and would be overwritten each time #'bind-cursor is called.

Can only be called when gamekit instance is active (started).

Example:

 (gamekit:bind-cursor (lambda (x y)
                        (shoot-to x y)))

function bind-any-gamepad (action)

Binds action to a gamepad connection and disconnection events. Once one of those events happen, action is called with two arguments: gamepad - opaque reference to a gamepad that will be supplied as an argument to other gamepad-related actions, and state - which can be either :connected or :disconnected to catch connection and disconnection of a gamepad accordingly.

If there were gamepads already connected before call to #'bind-any-gamepad, action is called for each one of those upon invocation.

Example:

 (gamekit:bind-any-gamepad (lambda (gamepad state)
                             (if (eq :connected state)
                                 (add-player-for-gamepad gamepad)
                                 (pause-game-and-wait-for-player gamepad))))

function bind-gamepad-button (button state action)

Binds action to specified gamepad’s button state. When button state changes to the one specified, action callback is invoked with gamepad opaque reference as an argument. state can be either :pressed or :released.

Actions are not stacked together and would be overwritten for the same button and state.

Can only be called when gamekit instance is active (started via #'start).

Gamekit’s gamepad is a generic xbox controller with the same layout of controls.

Supported buttons:

  :a :b :x :y
  :left-bumper :right-bumper
  :start :back :guide
  :left-thumb :right-thumb

Example

 (gamekit:bind-gamepad-button :start :pressed
                              (lambda (gamepad)
                                (declare (ignore gamepad))
                                (start-game)))

function bind-gamepad-any-button (action)

Binds action to all buttons of gamepads. When any button state of any gamepad changes, action callback is invoked with gamepad opaque reference as a first argument, gamepad’s button as a second and button’s state as a third argument. See #'bind-gamepad-button for available button values and states.

Actions are not stacked together and would be overwritten on each function invocation.

Can only be called when gamekit instance is active (started via #'start).

Example

 (gamekit:bind-gamepad-any-button (lambda (gamepad button state)
                                    (when (and (eq button :start) (eq state :pressed))
                                      (join-party (make-player-for-gamepad gamepad)))))

function bind-gamepad-dpad (state action)

Binds action to gamepad’s dpad. When dpad state changes, action callback is invoked with gamepad opaque reference as a first argument and new dpad state as a second.

Dpad states:

  :up :down :left :right
  :left-up :left-down
  :right-up :right-down
  :centered

Actions are not stacked together and would be overwritten for the same dpad state.

Can only be called when gamekit instance is active (started via #'start).

Example

 (gamekit:bind-gamepad-state :up (lambda (gamepad)
                                   (declare (ignore gamepad))
                                   (jump *player*)))

function bind-gamepad-stick (stick action)

Binds action to gamepad’s left or right stick. When position of the specified stick changes, action callback is invoked with gamepad opaque reference as a first argument, position’s x and y as second and third arguments. x and y values are in [-1;1] range: stick up (0;1), stick down (0;-1), stick left (-1;0) and stick right (1;0).

Sticks: :left and :right.

Actions are not stacked together and would be overwritten for the same stick.

Can only be called when gamekit instance is active (started via #'start).

Example

 (gamekit:bind-gamepad-stick :left (lambda (gamepad x y)
                                     (declare (ignore gamepad))
                                     (move-player *player* x y)))

function bind-gamepad-trigger (trigger action)

Binds action to gamepad’s left or right triggers. When value of the specified trigger changes, action callback is invoked with gamepad opaque reference as a first argument and new trigger value as second. Trigger values are in [0;1] range.

Triggers: :left and :right.

Actions are not stacked together and would be overwritten for the same trigger.

Can only be called when gamekit instance is active (started via #'start).

Example

 (gamekit:bind-gamepad-trigger :right (lambda (gamepad value)
                                        (declare (ignore gamepad))
                                        (setf (shot-power *player*) value)))

function deliver (system-name game-class &key build-directory (zip *zip*) (lisp *lisp*))

Builds an executable, serializes resources and packs required foreign libraries into a .zip archive for distribution. system-name is a name of asdf system of your application and game-class is a game class defined with defgame (the one that could be passed to #'start to start your game). By default, it builds all artifacts into build/ directory relative to system-name system path, but you can pass any other path to :build-directory key argument to put target files into it instead.

This routine uses zip and lisp (‘sbcl’ Steel Bank Common Lisp is the default) to build a distributable package on various platforms. If those executables are not on your system’s PATH, you would need to provide absolute paths to them via :zip and :lisp key arguments accordingly.

You can load this function into an image via :trivial-gamekit/distribution system.

Example:

(ql:quickload :trivial-gamekit/distribution)
(gamekit.distribution:deliver :example-asdf-system 'example-package::example
                              :build-directory "/tmp/example-game/"
                              :zip "/usr/bin/zip"
                              :lisp "/usr/bin/sbcl")