Wishful Coding

Didn't you ever wish your computer understood you?

On the proccess of writing a game engine in Clojure

Or at least a set of engine-framework-commons-utils.

There have been a few games and simulations in Clojure before, a few notable ones: 

The grandmother of all Clojure games is probably ants.clj by Rich himself, mostly a demonstration of concurrency in Clojure.

Ironclad is a game that deserves to be noted for its completeness; It's more than just an example.

And finally, this unnamed(and likely unfinished as well) RPG, not so much for the game as for the blog post that describes the process of writing it.

Just like Brian, I went through the process of agents and OpenGL, but let us begin with the begin.

Design 0: I want to make a game.

It's as simple as that. A nice and simple 2D game in Clojure. I started googling for game engines in Java and Clojure. The only ones I found that I liked and that did not look like they came from a past century where Slick and PulpCore.

Problem is, I did not even manage to compile Slick, and PulpCore is for applets only. Besides, they both use very OOP models that I suppose would feel awkward in Clojure.

So after reading a few opinions about Swing vs OpenGL, I decided to write my own game in Clojure based on Swing.

Design 1: Agents, agents, agents...

My initial design philosophy was very simple. It should be

  • multithreaded
  • functional
  • easy

So I set up an agent with a vector, made a Swing frame and overwrote its paintComponent to draw the objects in the vector to the frame.

Then I added a watcher to the agent to mark all changed objects dirty on the Swing frame, so they would be redrawn.

Design 1.1: Changed?

Okay, which object changed? Hard to tell. I turned the vector into a map, a set, and a map again, so that I could use clojure.set/difference to tell what happened.

Design 1.2: I can walk through walls!

Wait, we need collision detection. Simple, right?

Doing this the naive way would mean a quadratical increase in comparisons. I was well on my way implementing a quad tree when it hit me: For any non-huge number of objects, it's not worth the trouble.

I changed whatever I had at that moment into a sorted variant, so that I only had to check objects that where at least close in one dimension.

Design 1.999: This is getting out of hand!

At this moment I had changed every single line of code around 3 times. I had atoms, refs and agents, containing vectors, sets, maps, calling multimethods and protocols, I was knee-deep in thread pools and transactions when I remembered my second 2 goals.

This beautiful, concurrent, asynchronous design that I had was just not functional and easy at all. It had mutable state and transactions everywhere.

Design 2: I'm back in the loop.

After a few more sidetracks, I switched to active rendering and reduced my game loop to something like this pseudo-code.

(ns game)

(def world {:foo #<fooinstance> :bar #<barinstance>})

(def window (JPanel. 800 800))

(doseq [frame (iterate #(map act %) world)
        obj frame]
  (draw obj window))

Design 2.1: Reality.

After all this theorizing and experimenting, I finally got my stuff stable enough to actually write a little game; pong.

Let me say this about it: Few lines of the original pong game and my engine where left unchanged, but they where more subtle changes than before.

Next, I wrote a simple platform game. Again, changes where made.

Current design: Are we there yet?

I feel like what I have now is nearly ready to share with the world. I do believe that an event, or message processing based system could work, but what I have now does so too.

Begame currently provides:

  • A game loop
  • Time based animation
  • Collision detection
  • All the Swing stuff you don't care about
  • Protocols
    • actor; objects that do stuff
    • visible; objects that draw themselves
    • solid; objects involved in collision detection

Get it.

See the examples, and check out the source from Github.

Pepijn de Vos