I have been always amazed by a fascinating procedural animation using in some games like Rain World. The key feature there is that you define sort of a target points for a model and a clever algorithm figures out the way how each bone or "leg" will move to achieve the goal. This is a common problem of kinematics. Let's start with the simplest approach
Cycloid
If we roll a ball over a plane surface with attached marker at the side it will produce a well-known curve
ParametricPlot[{x - Sin[x], 1 - Cos[x]}, {x,0,6Pi}]
Imagine it it were legs, which would follow multiple cycloids with a little offset. But firstly, we should get rid of a plane ground and turn it into something more interesting
To project cycloid onto land
curve, we could not find anything better, that to offset y
axis
try to zoom in with a mouse
FABRIK Inverse Kinematics
Legs of a spider are not straight lines, and consists of small segments needed to be animated separately. For this case there is a nice heuristic algorithm FABRIK.
I do not claim that my implementation is most optimized, but it does solve a problem
ClearAll[cached];
cached[expr_] := cached[expr] = expr;
SetAttributes[cached, HoldAll]
SetAttributes[fabrik, HoldFirst]
fabrik[chain_, target_, origin_] := Module[{
buffer, prev,
lengths = cached[Norm /@ (chain // Reverse // Differences) // Reverse]
},
buffer = Table[With[{p = chain[[-i]]},
If[i === 1,
prev = target;
target
,
prev = prev - Normalize[(prev - p)] lengths[[1-i]];
prev
]
], {i, chain // Length}] // Reverse;
chain = Table[With[{p = buffer[[i]]},
If[i === 1,
prev = origin;
origin
,
prev = prev - Normalize[(prev - p)] lengths[[i-1]];
prev
]
], {i, chain // Length}];
]
Let us see how it works in the following example
chain = {{0,0}, {0.5,1}, {1,1}, {1,0.5}};
Graphics[{
Line[chain // Offload],
EventHandler[Graphics`Canvas[], {"mousemove" -> Function[xy,
fabrik[chain, xy, {0,0}]
]}]
}]
The next idea will be to assign the target position of each leg to a cycloid.
Modelling legs
We can isolate our a leg into a independent component, hence it would be easier to combine them together
here the first argument specifies an EventObject
identifier, that will be called later on to update all legs segments in the animation loop.
Now we can combine multiple legs, which target's variables will follows cycloids with small offsets as follows
Adding controls
Slides are boring. Using Graphics`Canvas[]
with EventHandler
we can capture arrow keys and animate accordingly. However, to animate continuously we can set a timer and remove it once the target position of a spider has been reached
Here we also animate the view of the canvas and pan it to keep our spider in the center of the screen. To achieve that we mark the graphics with MetaMarker
and then execute in its context ZoomAt
function, which affects the state of an existing Graphics
container.
The result
focus on a canvas with your mouse and control the spider using arrow keys
Notebook is available by the link below