-
self-portrait
Frame-independent easing
Hey guys,
Let's say I have a sprite that I want to "ease" to a target
Code:
sprite.x += ( target.x -sprite.x ) *0.9;
What I can't get my head around is how to do this while being
frame-independent. This will obviously not work since it will
change the fraction
Code:
sprite.x += ( target.x -sprite.x ) *0.9 *deltaTime;
Help?!
-
Senior Member
Why would it not work with actual time?
-
You can use the tween class which is time, not frame dependent, and allows you to use various easing algorithms.
eg. (as3)
Code:
var tween:Tween = new Tween(displayObject,"x",Strong.easeOut,startValue,targetValue,durationInSeconds,true);
-
-
The actual form of the equation you are after is exponential, where the speed it converges is proportional to the distance away.
Lets say you are staring at 1 and heading towards 0 (you can always add + subtract the offset).
So in one step you have 0.9, 2 your have 0.9*0.9 etc, but what about half a step? sqrt(0.9) = 0.9 ^ 0.5. So it is 0.9 ^ (dt).
-
It's a lot more complicated than that though, if you go the maths route.
Firstly, using an exponential function will never actually get the object all the way to the target (it will always be moving 0.9 of the remaining distance towards the target, so there will always be some distance remaining). You could get around that by either setting an artificial target value slightly beyond the actual target value, or increasing the value of 0.9 so that it gets closer and closer to a value of 1 as it approaches the target.
Secondly, from my own experience, using the delta time approach for exponential functions can be very temperamental and inaccurate - small blips in the frame rate can cause relatively large disturbances to a finely tuned exponential function.
-
....he's amazing!!!
tween class yes.
Native tween class, no.
Use tweenlite or tweenmax.
-
Developer
If you're using a low number of tweens (i.e less than 10 at a time) then the built in Tween is perfectly fine.
-
self-portrait
Thanks for all the answers!
Originally Posted by tonypa
Why would it not work with actual time?
Because applying delta time to the equation like that will change
the percent by which the value changes each frame.
Changing a value 3% ten times in not the same as changing
the same value 30% one time.
The tween class is good for creating a... tween. My example wasn't
the best probably, but it's not just about simple animations. What if you
had several forces acting upon a particle?
Chookmaster: So this should do the trick? I'll try it as soon as I get home.
Code:
var p:Object = { positon:Vector, velocity:Vector };
main_loop ( delta:Number)
{
p.position += p.velocity;
p.velocity *= Math.pow ( 0.9, delta ); // instead of: p.velocity *= 0.9;
}
-
Something like that - back in terms of your original equation:
Code:
sprite.x += ( target.x -sprite.x ) *0.9 *deltaTime;
It would be
Code:
sprite.x += ( target.x -sprite.x ) * Math.pow(0.9,deltaTime);
Depending on what you want. Seems like you have the correct form of equation for "closing in on a value".
-
The tween class is good for creating a... tween. My example wasn't
the best probably, but it's not just about simple animations. What if you
had several forces acting upon a particle?
The tween class can be used to apply a mathematical progression to any value - it doesn't have to be an animation or an actual tween. If you have a force who's value you want to curve to a target value, you might do domething like this:
Code:
var valueHolder:Object = new Object();
valueHolder.force = 0;
var forceTween:Tween;
initTween();
stage.addEventListener(Event.ENTER_FRAME,showValue);
function initTween():void{
forceTween = new Tween(valueHolder,"force",Strong.easeOut,startValue,targetValue,durationInSeconds,true);
}
function showValue(e:Event):void{
trace(valueHolder.force);
}
I get the feeling you want to do the maths yourself though - good for you.
-
self-portrait
_Ric_: Cool! I didn't know that. But yes, I want to do the maths myself if I can.
I still can't get this to work I wrote this example program (AS2), paste it on the first frame,
or just look at it here of course, it's very simple
Code:
init ();
onEnterFrame = main;
var textField:TextField; // text field for output
var red:MovieClip; // red square has an initial velocity that eases out
var blue:MovieClip; // blue square eases toward a target point
var redSpeed:Number; // initial velocity of red square
var previousTime:Number; // the time of the previous frame
var startTime:Number; // the time of the first frame
var redTime:Number; // the time of the moment the red square stops
var blueTime:Number; // the time of the moment the blue square stops
var redStopped:Boolean; // red square has stopped
var blueStopped:Boolean; // blue square has stopped
/*
* main
*/
function main () :Void
{
// get delta time
var deltaTime:Number = ( getTimer () -previousTime ) /1000;
previousTime = getTimer ();
// move red square
red._x += redSpeed;
//redSpeed *= 0.97; // normal, frame dependent easing
redSpeed *= Math.pow ( 0.97, deltaTime ); // updated, time dependent, non-working easing
// move blue square
var deltaX:Number = 500 -blue._x;
//deltaX *= 0.05; // normal, frame dependent easing
deltaX *= Math.pow ( 0.05, deltaTime ); // updated, time dependent, non-working easing
blue._x += deltaX;
// if a square has stopped (or at least is moving _very_ slowly) we want to know how long it took
if ( !redStopped && redSpeed < 0.1 )
{
redStopped = true;
redTime = getTimer ();
}
if ( !blueStopped && deltaX < 0.1 )
{
blueStopped = true;
blueTime = getTimer ();
}
// output
textField.text = "Delta time: " +deltaTime +"\n";
textField.text += "Red square: " +( redTime -startTime ) /1000 +" seconds.\n";
textField.text += "Blue square: " +( blueTime -startTime ) /1000 +" seconds.\n";
}
/*
* init
*/
function init () :Void
{
textField = createTextField ( "textfield", 0, 0, 0, 300, 100 );
// create graphics for start and end points
createSquare ( 20, 200, 0xffcccc ); // start red
createSquare ( 20, 300, 0xccccff ); // start blue
createSquare ( 500, 300, 0xccccff ); // end blue
// create the objects that will move
red = createSquare ( 20, 200, 0xff0000 );
blue = createSquare ( 20, 300, 0x0000ff );
// set some initial vars
redSpeed = 10;
previousTime = startTime = redTime = blueTime = getTimer ();
redStopped = blueStopped = false;
}
/*
* createSquare
*/
function createSquare ( x:Number, y:Number, color:Number ) :MovieClip
{
var mc:MovieClip = createEmptyMovieClip ( "mc" +color, getNextHighestDepth () );
mc._x = x;
mc._y = y;
mc._xscale = mc._yscale = 100 *10;
mc.beginFill ( color );
mc.moveTo ( -1, -1 );
mc.lineTo ( +1, -1 );
mc.lineTo ( +1, +1 );
mc.lineTo ( -1, +1 );
mc.lineTo ( -1, -1 );
mc.endFill ();
return mc;
}
Last edited by Kianis; 12-17-2008 at 08:27 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|