I'm making a very simple side scroller game - stuff slides in from the side and moves across the screen and you dodge it or collect it. The problem is that the animation seemed pretty jerky. I whittled down through the possible causes and ended up with a simple test case of just a plain square moving across the screen - and even that was jerky.
Odd that I've never noticed this before really, but perhaps it's just this case of having objects moving at a steady speed in a straight line that's making the jerkiness really obvious.
amazing test case - can you see the jerkiness?
Here's the code, there's a clip on the stage called "Block".
var lastTime:Number = 0;
var timer:Timer = new Timer(20); timer.addEventListener(TimerEvent.TIMER, handleTimer); function handleTimer($e:TimerEvent):void { var gt:Number = getTimer(); var t:Number = gt - lastTime; lastTime = gt; trace("t - " + t + " fps - " + Math.round(1000 / t));
Block.x -= t * 0.4;
if(Block.x < -100)Block.x = 900;
$e.updateAfterEvent();
} timer.start();
When I run this in the IDE I mostly get this:
t - 20 fps - 50 t - 20 fps - 50 t - 20 fps - 50 t - 20 fps - 50 t - 22 fps - 45 t - 22 fps - 45 t - 20 fps - 50 t - 20 fps - 50 t - 20 fps - 50
But just occasionally it spikes to this: > t - 57 fps - 18
I've also tried it with an ENTER_FRAME event instead of the Timer and the results are more or less the same, except there you sometimes get a really quick tick.
So I know I can't guarantee the framerate or the frequency of timer events - but does anyone have any tips for getting it a bit more steady? Or am I asking too much?
don't know but trace can take up a lot of time and it's not ideal to assume the value set to Block.x is the same as you get back.
var lastTime:Number = 0; var storeTrace: Array = [];
var dx: Number = Block.x;
var wait: Timer = new Timer(2000); wait.addEventListener(TimerEvent.TIMER, waitTillStable); var traceOut: Timer = new Timer( 10000 ); traceOut.addEventListener(TimerEvent.TIMER, traceOutInfo ); var timer:Timer = new Timer(20); timer.addEventListener(TimerEvent.TIMER, handleTimer);
wait.start();
function handleTimer($e:TimerEvent ):void {
var t: Number = -lastTime + (lastTime = getTimer());
storeTrace.push(t);
dx = dx - t * 0.4;
Block.x = dx;
if(dx < -100)dx = 900;
$e.updateAfterEvent();
}
function traceOutInfo($e:TimerEvent):void { var str: String = ''; var num:Number; for( var i: String in storeTrace ) { num = storeTrace[ i ]; str = str + "t - " + num + " fps - " + Math.round(1000 / num ) + "\n" } trace( str ); timer.removeEventListener(TimerEvent.TIMER, handleTimer); traceOut.removeEventListener(TimerEvent.TIMER, traceOutInfo ); }
function waitTillStable($e:TimerEvent):void { wait.removeEventListener(TimerEvent.TIMER, waitTillStable); traceOut.start(); timer.start(); }
well I only put the trace in because I saw it wasn't running smoothly, but I take your point.
Also noted your optimized code: var t: Number = -lastTime + (lastTime = getTimer());
well, I presume that's optimized?
'optimised' .. well only in theory, I would be reluctant to use that in production unless I had proved it was worthwhile but probably works faster? Are you running on a mac or pc, I find pc tend to run flash a lot better, there can be huge differences between say running in firefox standard and debug players and standalone and browser, I seem to rem safari is particularly a problem and IE is bad at flashvars, debug players are probably not ideal and standalone is not representative. I think if you run the timer constantly like that sometimes flash has to grab some time for its admin tasks so it probably works fine for movement over a much shorter timespan, but any longer and you will see flash taking back some admin time.
haha google forced me to spell it with a Z I swear! 
well I think at the end of the day I'll have to accept some jerkiness, but all said and done it's not running so bad in my actual game. Of course, that's on my fairly new PC laptop, so likely not representative, like you say.
Piece this URL together and have a look, tell me if it's badly jerky or acceptable? (I know there are depth issues and the collision detection is very simple - and all the levels are the same game)
danwashere.com/dev/pup REMOVE petman/grandpa/grandpa_game.html
also never test animation performance in the IDE. test it in your final environment. the debug player is gonna chug.
You may find you're just ducky in the production flash player.
err, JLM said that.
:pretend I wasn't here:
well now I've got more graphics in there to distract the eye the jerkiness isn't so noticeable I think. Hopefully it'll be ok now.
I've made a couple of updates to it, if anyone cares to take a look and let me know how it runs and how it plays?
http REMOVE ://www.danwashere.com/dev/pup REMOVE pet REMOVE man/grandpa/grandpa_game.html
p.s. improved collision detection is in the to do list, amongst many other things.
I would suggest with experimenting with custom tweens for the y position, that way the y movement won't stop so sharp for the car and movement would not be so linear so less easy for user to see issues. This is not the suggested tween but just for ideas ( unless you use physics engine for realistic acceleration decceleration. )
public inline function easeOutSubtle ( t : Float, b : Float, c : Float, d : Float ) : Float
{
var s = 0.1;
return c * ( ( t = t / d - 1 ) * t * ( ( s + 1 ) * t + s) + 1 ) + b;
}
Did you try moving the background with the timeline tween, or even try lots of animation frames, you can probably add remove elements into the timeline movieClip, I have not tried so don't know if better but I would try.
Also you could look at haXe it does more compiler optimisation.
Ps looks good movement is fine ( mac pro ).
thanks J. The bg is a timeline tween already - a flat floor image that cycles layed out in 3D with the 3D tool in the IDE.
I'll play about with softening the acceleration/deceleration with tweens.
Just had some issues with a complex background MovieClip full of vector being tweened with some away in front, it was too heavy so I grabbed the bitmaps and put them in a List, sorry tis haXe but I'm sure you will cope, maybe the approach will help you... it's funny how your own problems sharpen your solving skills more than anothers. Anyway it made a huge difference and was worth the additional code effort - I nearly abandoned all my timeline tween work. Er I would avoid using 3D for the floor surely that can be done in photo shop?
package zpartan.generic.views;
import zpartan.generic.views.SimpleView; import flash.display.Sprite; import flash.display.MovieClip; import flash.geom.Rectangle; import flash.geom.Point; import flash.events.Event; import flash.display.Bitmap; import flash.display.BitmapData;
// hsl signal classes import hsl.haxe.Signaler; import hsl.haxe.direct.DirectSignaler;
class CacheTimelineClipView {
private var _scope: Sprite;
private var _nom: String;
private var _view: SimpleView;
private var _mc: MovieClip;
private var _mcPreCache: MovieClip;
private var _listCacheClips: List<Sprite>;
private var _currCacheClip: Sprite;
private var _cacheItter: Iterator<Sprite>;
private var _firstTime: Bool;
private var _toggle: Bool;
public var cachedSignaler( default, null): Signaler<Void>;
public function new( scope_: Sprite, nom_: String )
{
_scope = scope_;
_nom = nom_;
cachedSignaler = new DirectSignaler( this, false );
}
public function init()
{
_toggle = false;
_firstTime = true;
_view = new SimpleView( _scope );
_mc = _view.addMovieClip( _nom );
_mc.visible = false;
_listCacheClips = new List();
_mcPreCache = new MovieClip();
//_bgPreCache.cacheAsBitmap = true;
_mc.addEventListener( Event.ENTER_FRAME, cacheFrames );
}
private function cacheFrames( e: Event )
{
var cacheSprite: Sprite;
trace( _mc.currentFrame );
if( _mc.currentFrame == 1 && _firstTime == false )
{
_mc.removeEventListener( Event.ENTER_FRAME, cacheFrames );
switchBgToCache();
cachedSignaler.dispatch();
}
else
{
_firstTime = false;
cacheSprite = new Sprite();
cacheSprite.addChild( new Bitmap( copyToBitmap( _mc ) ) );
cacheSprite.visible = false;
_listCacheClips.add( cacheSprite );
_mcPreCache.addChild( cacheSprite );
}
}
private function switchBgToCache()
{
trace('switchBgToCache' );
_mcPreCache.x = _mc.x;
_mcPreCache.y = _mc.y + 30;
_scope.removeChild( _mc );
_mc = null;
_scope.addChildAt( _mcPreCache, 0 );
_cacheItter = _listCacheClips.iterator();
_currCacheClip = _cacheItter.next();
_currCacheClip.visible = true;
_mcPreCache.addEventListener( Event.ENTER_FRAME, loopCacheFrames );
}
private function loopCacheFrames( e: Event )
{
if( _toggle == true )
{
_currCacheClip.visible = false;
if( _cacheItter.hasNext() == false )
{
_cacheItter = _listCacheClips.iterator();
}
_currCacheClip = _cacheItter.next();
_currCacheClip.visible = true;
}
_toggle = !_toggle;
}
public function copyToBitmap( mc: MovieClip ): BitmapData
{
mc.cacheAsBitmap = true;
var wide: Int = Std.int( mc.width );
var hi: Int = Std.int( mc.height );
var point: Point = new Point( 0, 0);
var rect: Rectangle = new Rectangle( 0 , 0, wide, hi);
var abitmap: BitmapData = new BitmapData( wide, hi, false, 0x000000 );
abitmap.draw( mc );
abitmap.copyPixels( abitmap, rect, point, abitmap, point, false );
return abitmap;
}
}
The _toggle is because I found I could update the background every other frame ( frame rate 30 but render bg at 15 ) and it reduced the over head to a point where flash stopped dropping frames but the bg still looked ok. I think that copying the bitmaps also reduced a lot of transparency calculations, but I am not sure if copyToBitmap is set up right I think it is - it seems to run fine at mo, but should check...
yeah I figure I'll be doing something similar for animated bits.
Er I would avoid using 3D for the floor surely that can be done in photo shop?
Not sure how you think that would work? The floor is animated, also I want to control the speed of the flow, so it has to be either using the 3D tool or faking it some other way - but a static p-shopped image won't work.
Not sure if its faster to cache the tween as frames, or to save them as jpg's... trouble is getting them into jpg's is work.
You don't need to 3D the table surface only the stuff above it.
you can control the flow by _bg.gotoAndStop( frame ), obviously you may need lots of frames for fine control but you may find some rounding will be ok for faking the eye... 3D its all about smoke and mirrors 
when I say "3D tool" I'm talking about the new CS4 3D tool in the IDE. The way it's animated is a simple horizontal timeline tween on the floor panels, then that clip is wrapped into another clip, and it's that 2nd clip that gets the 3D warping applied to it in the IDE. Very quick and simple way to do it - then you have to tweak the way your coded 3D works so that the objects appear to be on the floor surface, but that's just a little number jiggling.
The coded 3D is very simple: scale = fl / (fl + ty); $o.x = Math.round($o.rx * scale) + xcenter; $o.y = Math.round(-$o.ry * scale) + ycenter; $o.scaleX = $o.scaleY = scale;
Anyway, since it seems to run ok now, I think there's no need to optimise it anymore - I'll rethink that later on when I've added more stuff to it if I need to.
using any of the new 3D in flash will add an overhead and slow down did you try without to compare?
yeah, there wasn't any noticeable difference, and as you can see from my first stripped down test it's jerky even with practically nothing on the stage.
But I think the game plays ok now as it is.