Question

I'm new to Flash Actionscript 3.0 and object programming in general. I'm trying to create a simple game, which is drawing a shape based on steering.

public class Player extends Shape
{
    public var X,Y,v,vX,vY,size,a,r:Number;
    public var k,counter,leftKey,rightKey,_color:uint;  
    public var line:Shape = new Shape();
    public var dot:Shape = new Shape(); 

  /*...*/
  /*constructor, giving values to variables here, not important*/       
  /*...*/

   public function Move():void
    {

        a=a+0.05*k; 
       //player controls k parameter k=0 by default 
       //k=1 when right key pressed
       //k=-1 when left key pressed

        vX=v*Math.cos(a);
        vY=v*Math.sin(a);

        X=X+vX;
        Y=Y+vY;

        dot.x=X+vX*size/(2*v);
        dot.y=Y+vY*size/(2*v);

        if (counter==0)
        {
            line.graphics.lineTo(X,Y);
            if (Math.random()<0.008) counter=12;
        } else 
        {
            line.graphics.moveTo(X, Y);
            counter--;
        }
    }

}

Function Move is in my Player class, which is called from inifinite TimerEvent function in my Main Class

public function mainLoop(TimerEvent:Event):void
{           
    for (var i:uint=0; i<players; i++) player[i].Move();
}

It seems to be working well at the beginning but after some time CPU usage raises dramatically and game becomes unplayble. I belivie it's caused by my shape (line) getting more and more complex.

Is there some reasonable way to optimize it? Can I somehow draw a line in less consuming way? I tried to convert it to bitmap but that looked ugly and didn't really help.

Thanks and cheers!

Was it helpful?

Solution

You're right in assuming that your slowdown in coming from your shape code - vector data is redrawn every frame in flash, so the more complex it is, the longer it takes to draw. Some solutions, depending on what you're willing to do:

  • Your fidelity is way to high - you're calling your Move function every frame; you probably don't need it that high, as the difference in movement since the last frame is probably less than a pixel. Sample your position every X frames instead (where X is the level of fidelity your willing to go down to). This can be done with a simple counter in the enter frame
  • If you don't need to keep the entire history of the drawing, put all your points into an array/vector, culling the length as needed. Then every frame, do a line.graphics.clear() and just draw the points in the array
  • If you do need to keep the entire history, then keep a BitmapData under your line (e.g. the size of the stage). Every so often, draw the line to the BitmapData and call clear() on your graphics to get right of the vector data. You shouldn't notice any loss in quality (set smoothing to true when you're drawing)

I'd do the first point in any case, then choose between the second and third, depending on your use case

OTHER TIPS

Expanding my comment, try something like this:

public class Player extends Shape
{
    public var X,Y,v,vX,vY,size,a,r:Number;
    public var k,counter,leftKey,rightKey,_color:uint;  
    public var line:Shape = new Shape();
    public var dot:Shape = new Shape(); 

  /*...*/
  /*constructor, giving values to variables here, not important*/       
  /*...*/
    public function Player(){
        //draw shapes
        graphics.lineStyle(1);
        graphics.drawCircle(0,0,r);
        graphics.lineTo(size,0);//can't test this now, but make sure the line is in the same direction as rotation 0 (guessing it's to the right)
        //your other constructor code here
    }
   public function Move():void
    {

        a=a+0.05*k; 
       //player controls k parameter k=0 by default 
       //k=1 when right key pressed
       //k=-1 when left key pressed

        vX=v*Math.cos(a);
        vY=v*Math.sin(a);

        X=X+vX;
        Y=Y+vY;

        x=X+vX*size/(2*v);
        y=Y+vY*size/(2*v);
        rotation = a * 57.2957795;//quick'n'dirty radians to degrees
    }

and if you want to draw the trails you can try something like this:

var canvas:Bitmap = new BitmapData(state.stageWidth,stage.stageHeight,true,0xFF000000);
var ct:ColorTransform = new ColorTransform(1,1,1,.1);   
public function mainLoop(TimerEvent:Event):void
{           
    for (var i:uint=0; i<players; i++) {
        player[i].Move();
        canvas.draw(player[i],player[i].transform.concatenatedMatrix,ct);
    }
}

Hope this makes sense.

Update Here is a standalone code snippet to illustrate the idea above(which has untested syntax):

package {
    import flash.display.Bitmap;
    import flash.geom.ColorTransform;
    import flash.display.BitmapData;
    import flash.text.TextField;
    import flash.ui.Keyboard;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.utils.Dictionary;
    import flash.display.Sprite;
    public class PlayerMoveTest extends Sprite {
        private var keys:Dictionary = new Dictionary();
        private var players:Vector.<Player> = new Vector.<Player>();
        private var trails:BitmapData;
        private var fade:ColorTransform = new ColorTransform(1,1,1,.1);
        public function PlayerMoveTest() {
            addEventListener(Event.ADDED_TO_STAGE,init);
        }
        private function init(e:Event):void{
            trails = new BitmapData(stage.stageWidth,stage.stageHeight,true,0x00FFFFFF);
            addChild(new Bitmap(trails));
            for(var i:int = 0 ; i < 2; i++){
                var p:Player = addChild(new Player(10+i*10)) as Player;
                p.x = stage.stageWidth * .5;
                p.y = stage.stageHeight * .5;
                players.push(p);
            }
            stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP,onKeyUp);
            stage.addEventListener(Event.ENTER_FRAME,update);
        }
        private function onKeyDown(e:KeyboardEvent):void{
            keys[e.keyCode] = true;
        }
        private function onKeyUp(e:KeyboardEvent):void{
            keys[e.keyCode] = null;
        }
        private function update(e:Event):void{
            if(keys[Keyboard.LEFT] != undefined)  {players[0].a -= .05;players[1].a += .05;}
            if(keys[Keyboard.RIGHT] != undefined) {players[0].a += .05;players[1].a -= .05;}
            if(keys[Keyboard.UP] != undefined)    {players[0].s += .15;players[1].s -= .15;}
            if(keys[Keyboard.DOWN] != undefined)  {players[0].s -= .15;players[0].s += .15;}
            for(var i:int = 0 ; i < players.length; i++) {
                players[i].move();
                trails.draw(players[i],players[i].transform.concatenatedMatrix,fade);
            }
        }
    }

}
import flash.display.*;
class Player extends Shape{
    public var vx:Number,vy:Number,a:Number,size:Number,r:Number,s:Number;
    public function Player(size:Number){
        init(size);
    }
    private function init(size:Number):void{
       vx = vy = a = s = 0;
       this.size = size;
       this.r = size * .25;
       graphics.lineStyle(1);
       graphics.drawCircle(0,0,r);
       graphics.lineTo(size,0);     
    }
    public function move():void{
        rotation = a * 57.2957795;
        vx = Math.cos(a) * s;
        vy = Math.sin(a) * s;
        x += vx;
        y += vy;
        if(x < 0) x = 0;
        if(y < 0) y = 0;
        if(x > stage.stageWidth) x = stage.stageWidth-width;
        if(y > stage.stageHeight) y = stage.stageHeight-height;
    }
}

You can test this code here and here's a preview:

preview

Use the arrow keys to drive(up arrow accelerates, left/right steer). The first player is the smaller one, having the correct controls, the other is simply mirroring the previous controls)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top