Scripts, Tools & Methods Developed at Hook
If you’ve ever worked on any client projects requiring ‘realistic’ or ‘real-world’ physics, you’ve probably found that their idea of realistic differs wildly from the physics resultant from the Big Bang. Likewise with games, its often enough and even better to convey the feel of physics rather than running a precise simulation. After all, you are dealing with a combination of the audience’s senses, impressions, and perceptions of the real world, and perpetuating that lie is more important than the truth as measured by Newton.
While there are several great Actionscript physics engines out there, which we have used to great success, we also needed a toolset for handling those more… quirky requests. Out of this necessity, and surely an expression of my own masochism, I have undertaken an attempt at creating a set of tools for creating physics based movement (rather than a full blown physics engine) that is better suited at handling these ambiguities, and will be presenting the difficulties and discoveries encountered as that toolset develops. Here goes:
Implicit Velocity Integration
Having written plenty of on-the fly physics code, my go-to approach was simply plugging in numbers into the classical mechanics formulas:
Which were integrated into the system each update along these lines (known as Euler integration):
calculate acceleration according to accumulated forces:
integrate that acceleration into the velocity:
integrate velocity into the position:
It is a very simple and intuitive approach that can get you pretty far for most simple cases (where accuracy isn’t crucial). A little further research into the topic brought me to the Numerical Integration chapter of Keith Peters’ book AdvancED ActionScript 3.0 Animation, which gave a great overview of the various accuracy issues involved with simulated motion, and the first mention of integration I’ve seen in an Actionscript specific context. The most important take away from that in regards to fuzzy physics was the deliciously black-magic-esque Verlet Integration. The most cited and plainly stated (at least to a non-rocket scientist) description of which is in the paper ‘Advanced Character Physics’ by Thomas Jakobsen, which is available all over the interweb (link at bottom).
(according to Jakobsen)
How Verlet differs from other integration schemes is that rather than storing and modifying position and velocity in sequence, the velocity is determined each step by measuring the point’s current position from its previous position. While seemingly no-big-deal, what this offers us is direct control over the point’s position while the physics system catches up, rather than making offerings of forces and impulses to the gods within the machine and hoping the results work out pleasingly*. The points I am referring to can be anything represented by a position and mass: particles, vertices, boids, etc. and will be the focus of early development due to that simplicity.
*Note: This is in no way to say that Verlet is the end-all be-all of physics integration schemes, it wont necessarily pass the stress test of 100s of stacking boxes the way rigid-body physics would.
Using implicit velocity also allows us to focus on the relationship between points and manipulate the negative space around them (mmmmmsoft-bodies). Which brings us to the point of this first post:
A constraint represents a relationship between two point-masses and is used to ‘correct’ those points’ positions as to satisfy the constraint’s requirements (called relaxation from here-on out). These requirements could be that the points maintain a set distance, a minimum/maximum distance, or just about any silly condition you can dream up.
Example 1 illustrates a simple Equality (rigid) constraint in 1 dimension, which relaxes to maintain a set distance between the two points, while distributing the correction between the points based on the weight shift.
, where represents vector magnitude.
So on each update we are effectively moving each point-mass by a fraction of the displacement between the two as it differs from the constraint’s rest length. If you are familiar with spring physics, this should feel very similar, given that spring force is calculated by displacement:
, where is the elasticity of the spring
However, through this rigid constraint approach, our points are bound by springs that are precisely so elastic and dampened enough to maintain the specific relationship we have defined! (Which isn’t exactly the most practical or efficient thing to achieve with spring physics, we’ll cover spring-based motion in another post)
Let’s expand this concept to 2D, add a little mouse driven movement and see what happens:
Here I am using the same rigid constraint mechanism as described above, only when a point is clicked on, the weight shifts so that there is zero correction on the dragged point, while there is 100% correction on the connected point for what is beginning to resemble a kinematic chain. Here is the dynamics update in psuedo-code
// start off by applying our driving force, in this case mouse position drivingPoint.position = mousePosition; // relax our constraint // find the displacement between the points // Vector2D is my own version of a 2D object representation, but the concept here is any // n-dimensional object (x, y, Point, DisplayObject, Vector3D, etc. ) var delta:Vector2D = pointB - pointA; // then the ratio between that delta and the rest length var ratio:Number = (delta.length - constraint.length) / delta.length; // correct the position of our points pointA += delta * ratio * constraint.strengthA; pointB -= delta * ratio * constraint.strengthB;
With that basic constraint dynamic in place, we can now incorporate the implicit velocity system mentioned above to add a feeling of momentum and inertia. More psuedo-code:
First, some basic set-up:
// these two variables will be switched depending // on which side of the constraint we click var dragPoint:VerletPointMass; var endPoint:VerletPointMass; // a gravity constant var gravity:Number = 8.0; // our timestep frequency (it is critical when dealing with physics to maintain a consistent update frequency) // in the examples I have a corrected timer that runs the movement update at a fixed interval of 0.33 seconds var timeDelta:Number = 0.33;
Then, add to our update loop:
// because mouse movement is our driving force, we move that first, then correct the rest of our system // we're just using a basic linear interpolation here to get some smoothness dragPoint.old = dragPoint.position; dragPoint.position += (mouse - dragPoint.position) * 0.5; // some temporary variables for our position modification // because we need to properly store our old position for the next update var newX:Number, newY:Number; // integrate the movement according to Jakobsen's technique (chosen for clarity and simple drag) newX = 1.9 * endPoint.position.x - 0.9 * endPoint.old.x + (0 * timeDelta * timeDelta); newY = 1.9 * endPoint.position.y - 0.9 * endPoint.old.y + (gravity * timeDelta * timeDelta); endPoint.old = endPoint.position; endPoint.position.x = newX; endPoint.position.y = newY; // from there, run the constraint relaxation as described above // and the endpoint will maintain a fixed distance from the point being dragged // while simultaneously being affected by gravity and a 'pull' force from the mouse // as translated through the constraint
And here is what you get:
Next up, I’ll be doing some experiments with some different constraint set-ups as well as more advanced motion.