Procedural Claymation

Emulating the jittery claymation motion with procedural animation in Unity Engine

This animation experiment was created as part of my bachelor's thesis, where my team and I chose to replicate the look of claymation within 3D physics-based environments. The following WebGl demo shows a comparison between 2 procedurally animated apples: left one falls with normal game physics, right one falls with added claymation-like jitter.

In this demo, you can find serveral settings which affects the procedural claymation:

  • Frames per second: the update frequency for the claymation.

  • Stutter position/rotation: the amount of physical motion difference (the object's position/rotation) before the object visually moves. This creates a stutter effect when there is no jitter.

  • Jitter position/rotation: the amount of inconsistency in the object's motion path. This creates the jitter commonly seen in claymation.

We experimented and emulated claymation with an interest to popularize this artistic animation style in gaming and make the style more accessible to developers without experience with claymation productions. If you are interested in how it works and not read the thesis, below is a brief explaination of our approach and some snippets of our process.

The mechanics of clay-animation

Usually, to create claymation, you move or change clay objects by hand every time you take a photo of the scene. Naturally, there'd be some inconsistency in the objects' motion between frames, giving a distinct jitter to the motion.

As such, for our game objects to jitter in their movement, we attempted to interject some noise into their physics-based movements. The problem? Modifying the movement of physics objects (in our case, Unity's Rigidbody) doesn't yield pleasing results. Since you're interrupting the game's physics calculations, the resulting movements are neither realistic (as in following the laws of physics) nor claymation-like (too much jitter).

However, we found a workaround. Since we only need the frame-by-frame animation appearance, we separated the physics component and the visual component of our game objects. The object's visuals track and follow the movement of the physics component every 15 frames (the frequency depends on how apparent you want the frame-by-frame effect to be). Then, we add some noise to the visual's position and rotation. That way, the physics movement is unaffected, yet the object appears jittery in motion.

Our game object set up: an object with its visual component and physics component separated (top left), and the claymation script attached to the visual component (right)



void FixedUpdate()
{
//Only update mesh position after some frames (frame-by-frame effect)
_animTime += Time.fixedDeltaTime;
if(_animTime < _animDuration) return;

//Generate unique noise for each axis
_jitterPos = new Vector3(Noise(), Noise(), Noise());
_jitterPos = new Vector3(Noise(), Noise(), Noise());

//Mesh follows the current physics transformation + adds jitter (subtleness controlled by strength)
_meshPos = _physPos + _jitterPos*_jitterPosStrength;
_meshRot = _physRot + _jitterRot*_jitterRotStrength;

_animTime = 0;
}

Simplified code of our claymation script

This solution works until we found that the object jitters all the time, even when it's practically not moving. We also wanted to create visible motion stutters when there is no jitter by eliminating minute changes between frames. So, we limited the jitter to only happen when the physics is moving, and limited the tracking to update when there's a significant change in the motion.


void FixedUpdate()
{
...

//If the physics moves, create noise vector
_jitterPos = _physRBD.velocity.sqrMagnitude > 0f ? 
new Vector3(Noise(), Noise(), Noise()) : Vector3.zero;
_jitterRot = _physRBD.velocity.sqrMagnitude > 0f ? 
new Vector3(Noise(), Noise(), Noise()) : Vector3.zero;


//Calculate the difference in motion
//If the difference is bigger than the range specified, update the transform value
//Do this for each axis

_deltaPos = Mathv.Abs((_physPos - _meshPos));
_animPos  = new Vector3
(
_deltaPos.x > _posStableRange ? _physPos.x : _meshPos.x,
_deltaPos.y > _posStableRange ? _physPos.y : _meshPos.y,
_deltaPos.z > _posStableRange ? _physPos.z : _meshPos.z
);

_deltaRot = Mathv.Abs((_physRot - _meshRot));
_animRot  = new Vector3
(
_deltaRot.x > _rotStableRange ? _physRot.x : _meshRot.x,
_deltaRot.y > _rotStableRange ? _physRot.y : _meshRot.y,
_deltaRot.z > _rotStableRange ? _physRot.z : _meshRot.z
);

_meshPos = _animPos + _jitterPos*_jitterPosStrength;
_meshRot = _animRot + _jitterRot*_jitterRotStrength;

...
}

Modified code to include stabilazation and motion stutter

And that is how we create procedural claymation with physics using a bit of code. Of course, there are places for improvement. For example, the jitter or the stabilization can cause the object to lay floating. But that is a project for the future : )