Getting Dirty With State and Matter.js
Written by Bunkers on March 14, 2017
This is more of a brain dump of my thoughts on the most appropriate way of integrating Matter.js with ClojureScript. It's also an appeal for help. My hope is that it will help some people to get started and others will want to help me but describing the approach I should take. If nothing else it will serve as some notes as I try to develop further demos with Matter.js and ClojureScript.
In Clojure everything defaults to being immutable. When you want to play with the messy stuff of state it gives you some mechanisms to help you group all that together and touch it as little as possible. This is the principle that has made React so popular and I would argue, part of the reason that functional programming is such a buzzword (phrase? Can you have a buzz phrase?) at the moment.
While I was creating my ClojureScript version of the Coding Train's Matter.js tutorial, I needed to figure out what to do with my state. In an object oriented program this state gets spread out through the application as properties on objects and methods change the values. Daniel does this in the video by setting up Box
objects. The objects themselves contain the properties they need, and a show
method deals with rendering it.
In my version I needed to work out a way of combining the functional world of Quil, where the state passes through the function calls, with the not so functional mutable world of Matter.js. I could have just used the Clojure wrapper for Box2D to produce the same result, but where's the fun in that? Besides, I'm trying to learn some stuff, and although I have a tendency to dive in too deep, I wanted to develop something close to the tutorial.
Looking at the code for CljBox2D, the approach I took was quite similar. The physics simulation consists of a World
, a property of the Engine
object in Matter.js's case. The World
has Bodies
and we advance the simulation in time using a step!
or update
function.
Quil gets around the mutating of state by having copies of the state data structure passed through the chain of method calls. The problem is that we need to keep track of the objects in Matter.js and they will be changing as we make calls to update the simulation.
Part of the problem here is that I want to control the rendering of the bodies in the physics engine. How I've worked with these engines before with games was to have a simulation where characters were simple boxes in the physics engine, but displayed as the appropriate frame of animation, say a walking cycle. Without this requirement I could take the approach of using the World
object to contain all state and render the bodies by simply looping through them. Matter also has support for displaying sprites, but like the rendering itself I wanted to take control of this aspect.
My solution is to store a reference to the Body
object along with any necessary properties I need in a vector in the overall state data structure. This state structure also has a reference to the Engine
object along with some other pieces of global information. What I don't like is when we update the World
state, all the references to the Body
objects update too. This feels a bit dirty. All the state is in one blob, but I don't like the idea of updating something and having lots of side effects.
The solutions I can think of to overcome this are:
- Store the body's ID instead of the object. The rendering would then involve cycling through the bodies and looking up any appropriate additional data in our state data structure.
- Extend Body so that the objects store any additional information required. Reading the documentation on plugins, it seems like that would be the way to extend Matter like this.
- Stop fighting Matter and embrace its support for rendering and sprites. Handing all this control off to Matter doesn't appeal to me. It's relinquishing too much control!
For now storing a reference to the Engine object, and the bodies I'm interested in, is ok - it works! But I think there's a better wrapper that I could make for this. Taking some of the lessons learnt from React wrappers like Om, it should be possible to make a stack of World states, and therefore allow time to be rewound, or have replays built in.
I think the key to this would be to convert the World to an immutable data structure. The current state would then be an atom (a mutable reference in Clojure) referencing the appropriate World state. How to do this efficiently is an issue for me currently. To begin with I'm going to create a better wrapper along the lines of CljBox2D. At least this way my code will be less littered with JS interop.
Are you reading this and screaming about how dumb I'm being? Fancy letting me know what I'm missing and the simple idiomatic way of wrapping a library like Matter? Please get that warm smug feeling, you can only get by correcting someone with your superior knowledge, and get in touch!