A Flash Developer Resource Site

Results 1 to 12 of 12

Thread: Frame-independent easing

  1. #1
    self-portrait Kianis's Avatar
    Join Date
    Feb 2004
    Location
    Stockholm, Sweden
    Posts
    425

    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?!
    // Mazapán, my portfolio

  2. #2
    Senior Member tonypa's Avatar
    Join Date
    Jul 2001
    Location
    Estonia
    Posts
    8,223
    Why would it not work with actual time?

  3. #3
    Senior Member
    Join Date
    Jan 2008
    Location
    UK
    Posts
    269
    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);

  4. #4
    Student
    Join Date
    Apr 2001
    Location
    -
    Posts
    4,756

  5. #5
    Member
    Join Date
    Jun 2007
    Posts
    31
    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).

  6. #6
    Senior Member
    Join Date
    Jan 2008
    Location
    UK
    Posts
    269
    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.

  7. #7
    ....he's amazing!!! lesli_felix's Avatar
    Join Date
    Nov 2000
    Location
    London UK
    Posts
    1,506
    tween class yes.

    Native tween class, no.

    Use tweenlite or tweenmax.

  8. #8
    Developer dVyper's Avatar
    Join Date
    Oct 2008
    Location
    UK
    Posts
    168
    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.

  9. #9
    self-portrait Kianis's Avatar
    Join Date
    Feb 2004
    Location
    Stockholm, Sweden
    Posts
    425
    Thanks for all the answers!
    Quote 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;
    }
    // Mazapán, my portfolio

  10. #10
    Member
    Join Date
    Jun 2007
    Posts
    31
    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".

  11. #11
    Senior Member
    Join Date
    Jan 2008
    Location
    UK
    Posts
    269
    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.

  12. #12
    self-portrait Kianis's Avatar
    Join Date
    Feb 2004
    Location
    Stockholm, Sweden
    Posts
    425
    _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.
    // Mazapán, my portfolio

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center