[haXe] Animation performace issues
Jaevla Faenskap
harikke at email.com
Sun Aug 26 01:59:08 CEST 2007
Hi,
Thanks for the reply. I tried to do as you said in 1&2 - and now my animation at least is even. Tough not smooth. What I mean is that there is no more stuttering, which is good, but the movement is still not very smooth. If I move the player-clip 5 pixels per update, I can clearly see two of them moving along... Maybe I need some kind of double-buffering? I did try to experiment with copypixels and double-buffering, but still couldn't manage smooth movements.. I really feel like there is something vital that I am missing.
As for your 3. it seems you are right, removing the collision-detection I can see no slowing down. I'd love to have a look at your routine, if you would be so kind.
I found the reason it slows down over time tough, it was the mob-shots that weren't removed from the array. I was using the canvas height in my test for when it moves out of view, but the height of the canvas is extended as the shot moves out of view... So it never gets removed from the array, and I do a lot of collision-tests on shots that should have been removed. Not the behavior I expected, but I guess it makes sense..
You are quite right in 4 - I could have used a sprite for all the objects I have here, but in the end I would like them all to be animated. Is this unreasonable to expect with flash? I've seen a demo somewhere with 7000 onscreen animated objects in flash using copypixel, so I didn't expect 50-60 animated movieclips to be a problem?
For your 5 - how do I then free memory? Should I rather re-use the objects that move out of view?
Again, thanks for taking the time.
Cheers
----- Original Message -----
From: "edA-qa mort-ora-y"
To: "The haXe compiler list"
Subject: Re: [haXe] Animation performace issues
Date: Sat, 25 Aug 2007 12:26:23 +0200
I have several answers that may help.
1. Don't use updateAfterEvent. I know every site says this is a good
thing, but I've actually seen better performance by just setting the
framerate higher. That is, just take 30FPS, and whatever timer you
want, and the animation will be smoother.
2. That said, 10ms timer is a little low, and you are likely only
getting a 40-50ms timer. Choose about 50ms (that is how my Palisaro
game works and the animation is smooth there)
3. Your Collision detection is the likely long-term slow-down, it is
consuming an *extreme* amount of memory. I have a class which does this
and cache's the bitmaps (no excessive allocation). This had shown
significant performance improvements in Palisaro. I'm happy to make
this available to you.
4. MovieClip vs. Sprite. None of your shot objects actually need a
MovieClip, and Sprite would be better suited to that (perhaps the
MovieClip is adding overhead that is slowing down your code)
5. You should never need to assign null or do a deleteField for the
purpose of freeing memory -- not to mention that it likely doesn't even
achieve that.
Jaevla Faenskap wrote:
> Hi all,
>
> I'm totally new to flash and actionscript development, so I might
> be way off here.. However I was hoping you could help me a bit
> with a few issues I'm having.
>
> This might be a little more general actionscript than haxe
> specifically, I'm not sure - I've only used haxe so far (and I
> think it's a great project!).
>
> It might be a little long, but I decided to include all my code
> (it's (almost) a small game). Let me know if I should have posted
> it some other way. I am targeting Flash9 (AS3).
>
> Anyway, on to the questions.. The problems I am having is with
> animation. All the games I have seen have really smooth movement.
> Mine not so. I'm using a timer for updating positions (so it will
> run at the same speed independent of framerate), and I use an
> updateafterevent(). But still when I move my player-controlled
> clip I see a lot of stuttering - no matter what framerate I run
> it on. What's the magic trick I am missing?
>
> Also, if I let the "game" run, it just keeps on slowing down. I
> guess that this is because of my shots not beeing properly
> deleted somehow when they move out of the screen - even tough I
> set them to null. I've also tried Reflect.deleteField() - to no
> avail (though I couldn't find any documentation on what the
> string was supposed to be?).
>
> I hope I'm not asking too much, and that someone with the
> knowledge and kindness will step up?
>
>
> Cheers
>
>
> --- CODE FOLLOWS ---
> import flash.display.StageScaleMode;
> import flash.display.Bitmap;
> import flash.display.BitmapData;
> import flash.display.MovieClip;
> import flash.display.Sprite;
> import flash.events.Event;
> import flash.events.KeyboardEvent;
> import flash.events.TimerEvent;
> import flash.geom.ColorTransform;
> import flash.geom.Rectangle;
> import flash.Lib;
> import flash.net.ObjectEncoding;
> import flash.ui.ContextMenu;
> import flash.utils.Timer;
> import flash.display.BlendMode;
> import haxe.Timer;
>
> class Mob extends MovieClip
> {
> public var direction:String;
> public function new()
> {
> super();
>
> this.graphics.beginFill(0x555555);
> this.graphics.drawCircle(32,32,32);
> this.graphics.endFill();
>
> direction = "RIGHT";
> }
> }
> class Shot extends MovieClip
> {
> public var speed:UInt;
> public function new()
> {
> super();
>
> this.graphics.beginFill(0xff0000);
> this.graphics.drawCircle(8,8,8);
> this.graphics.endFill();
>
> this.speed = 4;
> }
> }
> class MobShot extends MovieClip
> {
> public var speed:UInt;
> public function new()
> {
> super();
>
> this.graphics.beginFill(0xff0000);
> this.graphics.drawRect(0,0,3,6);
> this.graphics.endFill();
>
> this.speed = 2;
> }
> }
> class Spaceship extends MovieClip
> {
> public var moveleft:Bool;
> public var moveright:Bool;
> public var speed:UInt;
> public function new()
> {
> super();
>
> this.graphics.beginFill(0x000000);
> this.graphics.moveTo(31,0);
> this.graphics.lineTo(33,0);
> this.graphics.lineTo(33,10);
> this.graphics.lineTo(64,64);
> this.graphics.lineTo(0,64);
> this.graphics.lineTo(31,10);
> this.graphics.lineTo(31,0);
> this.graphics.endFill(); this.moveleft = false;
> this.moveright = false;
> this.speed = 3;
> }
> }
>
>
> class App
> {
> var player:Spaceship;
> var shots:Array;
> var mobs:Array;
> var mobshots:Array;
>
> var updatetimer:Timer;
>
> var canvas:Sprite;
>
> var shottimer:UInt;
>
> public function new(w:Int, h:Int)
> {
> // Set up the board
> canvas = new Sprite();
> canvas.graphics.beginFill( 0xffffffff, 0x0 );
> canvas.graphics.drawRect( 0, 0, w, h );
> canvas.graphics.endFill();
> Lib.current.addChild(canvas);
>
> // Set up the pieces on the board
> player = new Spaceship();
> player.x = ( w / 2 ) - ( player.width / 2);
> player.y = h - player.height - ( h / 20 );
>
> shots = new Array();
> shottimer = 0;
>
> mobs = new Array();
> mobshots = new Array();
>
> var mob1 = new Mob();
> mobs.push(mob1);
>
> canvas.addChild(player);
> for ( i in mobs )
> {
> canvas.addChild( i );
> }
>
> // Handlers
> updatetimer = new Timer( 10, 0 ); // T=10ms => f=
> updatetimer.addEventListener(TimerEvent.TIMER, update);
> updatetimer.stop();
>
> Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyhandler);
> Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, keyhandler);
> }
> public function start()
> {
> updatetimer.start();
> }
> public function stop()
> {
> updatetimer.stop();
> }
> public function pause()
> {
> if ( updatetimer.running == true ) updatetimer.stop();
> else updatetimer.start();
> }
> private function keyhandler(evt:KeyboardEvent)
> {
> switch( evt.keyCode )
> {
> case 37:
> // left
> if ( evt.type == KeyboardEvent.KEY_DOWN ) player.moveleft = true;
> else if ( evt.type == KeyboardEvent.KEY_UP ) player.moveleft = false;
> case 39:
> // right
> if ( evt.type == KeyboardEvent.KEY_DOWN ) player.moveright = true;
> else if ( evt.type == KeyboardEvent.KEY_UP )
> player.moveright = false;
> case 32:
> // space, shoot
> if ( evt.type == KeyboardEvent.KEY_DOWN && shottimer == 0)
> {
> shottimer = 30;
> var newshot = new Shot();
> newshot.x = ( player.x + ( player.width / 2) ) - (
> newshot.width / 2);
> newshot.y = player.y - 1;
> this.canvas.addChild( newshot );
> this.shots.push( newshot );
> }
> }
> }
> private function update(evt:TimerEvent)
> {
> // Move player
> if ( player.moveleft == true ) {
> player.x -= player.speed;
> if ( player.x < 0 ) player.x = 0;
> }
> if ( player.moveright == true ) {
> var xmax = canvas.width - player.width;
> var newx = player.x + player.speed;
> if ( newx > xmax ) player.x = xmax;
> else player.x = newx;
> }
>
> // Move shots
> if ( shottimer >= 1 ) shottimer--;
> for ( s in this.shots )
> {
> s.y -= s.speed;
> if ( s.y < (0-s.height) )
> {
> this.canvas.removeChild( s );
> this.shots.remove( s );
> s = null;
> Reflect.deleteField(s, "");
> }
> }
>
> // Move mobs
> for ( i in this.mobs )
> {
> // Shoot?
> if ( Math.random() < 0.02 )
> {
> var ms = new MobShot();
> ms.x = i.x + ( i.width / 2 ) - ( ms.width / 2 );
> ms.y = i.y + i.height + 1;
> this.mobshots.push( ms );
> this.canvas.addChild( ms );
> }
> // Movement
> switch( i.direction )
> {
> case "LEFT":
> i.x--;
> if ( i.x <= 0 ) {
> i.x = 0;
> i.direction = "RIGHT";
> }
> case "RIGHT":
> i.x++;
> var xmax = canvas.width - i.width;
> if ( i.x >= xmax )
> {
> i.x = xmax;
> i.direction = "LEFT";
> }
> default:
> }
> }
> // Move mobshots
> for ( msh in this.mobshots )
> {
> msh.y += msh.speed;
> if ( msh.y > canvas.height + msh.height )
> {
> this.canvas.removeChild( msh );
> this.mobshots.remove( msh );
> msh = null;
> Reflect.deleteField(msh, "");
> }
> }
>
> // Check for collisions
> // Playershot - Mob
> for ( sh in this.shots )
> {
> for ( mo in this.mobs )
> {
> var col = this.checkForCollision( sh, mo, 0 );
> //if ( col != null ) trace("HIT!");
> }
> }
> // Mobshot - Player
> for ( msho in this.mobshots )
> {
> var col = this.checkForCollision( msho, player, 0);
> //if ( col != null ) trace ("BANG, YOU'RE DEAD!");
> }
>
> // Update
> evt.updateAfterEvent();
> }
> private function checkForCollision( p1:MovieClip, p2:MovieClip,
> alphatolerance:Int ) : Rectangle
> {
> var bounds1 = p1.getBounds( Lib.current );
> var bounds2 = p2.getBounds( Lib.current );
> if ( ( ( bounds1.right < bounds2.left ) || ( bounds2.right <
> bounds1.left ) ) || ( ( bounds1.bottom < bounds2.top) || (
> bounds2.bottom < bounds1.top ) ) )
> {
> return null;
> }
> var bounds = new Rectangle();
> bounds.left = Math.max( bounds1.left, bounds2.left );
> bounds.right = Math.min( bounds1.right, bounds2.right );
> bounds.top = Math.max( bounds1.top, bounds2.top );
> bounds.bottom = Math.min( bounds1.bottom, bounds2.bottom );
>
> var img:BitmapData = new BitmapData( cast( bounds.width,Int
> ), cast( bounds.height,Int ), false );
> var mat = p1.transform.concatenatedMatrix;
> mat.tx -= bounds.left;
> mat.ty -= bounds.top;
> img.draw( p1, mat, new ColorTransform( 1, 1, 1, 1, 255, -255,
> -255, alphatolerance ) );
>
> mat = p2.transform.concatenatedMatrix;
> mat.tx -= bounds.left;
> mat.ty -= bounds.top;
> img.draw( p2, mat, new ColorTransform( 1, 1, 1, 1, 255, 255,
> 255, alphatolerance ), BlendMode.DIFFERENCE );
>
> var intersection:Rectangle = img.getColorBoundsRect(
> 0xFFFFFFFF, 0xFF00FFFF );
> if ( intersection.width == 0 ) { return null; }
>
> intersection.x += bounds.left;
> intersection.y += bounds.top;
> return intersection;
> }
> }
>
> class Main
> {
> public function new()
> {
> Lib.current.addEventListener( Event.ENTER_FRAME, firstframe );
> }
> static function main()
> {
> Lib.current.stage.scaleMode = StageScaleMode.NO_SCALE;
> var self = new Main();
> }
> public function firstframe(evt:Event)
> {
> Lib.current.removeEventListener( Event.ENTER_FRAME, firstframe );
> var app = new App( Lib.current.stage.stageWidth,
> Lib.current.stage.stageHeight );
> app.start();
> }
> }
>
>
--
edA-qa mort-ora-y
Idea Architect
http://disemia.com/
Sign: Please digitally sign your emails.
Encrypt: I'm also happy to receive encrypted mail.
<< signature.asc >>
--
haXe - an open source web programming language
http://haxe.org
--
We've Got Your Name at http://www.mail.com !
Get a FREE E-mail Account Today - Choose From 100+ Domains
More information about the Haxe
mailing list