Brute – Entity Component System Library for Clojure

Warning: If you are new to Entity Component Systems, I would highly recommend Adam Martin’s blog series on them, he goes into great detail about what problem they solve, and what is required to implement them.  I’m not going to discuss what Entity Component Systems are in this blog post, so you may want to read his series first.

Doing some more fun time with game development, I wanted to use a Entity Component System Library for my my next project. Since I’m quite enamoured with Clojure at the moment, I went looking to see what libraries currently existed to facilitate this.

I found simplecs, which is quite nice, but I wanted something that was far more lightweight, and used simple Clojure building blocks, such as defrecord and functions to build your Entities, Components and Systems.  To that end, I wrote a library called brute, which (I think) does exactly that.

I wrote a Pong Clone example application to test out Brute, and I feel that it worked out quite well for my objectives.  Below are a few highlights from developing my example application with Brute that should hopefully give you a decent overview of the library.

As we said before, we can use defrecords to specify the Components of the system. For example, the components for the Ball:

We have a:

  • Rectangle, which defines the position of the Ball, the dimensions of the rectangle to be rendered on screen, and its colour.
  • A Ball component as a marker to delineate an Entity is a Ball.
  • Velocity to determine what direction and speed the Ball is currently travelling.

As you can see, there is nothing special, we have just used regular old defrecord. Brute, by default will use the the Component instance’s class as the Component type, but this can be extended and/or modified (although we don’t do that here).

Therefore, to create the ball in the game, we have the following code:

This creates a Ball in the centre of the playing field, with a white rectangle ready for rendering, and a Velocity of 300 pointing in a random direction.

As you can see here, creating the entity (which is just a UUID), is a simple call to create-entity!. From there we can add components to the Entity, such as an instance of the Ball defrecord, by calling add-component! passing in the entity and the relevant instance. Since we are using the defrecord classes as our Component types, we can use those classes to retrieve Entities from Brute.

For example, to retrieve all Entities that have Rectangle Components attached to them, it is simply a matter of using get-all-entities-with-component

From there, we can use get-component to return the actual Component instance, and any data it may hold, and can perform actions accordingly.

Systems become far more simple in Brute than they would when building an Entity System architecture on top of an Object Oriented language.

Systems in Brute are simply a function, that takes a delta argument, for the number of milliseconds that have occurred since the last processing of a game tick. This leaves the onus up to the game author to structure Systems how they like around this core concept, while still giving a simple and clean entry point into getting this done.

Brute maintains a sequence of System functions in a registry, which is very simple to add to through the appropriately named add-system-fn! function.

Here is my System function for keeping score:

Here we add it to the registry:

Finally, to call all registered system functions are fired, by using the function process-one-game-tick, which calls all registered System functions in the order they were registered – and in theory, your game should run!

For more details on Brute, check out the full API documentation, as well as the Pong clone sample game that I wrote with the great play-clj framework (that sits on top if libGDX).

As always, feedback is appreciated.

Leave a Comment

Comments

  • Martin Janiczek | April 16, 2014

    That is awesome!
    I have half-baked component-entity-system Clojure library on my localhost, yet to be determined if I’ll abandon it or finish it … Maybe after I try your approach I can move on somehow 🙂
    https://gist.github.com/Janiczek/10823557

    I feel your approach is better. In my approach I have too many macros, too much indirection, when simple functions would do… 🙂

  • Mark Mandel | April 16, 2014

    @Martin,

    Thanks for your feedback! Much appreciated.

    I also got some great feedback on Github (https://github.com/markmandel/brute/issues/1), and after discussion am currently working on rewriting out all the global refs, so you are simply passing around an immutable collection. Then you can manage how you want to store your state yourself.

    This will mean that having multiple ES systems within the one game, and just be far more flexible, as you will have access to all the data if you need it.

    I expect I’ll have the updated version up in a day or so.

  • Frankie | May 9, 2014

    Great job Mark! I was just looking for something similar to simplify the development of ECS games/applications. Have you considered integrating brute with the newly released datascript? Brute could be a lightweight layer on top of datascript for 99% of common usage but if the user needs more sophisticated queries it can bypass it. Also, having a centralised point of change makes Om integration easier, right?

  • Mark Mandel | May 12, 2014

    @Frankie,

    Interesting idea!

    Unfortunately I’ve been aiming this library directly primarily at JVM based games (desktop/mobile), and wasn’t looking to tie it directly to ClojureScript.

    Right now it it is tied to the JVM because of some interop code (mainly creating UUIDs), but it would be great to have some pull requests to mean you could use it in CLJS as well.

  • Wei Hsu | May 18, 2014

    @Mark, thanks for making this, I’m using it in my Clojurescript game. All I had to for CLJS compatibility was add a function that generates UUIDs.

  • Mark Mandel | May 19, 2014

    @Wei, thanks for using it!

    I’d love to have a pull request that let’s it work on both Clojure and ClojureScript, if you are so inclined. I’m primarily working on the JVM, but happy to see it opened up to other platforms.

  • codernoobz | August 4, 2014

    Hi.

    Thanks for the example, its helping me to move forward with my game dev stuff.

    In your Brute Pong example how can I set the position if I swap a Rectangle for a Polygon in the ShapeRenderer part of the renderer namespace? It seems difficult with the ShapeRenderer… if its not possible that way how can I just draw a ‘shape’ there instead ?

    Thanks.