Procedural Animation
I recently made a procedural animation toy. It’s a 2D implementation, which kept things simple, so I could focus on the this unfamiliar domain. You can view this in the browser here.
Constraints
A nice way to generate procedural animations is to think in terms of simple and composable constraints. It’s really hard to invent a solution for a complex mathematically-driven animation all at once, but it’s a lot simpler to try and decompose it into pieces that we can solve independently.
Distance contraint
First, we can think of modelling the simplest creature possible. Something like a snake. We can model several “joints” which represent a part of the snake’s volume. We want each joint to be attached to the next, so we want them to maintain some fixed distance. This can be achieved by the distance formula (an application of the Pythagorean theorem).
Notice how each joint follows its parent in a very natural way. The first joint is following some predefined path and the following joints just have the distance constraint applied.
[source]
Angle consntraint
Next is the angle constraint. This somewhat functions as a mechanism for preserving the perceived volume of the creature. There are definitely better mechanisms for this, but I found this the simplest. This allows the creature to turn on itself without crumpling like paper. It’s easy enough to decide if the angle between the two vectors is too large. We can get that angle by rearranging the terms in our handy dot product:
However, constraining the angle is a bit more complicated because we need to know the “sidedness” of the one segment relative to the other. Fortunately, there’s a something often referred to as the 2D cross product1, wedge product, or determinant. It’s whatever you want to call it, but one interpretation is that it’s the signed area of the parallelogram represented by the two vectors.
For example, the following case of :
I’ve also written up a little interactive version of this below. You can use the right-hand rule to determine whether the sign is positive. Point your fingers at and curl them towards . If your thumb points at you, it’s positive.
Now, we don’t actually care about the area of this parallelogram, but the sign is interesting! It can help us determine the side with the convex angle2. That is, the angle that might be infringing on the limit imposed by the angle constraint. This is resolved by adding or subtracting (based on the sign we obtained above) the predefined angle from the angle of the parent joint.
[source]
FABRIK contraint
The next thing we want is arms/legs. We want something that can reach for a target position. We can use a process known as FABRIK. It stands for “Forwards and Backwards Reaching Inverse Kinematics”. It’s a bit of a mouthful, but we can break it down. If we consider some arm that is reaching for a target, forward kinematics relates to the configuration of the joints. It’s the parameters we set for the angles between joints resulting in some final “target”. Inverse kinematics are the opposite, we provide some target and want to calculate the necessary transformations of the arm needed to reach for the target. Note that the arm may not always actually be able to reach the target. Now, analytical solutions are really hard, but FABRIK exists as a numerical method, or a method which solves the problem through iteration. This is perfect for our case since we don’t need a perfect solution. The last part of the definition to discuss is the “forwards and backwards” part. This can be broken up into four steps:
- Move the last joint to the target.
- Apply the distance constraint to each previous point.
- Move the first joint to its root.
- Apply the distance constraint to each following point.
That’s it! It’s applying the distance constraint forwards and backwards!
In my implementation, I actually embed two distance constraints to achieve this. The only other thing I do is make the FABRIK constraint choose a comfortable resting position, or target, for the legs. As the creature moves, we assume its center of mass moves away from the current target, so after some predefined threshold, it would want to move its leg. This is fully configurable and you can create some pretty interesting setups.
There’s definitely more to be done here. For example, you can restrict the angles of the legs depending on the underlying joint you are trying to model, but I’ll leave it at that.
[source]
Rendering
The Macroquad library does most of the heavy lifting for this project and I
didn’t want that to eclipse the discussion of simplifying something as seemingly
complex as procedural animations. For my implementation, I mostly used
primitives provided by the library like draw_circle
and draw_line
. The only
thing that was somewhat complicated was generating a mesh for the inner fill of
the creature. The way I calculated the points for the mesh was to take the angle
of each joint and add/subtract for the left and right points.
Generating a mesh wasn’t fully necessary, and you can get away with just drawing
a bunch of circles or rectangles.