Skip to content

02 Sprites

Kimbsy edited this page May 26, 2023 · 5 revisions

For a quick demo, check out the basic-sprite example game.

Adding Sprites

Adding sprites to a scene is straightforward, the scene's configuration map can specify a :sprites vector.

(ns quip.foo
  (:require [quip.core :as qp]
            [quip.utils :as qpu]
            [quip.sprite :as qpsprite]))

(defn update-level-01 [state] state)

(defn draw-level-01 [state] (qpu/background [0 43 54]))

(defn scenes []
  {:level-01 {:update-fn update-level-01
              :draw-fn draw-level-01
              ;; adding a single image-sprite
              :sprites [(qpsprite/image-sprite :player
                                               [100 100]
                                               24
                                               24
                                               "player.png")]}})

(def g (qp/game {:init-scenes-fn scenes
                 :current-scene :level-01}))

(qp/run g)

Alternatively sprites can be added to or removed from a scene by modifying the :sprites collection for that scene in any function that returns a new state.

;; this could be in a scene update functions, an input handler, a collision handler etc.
(update-in state
           [:scenes :level-01 :sprites]
           conj
           (qpsprite/image-sprite :enemy
                                  [50 50]
                                  10
                                  10
                                  "enemy.png"))

Updating and Drawing Sprites

Sprites have their own update and draw functions, you can invoke them in the scene update and draw functions:

(defn update-level-01
  [state]
  ;; uses the value of `:current-scene` in state to update that scene's sprites
  (qpsprite/update-scene-sprites state))

(defn draw-level-01
  [state]
  (qpu/background [0 43 54])
  ;; uses the value of `:current-scene` in state to draw that scene's sprites
  (qpsprite/draw-scene-sprites state))

Available Sprites

There are several ready to use sprites provided by quip. However creating your own sprite is often helpful, especially if you want it to include advanced behaviour.

Custom sprites should be represented by a map, containing at a minimum a :sprite-group, an :update-fn and a :draw-fn.

Image Sprite

An image sprite needs a group keyword (used for collision detection, and z-height layers), a position vector, a width, a height, and a filepath for it's image (relative to the resources directory).

Optionally you can also specify:

(qpsprite/image-sprite
 :player
 [100 100]
 24
 24
 "player.png"
 :rotation 90                ;; rotation in degrees
 :vel [-1 1]                 ;; velocity vector
 :update-fn (fn [s] ... )    ;; custom update function
 :draw-fn (fn [s] ... )      ;; custom draw function
 :points [[0 0] ... ]        ;; list of points describing bounding polygon
 :bounds-fn (fn [s] ... )    ;; custom bounds function
 )

The default update function modifies the sprite position based on its velocity, the default draw function draws the sprite image to the scene. The default bounding function (used for collision detection) returns a rectangle based on the sprites width and height unless :points are specified in which case it will return the polygon they describe. Specifying :bounds-fn will override the default bounding function and should return a set of points describing a polygon.

Animated Sprite

The animated-sprite function is slightly more complex as it takes a spritesheet image (rather than a single image) and an optional :animations configuration map describing the animations found in the spritesheet (each animation should be a row in the spritesheet). The animation configuration map should detail each animation, specifying :frames (the number of frames in this animation), :y-offset (zero-indexed position from the top of the sheet, with the unit being the sprite's height) and :frame-delay (the number of game frames to wait before moving to the next animation frame). Finally, the sprite should also specify the :current-animation.

(qpsprite/animated-sprite
 :player
 [100 100]
 24
 24
 "player-spritesheet.png"
 :animations {:idle {:frames      4
                     :y-offset    0
                     :frame-delay 15}
              :run  {:frames      4
                     :y-offset    1
                     :frame-delay 8}
              :jump {:frames      7
                     :y-offset    2
                     :frame-delay 8}}
 :current-animation :idle)

Text Sprite

The text-sprite function requires the text to display and the position. It takes several optional arguments, such as :font, :size, :color and :offsets.

(qpsprite/text-sprite
 "Super Cool Game"
 [200 150]
 :font "Ubuntu Mono Italic"  ;; font must exist
 :size 32                    ;; font height in pixels
 :color [255 0 0]            ;; [R G B] colour
 :offsets [:left :center])   ;; alignment offsets

The :offsets field is a vector of one or two keywords. If a single keyword it will apply to both x and y (e.g. [:center]), if two keywords they will apply to the x and y axis respectively (e.g. [:left :top]). This field defaults to [:center].

The font being used for a text-sprite must exist on the end user's system. To ensure this you can add the <font>.tff file to your resources directory and add the font to the game state during setup using Quil's create-font function.

(defn setup
  []
  {:font (quil.core/create-font "UbuntuMono-Italic.ttf"
                                quip.utils/default-text-size)})