hey folks, I'm looking for an expression that will take keyframed position data and output position data as if the layer were attached by a bungee or shock absorber.
I'm ok with simple expressions, but I don't even know where to start with this one.
Thanks for any suggestions.
springy expression
this will work well if you have position keyframes and you would like decay after the keyframes on the y axis (Thanks to Dan Ebberts for the original code)
n = position.numKeys;
if (n > 0){
t = time - position.key(n).time;
if (t > 0){
freq =8;
amplitude = 50;
decay = 2.0;
e = amplitude*Math.sin(freq*t*1*Math.PI)/Math.exp(decay*t)
value + [0,e]
}else{
value
}
}else{
value
}
n = position.numKeys;
if (n > 0){
t = time - position.key(n).time;
if (t > 0){
freq =8;
amplitude = 50;
decay = 2.0;
e = amplitude*Math.sin(freq*t*1*Math.PI)/Math.exp(decay*t)
value + [0,e]
}else{
value
}
}else{
value
}
I've also been after something similar here. Ebberts' original code creates the spring effect from the in/start point of the layer. Varangian's variation on the script applies preset spring values afrter the last key frame on a move.
Is there any way of having a layer follow every keyframe (not just the last) as though on a spring, based on it's inherited velocity.
I've tried creating a delay expression but this means a layer doesn't inherit any spring motion as it only tries to follow another position.
Any thoughts? Dan, if you're watching, ever created anything like this?
Is there any way of having a layer follow every keyframe (not just the last) as though on a spring, based on it's inherited velocity.
I've tried creating a delay expression but this means a layer doesn't inherit any spring motion as it only tries to follow another position.
Any thoughts? Dan, if you're watching, ever created anything like this?
-
- Posts: 320
- Joined: June 26th, 2004, 10:01 am
- Location: Folsom, CA
- Contact:
Try this one:
n = 0;
if (numKeys > 0){
n = nearestKey(time).index;
if (key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - key(n).time;
}
if (n > 0){
v = velocityAtTime(key(n).time - thisComp.frameDuration/10);
amp = .05;
freq = 4.0;
decay = 2.0;
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}
Dan
n = 0;
if (numKeys > 0){
n = nearestKey(time).index;
if (key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - key(n).time;
}
if (n > 0){
v = velocityAtTime(key(n).time - thisComp.frameDuration/10);
amp = .05;
freq = 4.0;
decay = 2.0;
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}
Dan
cracking work Dan
That's grrrreat.
I'm trying to get layers to follow another leader layer and inherit the same spring from velocity. I revised your script as follows (so that the layer looked at "leader"s position keyframes):
n = 0;
if (thisComp.layer("leader").position.numKeys > 0){
n = thisComp.layer("leader").position.nearestKey(time).index;
if (thisComp.layer("leader").position.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - thisComp.layer("leader").position.key(n).time;
}
if (n > 0){
v = thisComp.layer("leader").position.velocityAtTime(thisComp.layer("leader").position.key(n).time - thisComp.frameDuration/10);
amp = .05;
freq = 4.0;
decay = 2.0;
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}
this layer had to be parented to the "leader" layer to follow the position as the expression only creates the bounce. there was some bounce but it was significantly less than if the keyframes were in the same layer. can you see something i've missed?
is there a way of having a stack of layers, each layer looking at the layer above, thus creating a longer dynamic spring?
one more question, what does n-- mean? ..think i really need to pick up that 'physics for game developers' book you recommend on your site.
thank you in anticipation of your help

I'm trying to get layers to follow another leader layer and inherit the same spring from velocity. I revised your script as follows (so that the layer looked at "leader"s position keyframes):
n = 0;
if (thisComp.layer("leader").position.numKeys > 0){
n = thisComp.layer("leader").position.nearestKey(time).index;
if (thisComp.layer("leader").position.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - thisComp.layer("leader").position.key(n).time;
}
if (n > 0){
v = thisComp.layer("leader").position.velocityAtTime(thisComp.layer("leader").position.key(n).time - thisComp.frameDuration/10);
amp = .05;
freq = 4.0;
decay = 2.0;
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}
this layer had to be parented to the "leader" layer to follow the position as the expression only creates the bounce. there was some bounce but it was significantly less than if the keyframes were in the same layer. can you see something i've missed?
is there a way of having a stack of layers, each layer looking at the layer above, thus creating a longer dynamic spring?
one more question, what does n-- mean? ..think i really need to pick up that 'physics for game developers' book you recommend on your site.
thank you in anticipation of your help
-
- Posts: 320
- Joined: June 26th, 2004, 10:01 am
- Location: Folsom, CA
- Contact:
This should eliminate the parenting issue:
P = thisComp.layer("leader").position;
n = 0;
if (P.numKeys > 0){
n = P.nearestKey(time).index;
if (P.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - P.key(n).time;
}
if (n > 0){
v = P.velocityAtTime(P.key(n).time - thisComp.frameDuration/10);
amp = .05;
freq = 4.0;
decay = 2.0;
P + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
P;
}
I'm not sure about the stack of layers. You'd need to describe the desired behavior a little more. I mean, is the bounce no longer being driven by the keyframes of the leader, but instead by the motion of the layer above?
n-- is just JavaScript notation for n = n -1 (decrement by one).
Dan
P = thisComp.layer("leader").position;
n = 0;
if (P.numKeys > 0){
n = P.nearestKey(time).index;
if (P.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - P.key(n).time;
}
if (n > 0){
v = P.velocityAtTime(P.key(n).time - thisComp.frameDuration/10);
amp = .05;
freq = 4.0;
decay = 2.0;
P + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
P;
}
I'm not sure about the stack of layers. You'd need to describe the desired behavior a little more. I mean, is the bounce no longer being driven by the keyframes of the leader, but instead by the motion of the layer above?
n-- is just JavaScript notation for n = n -1 (decrement by one).
Dan
fantastic. that now works perfectly for a following layer.
Yes, as you said, I'd like to achieve a layer following the motion from the layer above's motion, not the leader keyframes. I'll have a play over the weekend. have a nice one yourself.
Jovial
Yes, as you said, I'd like to achieve a layer following the motion from the layer above's motion, not the leader keyframes. I'll have a play over the weekend. have a nice one yourself.
Jovial
okay, here's what i have now. looking much more fun. create a vertical column of about 5 solids, each 100 x 100. switch all the layers to 3D (if you desire 3 dimensional movement). change the top layer name to "leader". apply this to the position property of each layer below in the stack:
P = thisComp.layer("leader").position;
delay = 0.5;
de = delay*thisComp.frameDuration*(index-thisComp.layer("leader").index);
F = thisComp.layer(index-1).position.valueAtTime(time-de);
xyz = position.valueAtTime(0) - P.valueAtTime(0);
n = 0;
if (P.numKeys > 0){
n = P.nearestKey(time).index;
if (P.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - P.key(n).time;
}
if (n > 0){
v = P.velocityAtTime(P.key(n).time - thisComp.frameDuration/10);
amp = 0.1;
freq = 2;
decay =4;
F + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t) + xyz;
}else{
F;
}
throw a few keyframes on the leader's position, allowing it to come to rest a few times. there should be a nice delay on each lower solid as though they're attached in one long column. note, the layers won't disperse to their original places until the timeline hits the leader's first position keyframe. therefore make sure you place a keyframe on the first frame.
the 'F' value follows the position of the layer above and also creates the springy lag. thanks to Dan Ebberts for his delay expression on motionscript.
the 'xyz' variable allows you to maintain the same position offset between all the layers, instead of every layer snapping to the leader's position.
in my project i've attached the 'delay', 'amp', 'freq', and 'decay' variables to slider controls on one main 'controls' null. this allows much faster tweaking of all the bounce and delay conditions.
lots more to play with. i now want to apply this to a puppet's limbs. as the body is jerked around the arms and legs look slightly independant.
jovial
EDIT: i tried setting up based on what i described above and it's not as straight forward as i'd hoped. the layers exponetially seperate therefore once the expression and keyframes are applied you need to slide the position values of the layer stack to get them back into position. this is something i will look into solving.
be back soon
P = thisComp.layer("leader").position;
delay = 0.5;
de = delay*thisComp.frameDuration*(index-thisComp.layer("leader").index);
F = thisComp.layer(index-1).position.valueAtTime(time-de);
xyz = position.valueAtTime(0) - P.valueAtTime(0);
n = 0;
if (P.numKeys > 0){
n = P.nearestKey(time).index;
if (P.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - P.key(n).time;
}
if (n > 0){
v = P.velocityAtTime(P.key(n).time - thisComp.frameDuration/10);
amp = 0.1;
freq = 2;
decay =4;
F + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t) + xyz;
}else{
F;
}
throw a few keyframes on the leader's position, allowing it to come to rest a few times. there should be a nice delay on each lower solid as though they're attached in one long column. note, the layers won't disperse to their original places until the timeline hits the leader's first position keyframe. therefore make sure you place a keyframe on the first frame.
the 'F' value follows the position of the layer above and also creates the springy lag. thanks to Dan Ebberts for his delay expression on motionscript.
the 'xyz' variable allows you to maintain the same position offset between all the layers, instead of every layer snapping to the leader's position.
in my project i've attached the 'delay', 'amp', 'freq', and 'decay' variables to slider controls on one main 'controls' null. this allows much faster tweaking of all the bounce and delay conditions.
lots more to play with. i now want to apply this to a puppet's limbs. as the body is jerked around the arms and legs look slightly independant.
jovial
EDIT: i tried setting up based on what i described above and it's not as straight forward as i'd hoped. the layers exponetially seperate therefore once the expression and keyframes are applied you need to slide the position values of the layer stack to get them back into position. this is something i will look into solving.
be back soon
here it is
P = thisComp.layer("leader").position;
delay = 0.8; // link to a slider for easy tweaking
de = delay*thisComp.frameDuration*(index-thisComp.layer("leader").index);
F = thisComp.layer(index-1).position.valueAtTime(time-de);
xyz = position.valueAtTime(0) - thisComp.layer(index-1).position.valueAtTime(0);
n = 0;
if (P.numKeys > 0){
n = P.nearestKey(time).index;
if (P.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - P.key(n).time;
}
if (n > 0){
v = P.velocityAtTime(P.key(n).time - thisComp.frameDuration/10);
amp = 0.05; // link to a slider for easy tweaking
freq = 2; // link to a slider for easy tweaking
decay = 2; // link to a slider for easy tweaking
F + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t) + xyz;
}else{
F + xyz;
}
forgot to change the xyz difference from the leader to difference from layer above. also changed the final else statement to F+xyz so this shouldn't cause problems with the first keyframe now.
P = thisComp.layer("leader").position;
delay = 0.8; // link to a slider for easy tweaking
de = delay*thisComp.frameDuration*(index-thisComp.layer("leader").index);
F = thisComp.layer(index-1).position.valueAtTime(time-de);
xyz = position.valueAtTime(0) - thisComp.layer(index-1).position.valueAtTime(0);
n = 0;
if (P.numKeys > 0){
n = P.nearestKey(time).index;
if (P.key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - P.key(n).time;
}
if (n > 0){
v = P.velocityAtTime(P.key(n).time - thisComp.frameDuration/10);
amp = 0.05; // link to a slider for easy tweaking
freq = 2; // link to a slider for easy tweaking
decay = 2; // link to a slider for easy tweaking
F + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t) + xyz;
}else{
F + xyz;
}
forgot to change the xyz difference from the leader to difference from layer above. also changed the final else statement to F+xyz so this shouldn't cause problems with the first keyframe now.
All these ways to simulate a spring have a little problem: they generate fake animations. The springy motion gets calculated only at keyframes.
I've tried a different way:
Motion is calculated for every frame. Since After Effects is not able to keep global variables (unlike the old Motion Math), all frames before the actual frame have to be calculated to get actual speed and direction.
That's the disadvantage of this script: it's getting slower with every frame.
Sorry for my bad english
Greetings from Germany
Here's a demo project with all methods of this topic included.
http://www.zweikaisers.de/AEscripts/spring.aep.zip
I've tried a different way:
Code: Select all
rapidity=3; //following speed
inertia=0.15; //how dull the following will be (0-1)
leader=thisComp.layer("leader")
pos1=leader.position;
pos2=leader.position;
v=0; i=0;
while (i<=time)
{
pos1=leader.position.valueAtTime(i);
delta=sub(pos1,pos2);
a=delta*rapidity*thisComp.frameDuration;
v=(v+a)*(1-inertia);
pos2 += v;
i += thisComp.frameDuration;
}
pos2
That's the disadvantage of this script: it's getting slower with every frame.
Sorry for my bad english

Greetings from Germany
Here's a demo project with all methods of this topic included.
http://www.zweikaisers.de/AEscripts/spring.aep.zip
great work Gimmel. i love the fact the follower can inherit velocity around the leader and orbit for a short period of time.
i was looking at flash actionscripts that acheived this effect but couldn't translate the code as couldn't find a way of storing variables globally.
again, great
i was looking at flash actionscripts that acheived this effect but couldn't translate the code as couldn't find a way of storing variables globally.
again, great

gimmel,
thanks for posting this. it's very cool.
but when i play with this and animate the leader in 3d space, the leader and follower flip-flop in 3d space, so sometimes the leader is in front and then sometimes the follower is in front.
how can you adapt this to always have the follower behind the leader?
thanks
thanks for posting this. it's very cool.
but when i play with this and animate the leader in 3d space, the leader and follower flip-flop in 3d space, so sometimes the leader is in front and then sometimes the follower is in front.
how can you adapt this to always have the follower behind the leader?
thanks
Code: Select all
rapidity=3; //following speed
inertia=0.15; //how dull the following will be (0-1)
leader=thisComp.layer("leader")
pos1=leader.position;
pos2=leader.position;
v=0; i=0;
while (i<=time)
{
pos1=leader.position.valueAtTime(i);
delta=sub(pos1,pos2);
a=delta*rapidity*thisComp.frameDuration;
v=(v+a)*(1-inertia);
pos2 += v;
i += thisComp.frameDuration;
}
pos2