Building a Flappy Bird in Clojure Quil #1

I recently got my hands on the book “Nature of Code – Simulating Natural Phenomena with Processing" and decided to dive into it using Clojure’s Quil library. I’ve completed the first chapter, and now, as a review, I’m planning to create a Flappy Bird game. Given the concepts from “Nature of Code," this Flappy Bird implementation might involve some additional processing.

Since I’m still learning Clojure, I welcome any suggestions or feedback if you think there’s a better way to approach this.

What is Quil?

Quil is an environment for Processing written in Clojure, a functional Lisp dialect that runs on the JVM (Java Virtual Machine). You can even run it in your web browser through its official website.

Setting Up the Project

First, let’s create a project using Leiningen.

lein new quil flappy-bird

Sample code is created in src/flappy_bird/core.clj with comments.

├─  .gitignore
├─  project.clj
└─ /src
    └─ /flappy_bird
            └ core.clj

The following is an automatically generated sample code.

(ns flappy-bird.core
  (:require [quil.core :as q]
            [quil.middleware :as m]))

(defn setup []
  (q/frame-rate 30)
  (q/color-mode :hsb)
 {:color 0   
   :angle 0})                          ;;Returns initial color and rotation angle

(defn update-state [state]
  {:color (mod (+ (:color state) 0.7) 255)
   :angle (+ (:angle state) 0.1)})     ;;Returns a new value 
                                       ;;based on the value received in the argument

(defn draw-state [state]
  (q/background 240)
  (q/fill (:color state) 255 255)     ;;Drawing based on the value received as an argument
  (let [angle (:angle state)
        x (* 150 (q/cos angle))
        y (* 150 (q/sin angle))]
    (q/with-translation [(/ (q/width) 2)
                         (/ (q/height) 2)]
      (q/ellipse x y 100 100))))

(q/defsketch flappy-bird               ;;Basic settings such as screen size
  :title "You spin my circle right round"
  :size [500 500]
  :setup setup
  :update update-state
  :draw draw-state
  :features [:keep-on-top]
 :middleware [m/fun-mode])

When you run it, you’ll see a rotating circle changing colors.

Understanding the Overall Workflow

In the Case of Processing

In Processing, which Quil is based on, there are two essential functions: setup and draw.

  • In setup, you set up things like the screen size, and it runs once at the beginning.
  • In draw, you handle continuous tasks like changing coordinates and drawing shapes.

Data such as coordinates are typically stored in global variables.

In the Case of Quil

Quil introduces three functions: setup, update-state, and draw-state.

  • In setup, you perform initialization and return data to be passed to the draw-state function.
  • In update-state, you receive data as an argument, modify it, and return it.
  • In draw-state, you use the received data to draw shapes.

Quil follows a functional programming paradigm, avoiding global variables. Instead, it passes data through function arguments, updates it, and returns it in a structured way.

The flow goes like this: setup (initial data)draw-state (drawing)update-state (modification)draw-state (drawing)update … Each function has its specific role, and even if you modify data in draw-state, it won’t automatically transfer to update-state. (However, if you draw inside update-state, it will appear on the screen.)

show the player character

First, remove any unnecessary code and show the player character (for now, represented as a square).

(ns flappy-bird.core
  (:require [quil.core :as q]
            [quil.middleware :as m]))

(defn setup []
  (q/frame-rate 30)
  {:bird [100 100]})     ;;Holds the xy-coordinates of the ship as a vector

(defn update-state [status]
  status)                  ;;Receive xy coordinates and return them as is

(defn draw-state [{:keys [bird]}]
  (q/background 240) 
  (let [[x y] bird] 
    (q/rect x y 30 30)))     ;;Draw a 30 30 rectangle at the received XY coordinates

(q/defsketch flappy-bird
  :title "flappy bird"
  :size [400 600]             ;;Set screen to portrait
  :setup setup
  :update update-state
  :draw draw-state
  :features [:keep-on-top]
  :middleware [m/fun-mode])

You should see a square displayed on the screen.

Wrapping Up

In the next post, we’ll work on enabling the player character to move. Stay tuned for more!