springy expression

Moderators: Disciple, zlovatt

yogert909
Posts: 10
Joined: April 10th, 2006, 4:52 pm
Location: los angeles

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.
Varangian
Posts: 38
Joined: December 16th, 2004, 11:15 am

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
}
jovial
Posts: 11
Joined: April 7th, 2006, 4:22 am
Location: London
Contact:

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?
Dan Ebberts
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
jovial
Posts: 11
Joined: April 7th, 2006, 4:22 am
Location: London
Contact:

cracking work Dan :D 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
Dan Ebberts
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
jovial
Posts: 11
Joined: April 7th, 2006, 4:22 am
Location: London
Contact:

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
jovial
Posts: 11
Joined: April 7th, 2006, 4:22 am
Location: London
Contact:

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
jovial
Posts: 11
Joined: April 7th, 2006, 4:22 am
Location: London
Contact:

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.
gimmel
Posts: 7
Joined: January 13th, 2006, 5:35 am

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:

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
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 :oops:
Greetings from Germany

Here's a demo project with all methods of this topic included.
http://www.zweikaisers.de/AEscripts/spring.aep.zip
jovial
Posts: 11
Joined: April 7th, 2006, 4:22 am
Location: London
Contact:

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 :D
pants
Posts: 22
Joined: October 8th, 2004, 8:35 am

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





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
gimmel
Posts: 7
Joined: January 13th, 2006, 5:35 am

To be true: I never tried this expression in 3D space. If I find some time, I will try to make a better expression that works in 3D, too.
redforty
Posts: 5
Joined: March 1st, 2007, 4:32 pm

It works in 3d space for me. I love being able to slap this on camera position to get organic movement out of it. Slap it on focus distance, too, and you have a really nice setup.
ScottG
Posts: 38
Joined: March 19th, 2006, 2:45 am

motion blur doesn't seem to work properly when this is on! oh no!
does anyone else have that problem? i'm using ae7 on a g5 (non-intel) at the moment.
such a shame... works really well, if only it would blur!
Post Reply