How to Make a 2D Physics Engine (Overview)

What is a Physics Engine?

It’s simply a framework that allows you to simulate the effects of physics on objects. The physics engine on its own merely performs computations on vectors, so it is typically accompanied by a graphics or render engine, with which it runs in tandem. The level of realism achieved by the physics engine varies greatly depending on the requirements, with real-time game physics engines sacrificing pinpoint accuracy to preserve FPS and realistic fluid simulations for research doing precisely the opposite. Rendered physics in movies, for instance, lies somewhere in between.

A major component of physics engines is the so-calles “rigid bodies”, which are non-deformable objects with a definite shape. Computationally this is nice because at each instant, the body can be specified by a precomputed fixed shape and a single position and angle, which is then passed onto the renderer to display the object on screen. Although we live in a world with continuous time, physics engines discretise it, but since the laws of motion are formulated using calculus, we must employ an “integrator” to yield physically realistic results using a discrete approximation.

Other aspects that a physics engine has to tackle is collisions between shapes - detecting and resolving them - as well as constraints such as hinges and ball-and-socket joints. On the surface these appear to be completely different problems, but a constraint-based physics engine actually implements these in exactly same manner, and I will have a great deal to say about this in later blog posts.

How did I make it?

I was initially drawn to making game physics engines when I was in around 8th grade, before I even knew much physics. But I had just learned integral calculus, so it seemed like a reasonably “extended” project that I could attempt with my newfound skills. My language of choice was Java (with Swing), and I ended up making a decent, simple physics engine - you could add circles of different sizes, masses and restitutions and make them bounce off each other and walls.

A couple of years later I decided to revisit this and completely revamp it, using the more powerful constraint-based approach, and allowing for more complex shapes, rotations, friction, and constraints.

Instead of continuing with Java and its annoying absence of operator overloading, I opted for Python, which provides the incredibly powerful NumPy library for linear algebra in computational science. Bear in mind that I was just cooling off from a couple months’ obsession with machine learning, so I was adept at computational linear algebra even before starting.

NumPy for the physics part was inevitable, but I had a few options for the animation toolset. Eventually I settled with the Pyglet package, which was reasonably concise since it used OpenGL-like syntax.

I’ll give an overview of the various components of my physics engine here, and you can find an in-depth explanation in the links provided.

  • The core rigidbody is very straightforward - the basic features are attributes for mass, position, velocity and acceleration vectors (and their angular counterparts). You provide a callback to update these values at a timestep, using the accumulated force, velocity, etc. to update the velocity, position, etc. The final element is a function to convert from the rigidbody frame to the global frame and vice versa.

  • The collider (I used Unity’s terminology) encodes the actual shape of the object in question, simply specified by a list of coordinates in global frame, from which the centre of mass is computed by the shoelace formula. It also stores restitution and friction, since multiple colliders can be assigned to a single rigidbody to create a composite shape. The main method here is to return the axis-aligned bounding box, a non-rotated bounding rectangle which speeds up detection of polygon intersection by providing a quick way to discard true negatives.

  • The GameObject is the container of one or more colliders, and a rigidbody for centre of mass motion. It provides a static function for determining whether two GameObjects are intersecting (a position update at a timestep may push one body slightly into another), and returns one or more “contact manifolds” containing details of such an intersection, if present. Collision detection for arbitrary polygons is rather difficult - here I restrict to convex polygons only, and I employ the SAT algorithm, which is quite tricky to get right.

  • The Constraint class is an abstract parent class of the various types of constraints - one is of course the contact constraint, and others include the fixed distance constraint and the prismatic joint. These are defined to constrain two GameObjects, and can cache and return a “Jacobian” which characterises the violation of the constraint at the current time step

  • The physics engine itself controls the timestepping and intervals, carefully inserting extra frames to regulate smoothness, and passing the $\Delta t$ parameter which controls the integrator mentioned above. At each time step, it also solves the constraints and applies external forces to update the bodies.

  • The graphics engine runs at its own pace, rendering the colliders of all GameObjects in the scene

Conceptually, these elements are quite simply, but implementing an engine that is both accurate and fast is quite an optimisation challenge, so there are several complicated features that can go into making such an engine.