Flash AS3 (shape class) CPU usage and optimization - actionscript-3

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!

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

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:
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)

Related

AS3 Projectile moves incorrectly

So I'm currently attempting to make a prototype for a Bullet Hell game and I've run into a bit of a dead end.
So far I can move my player perfectly, the boss moves back and forth as he is supposed to, however the projectiles have some funny behaviour. Basically, when the boss moves left/right, so do the projectiles as if they are stuck to him. They move on the y as they are supposed to, except they stop just short of the player and move no further, so I'm hoping anyone can take a look at my code and give me a hand with what's going on.
Note: Ignore the rotation stuff, that's for later implementation, I was just laying the ground work.
Projectile.as
package
{
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
public class Projectile extends MovieClip
{
private var stageRef:Stage;
private var _xVel:Number = 0;
private var _yVel:Number = 0;
private var rotationInRadians = 0;
private const SPEED:Number = 10;
public function Projectile(stageRef:Stage, x:Number, y:Number, rotationInDegrees:Number)
{
this.stageRef = stageRef;
this.x = x;
this.y = y;
this.rotation = rotationInDegrees;
this.rotationInRadians = rotationInDegrees * Math.PI / 180;
}
public function update():void
{
this.y += SPEED;;
if(x > stageRef.stageWidth || x < 0 || y > stageRef.stageHeight || y < 0)
{
//this.removeChild(this); <- Causing a crash, will fix later
}
}
}
}
Boss.as
package
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Boss extends MovieClip
{
private var stageRef:Stage;
private var _vx:Number = 3;
private var _vy:Number = 3;
private var fireTimer:Timer;
private var canFire:Boolean = true;
private var projectile:Projectile;
public var projectileList:Array = [];
public function Boss(stageRef:Stage, X:int, Y:int)
{
this.stageRef = stageRef;
this.x = X;
this.y = Y;
fireTimer = new Timer(300, 1);
fireTimer.addEventListener(TimerEvent.TIMER, fireTimerHandler, false, 0, true);
}
public function update():void
{
this.x += _vx;
if(this.x <= 100 || this.x >= 700)
{
_vx *= -1;
}
fireProjectile();
projectile.update();
}
public function fireProjectile():void
{
if(canFire)
{
projectile = new Projectile(stageRef, this.x / 200 + this._vx, this.y, 90);
addChild(projectile);
canFire = false;
fireTimer.start();
}
}
private function fireTimerHandler(event:TimerEvent) : void
{
canFire = true;
}
}
}
Edit: Current suggestions have been to do the following:
stage.addChild(projectile); and this.parent.addChild(projectile); both which have the projectile firing from the top left corner (0, 0) and not constantly firing from the current center of the Boss.
The other issue, which has been untouched, is the fast that the projectile stops moving after a certain point and remains on the screen.
Another Edit:
After commenting out the code with the timer I have found that the projectile stops moving entirely. The reason why it was stopping after a certain amount of time was due to the timer, when the timer elapsed the projectile stopped and another would fire.
So now I need the projectile to constantly fire and move until it hits the edge of the screen, any ideas?
The problem is you are 'addChild'ing your projectiles to your Boss as opposed the stage (or the same display level as your Boss). When your Boss moves, your projectiles will move relative to him (ie, when he moves sideways, so will they).
When your boss fires a projectile, use a custom event to trigger a fireProjectile method in the Class that is your Boss' display parent. Instantiate your projectiles there and addChild them to the same object to which you addChild your Boss (possibly the stage?).
Alternatively, if you don't want to use a custom event, in your current fireProjectile method change the addChild line to:
this.parent.addChild(projectile);
This will add projectiles to the parent object of your Boss. Although that line seems, slightly, like cheating to me.

AS3 - Remove a Specific Instance of a MovieClip with removeChild() Function

I'm working on a Flash game for an assignment. It's a pretty standard missile defense-type game, with a rotating missile launcher in the center firing up at passing bombers above. The missiles and bombers are functioning correctly by themselves, but I'm running into a problem when I try to get the two to interact. Specifically, when a missile hits a bomber, I want the specific instances of that missile and that bomber to be removed from the screen and have their respective event listeners removed, but everything I've tried has failed and I can't seem to figure out just how to do it.
Here are the Main, Bomber, and Missile classes I'm working with:
The Main Class:
package {
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class Main extends MovieClip {
public var background:Background;
public var launcher:Launcher;
public var mount:Mount;
public var missile:Missile;
public var salvo:Array = [];
public var bomber:Bomber;
public var squadron:Array = [];
/*
* This function sets up the permanent items (items that remain on-stage for
* the duration of the game) and adds event listeners that call functions to
* add non-permanent items to the stage
*/
public function Main() {
// Add background to the stage
background = new Background(stage);
stage.addChild(background);
stage.setChildIndex(background, 0);
// Add the rotating launcher to the stage
launcher = new Launcher(stage);
stage.addChild(launcher);
stage.setChildIndex(launcher, 1);
// Add the static mount to the stage (on top of launcher)
mount = new Mount(stage);
stage.addChild(mount);
stage.setChildIndex(mount, 2);
// Call loop() every new frame
stage.addEventListener(Event.ENTER_FRAME, loop);
// Call fire() every time the mouse is clicked
stage.addEventListener(MouseEvent.CLICK, fire);
}
/*
* This function runs every time the program enters a new frame, or 60 times
* every second. Each time this function runs, it tries to add a new Bomber
* to the squadron array and checks to see if there are any Missiles or
* Bombers currently in their respective arrays (and if so, calls a function
* to make them move).
*/
public function loop(evt:Event) {
// If the random number generated by Math.random() is less than
// waveLimiter, create a new Bomber and add it to the squadron array.
if(Math.random() < 0.02 /* Change this number to change how fast bombers spawn */) {
bomber = new Bomber(stage);
bomber.addEventListener(Event.REMOVED_FROM_STAGE, removeBomber); // If the Bomber is removed from the stage, call removeBomber() to remove its event handler.
squadron.push(bomber);
stage.addChild(bomber);
stage.setChildIndex(bomber, 1);
}
// Check to see if there is at least one missile in the salvo array, and
// if so, call Missile.velocity() to make it move.
if(salvo.length > 0) {
for(var i:int = salvo.length - 1; i >= 0; i--) {
salvo[i].velocity();
}
}
// Check to see if there is at least one bomber in the squadron array,
// and if so, call Bomber.fly() to make it move.
if(squadron.length > 0) {
for(var j:int = squadron.length - 1; j >= 0; j--) {
squadron[j].fly();
}
}
}
/*
* This function checks for a mouse click, and if it detects one, creates a
* new Missile and adds it to the salvo array.
*/
public function fire(evt:MouseEvent) {
missile = new Missile(stage, launcher.rotation);
missile.addEventListener(Event.REMOVED_FROM_STAGE, removeMissile); // If the Missile is removed from the stage, call removeMissile() to remove its event handler.
salvo.push(missile);
stage.addChild(missile);
stage.setChildIndex(missile, 1);
}
/*
* This function removes the EVENT LISTENER for the current Missile instance.
* It does not remove the Missile itself from the stage.
*/
public function removeMissile(evt:Event):void {
evt.currentTarget.removeEventListener(Event.REMOVED_FROM_STAGE, removeMissile);
salvo.splice(salvo.indexOf(evt.currentTarget), 1);
}
/*
* This function removes the EVENT LISTENER for the current Bomber instance.
* It does not remove the Bomber itself from the stage.
*/
public function removeBomber(evt:Event) {
evt.currentTarget.removeEventListener(Event.REMOVED_FROM_STAGE, removeBomber);
squadron.splice(squadron.indexOf(evt.currentTarget), 1);
}
}
}
The Bomber class:
package {
import flash.display.Stage;
import flash.display.MovieClip;
public class Bomber extends MovieClip {
var stageInstance:Stage;
var randomNumber:Number = Math.round(Math.random() * 1);
public function Bomber(stageInstance:Stage):void {
this.stageInstance = stageInstance;
if(randomNumber == 1) {
x = -39;
y = (Math.random() * 120) + 30;
}
else if(randomNumber == 0) {
scaleX *= -1;
x = 679;
y = (Math.random() * 120) + 30;
}
}
public function fly():void {
if(randomNumber == 1) {
x = x + 4;
}
else if(randomNumber == 0) {
x = x - 4;
}
if(x > 680 || x < -40) {
this.parent.removeChild(this);
}
}
}
}
The Missile Class:
package {
import flash.display.Stage;
import flash.display.MovieClip;
public class Missile extends MovieClip {
var stageInstance:Stage;
var velocityX:Number;
var velocityY:Number;
var speed:Number = 10;
var rotationRadians:Number;
var rotationDegrees:Number;
public function Missile(stageInstance:Stage, rotationDegrees:Number):void {
this.stageInstance = stageInstance;
x = 320;
y = 363;
rotation = rotationDegrees;
rotationRadians = rotationDegrees * Math.PI / 180;
}
public function velocity():void {
velocityX = Math.cos(rotationRadians) * speed;
velocityY = Math.sin(rotationRadians) * speed;
x += velocityX;
y += velocityY;
if(x > 640 || x < 0 || y > 480 || y < 0) {
this.parent.removeChild(this);
}
}
}
}
In the Main Class, I've tried adding something like this:
if(squadron.length > 0) {
for(var j:int = squadron.length - 1; j >= 0; j--) {
squadron[j].fly();
if(salvo.length > 0) {
if(missile.hitTestObject(squadron[j])) {
this.parent.removeChild(this);
}
}
}
}
But no luck. I've also tried using a trace statement, and it doesn't even give me an output, which leads me to think it's not even detecting collision at all. Any ideas?
Update: I added a few more details.
this represents the object on which the function is called. So
this.parent.removeChild(this); makes no sense when it is written in the main class.
When you write it in the Missile Class, this is the Missile instance, and this.parent is the stage.
Try replacing it with: stage.removeChild(missile), in the last sample of code you posted, and call removeMissile() just after.
Try to use stage.removeChild instead this.parent.removeChild(this);
Actually you have many problems in your code. First of all, you don't need to work with Stage. You can work with your main container Main. When you add object to the display list, don't do after setChildIndex. In your code It doesn't have any sense. Also you don't need any length conditions. And create light objects by extending Sprite, not MovieClip.
Code for your loop, for missiles:
private function loop(e: Event):void {
//...not full listing
var i:uint, j:uint, salvos:uint = salvo.length, bombers:uint = squadron.length, missile:Missle, bomber:Bomber;
var disposeMissiles:Array = [];
var disposeBombers:Array = [];
//Rendering missiles
for (i = 0; i < salvos; ++i) {
missile = salvo[i];
missile.valocity();
for (j = 0; j < bombers; ++j) {
bomber = squadron[j];
if (!bomber.isHitted() && missile.hitTestObject(bomber)) {
//Dispose both missile and bomber
bomber.setHitted = true;
disposeMissiles.push(missile);
disposeBombers.push(bomber);
}
}
}
//Clear lists and display list
disposeObjects(disposeMissiles, salvo);
disposeObjects(disposeBombers, squadron);
}
private function disposeObjects(objects:Array, from:Array):void {
//Create interface for both missiles and bombers, like IGameActor
var i:uint, len:uint = objects.length, objectToRemove:IGameActor;
for (i; i < len; ++i) {
objectToRemove = objects[i];
//Remove from the display list, in your design Parent is Stage
this.stage.removeChild(DisplayObject(objectToRemove));
//Release memory, links, event listeners
objectToRemove.dispose();
//Try manage also indexes, splice is slow operation
from.splice(from.indexOf(objectToRemove), 1);
}
}

I need to get the object circles to continue repeat going down the screen, but I'm having trouble finding the right code

import flash.display.MovieClip;
import flash.events.Event;
public class rainfall extends MovieClip {
public function rainfall() {
// rainfall
var i:int;
for (i = 0; i< 50; i++)
{
//variables
var mc:MovieClip = new MovieClip ();
//theStage, and alpha properties
mc.x = Math.random() * stage.stageWidth ;
mc.y = Math.random() * 400 * 4 ;
mc.alpha = Math.random()* 2;
mc.graphics.beginFill(0x0000FF);
mc.graphics.drawCircle(0,0,20);
//trace
trace(i);
addChild(mc);
mc.addEventListener(Event.ENTER_FRAME, moveDown) ;
}
function moveDown(e:Event):void
{ //fall speed
e.target.y += 1 ;
}
}
having a lot of trouble trying to figure out how to get the circles to repeat down the screen in a continueous loop, I'm fairly new to actionscript 3 but any tips on what im doing wrong or what I need to get it to loop threw
Try something like this:
import flash.display.Sprite;
import flash.display.Shape;
import flash.display.DisplayObject;
import flash.events.Event;
package{
public class Rainfall extends Sprite {//class names should be title case
private var numDrops = 50;//this make it easier to configure
private var dropRadius = 20;
private var maxY;
public function Rainfall() {
addEventListener(Event.ADDED_TO_STAGE,init);//make sure the stage property will not be null
}
private function init(event:Event):void{
maxY = stage.stageHeight;//getters can be slow, store the height so it can be reused for each drop reset
var i:int;
for (i = 0; i < numDrops; i++){
var mc:Shape = new Shape();//if no interactivity is needed, Shape is the simplest/lightest class to use for drawing
mc.x = Math.random() * stage.stageWidth ;
mc.y = Math.random() * 400 * 4 ;
mc.alpha = 0.1 + Math.random() * 0.9;//alpha values are from 0 to 1.
mc.graphics.beginFill(0x0000FF);
mc.graphics.drawCircle(0,0,dropRadius);
addChild(mc);
}
addEventListener(Event.ENTER_FRAME, moveDown) ;
}
private function moveDown(e:Event):void
{ //fall speed
for(var i:int = 0 ; i < numDrops; i++){
var drop:DisplayObject = getChildAt(i);
drop.y += 1 ;
if(drop.y > maxY) drop.y = -dropRadius;//if the drop exits the screen downards, place it back at the top (above the visible area)
}
}
}
}
It's not tested code, so you might run into syntax errors, but the ideas are commented:
You need to use a conditional for your problem: if the drop's vertical position is greater than the stage's height, then the drop's vertical position should reset back to the top of the stage(`if(drop.y > maxY) drop.y = -dropRadius;a)
Although not necessary if you're just getting started, here are a few tips on efficiency/speed when working in flash:
MovieClip is a dynamic class (you can add properties to instances on the fly) but also has a cost. Since you only need to render/draw elements and no events or children are needed, this makes your circles perfect candidates for using the Shape class. Also, your main class can be a Sprite since you're not using a timeline
Getters and setters can be a bit slow in actionscript. It's a healthy habbit to cache/store values that don't change much over time for reuse as local variables for faster access.
Not as much a performance issue:a common pitfall is not having the stage initialized on a display object, resulting in an annoying and for beginners puzzling null object reference errors. If you use the stage property on a DisplayObject(Shape/Sprite/MovieClip), it's best to make sure it's been added to the stage (and the stage property isn't null) using the ADDED_TO_STAGE event
Good luck!

A loop in enterframe?

I'm animating a bunch of words in AS3. Because I'm going to be using this on a mobile device, I want to use bitmaps rather than Sprites. So I've created WordObjects, which have a .bitmap property that I can access.
I have the following code, which fires on the click event and loops through an array inside an enterframe event. This is probably a bad idea, but I'm not sure how to do it better. (What is surprising is that it runs just fine in Flashbuilder, but slows to a crawl in Flash CS5.)
Is there some better way to do this? I just want an efficient way to animate the array of bitmaps.
private function clickhandler (e:MouseEvent){
this.addEventListener(Event.ENTER_FRAME, blowemup);
}
private function blowemup(e:Event){
var newPosition:Number;
for(var i:int=0; i<arrWordObjects.length; i++)
{
newPosition = updatePosition(arrWordObjects[i].bitmap);
arrWordObjects[i].bitmap.x += newPosition;
arrWordObjects[i].bitmap.y += getRandomNumber();
}
}
Something that will make a huge difference is using for each(Object in Array) rather than the standard for loop.
private function blowemup(e:Event):void
{
var newPosition:Number;
var i:ArrWordsObjectClass; // <-- don't know what the class for this is, just replace
for each(i in arrWordObjects)
{
newPosition = updatePosition(i.bitmap);
i.bitmap.x += newPosition;
i.bitmap.y += getRandomNumber();
}
}
A for each loop is typed, meaning a lot of time is saved where normally it'd be trying to work out what arrWordObjects[i] is every iteration.
Also, side note: using one ENTER_FRAME driven function and looping through everything in your application that you want to handle each frame is much more efficient than applying hundreds of listeners for objects.
I normally create a handler class that contains the ENTER_FRAME and an array storing my objects, like so:
package
{
import flash.events.Event;
import flash.display.Sprite;
public class Handler extends Sprite
{
// vars
public var elements:Array = [];
/**
* Constructor
*/
public function Handler()
{
addEventListener(Event.ENTER_FRAME, _handle);
}
/**
* Called on each dispatch of Event.ENTER_FRAME
*/
private function _handle(e:Event):void
{
var i:Element;
for each(i in elements)
{
i.step();
}
}
}
}
Then I create a base class for all the objects that I want to handle, containing the step() function called above.
package
{
import flash.display.DisplayObject;
public class Element extends Object
{
// vars
public var skin:DisplayObject;
/**
* Called on each dispatch of Event.ENTER_FRAME at Handler
*/
public function step():void
{
// override me
}
}
}
Now just extend Element with your objects:
package
{
import flash.display.Sprite;
public class MyThing extends Element
{
/**
* Constructor
*/
public function MyThing()
{
skin = new Sprite();
skin.graphics.beginFill(0);
skin.graphics.drawCircle(0,0,40);
skin.graphics.endFill();
}
/**
* Override step
*/
override public function step():void
{
skin.x += 4;
}
}
}
And get it all going!:
var handler:Handler = new Handler();
var m:MyThing;
var i:uint = 0;
for(i; i<10; i++)
{
m = new MyThing();
m.y = Math.random()*stage.stageHeight;
handler.elements.push(m);
addChild(m.skin);
}
How many bitmaps do you plan to have on the stage at a time?
I have had 40 900x16px bitmaps animating on the stage at full speed running on my iphone using air 2.6.
I used a foreach loop in an enterframe event which i added on mouseclick and removed once the animation was finished.
Remember to compile it for the mobile with gpu rendering enabled. (gpu in your app.xml if you are using air 2.6)
This is worth a read too, it explains a lot about performance for mobile devices
http://help.adobe.com/en_US/as3/mobile/WS901d38e593cd1bac-3d719af412b2b394529-8000.html
Here is a basic example of what I had...
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
[SWF(frameRate="30", backgroundColor="#FF00FF")]
public class Test extends Sprite
{
private var fields:Vector.<Bitmap> = new Vector.<Bitmap>();
public function Test()
{
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
for(var i:int = 0; i< 37; i++){
var bd:BitmapData = new BitmapData(960, 16, true, 0x000000);
bd.fillRect(new Rectangle(0, 0, 900, 16), Math.round( Math.random()*0xFFFFFFFF ));
var b:Bitmap = new Bitmap(bd);
b.x = 0;
b.y = i*16;
stage.addChild(b);
fields.push(b);
}
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private var inertia:Boolean = false;
private var yCurrent:Number;
private var ySpeed:Number;
private var startY:Number;
private var cy:Number = 0;
private function onEnterFrame(e:Event):void{
if(!inertia){
ySpeed = (startY - yCurrent) ; // / 16;
startY = yCurrent
} else {
ySpeed *= 0.8;
if(ySpeed < 0.01 && ySpeed > -0.01){
inertia = false;
stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
}
cy += ySpeed;
if(cy > 640)
cy -= 640;
var ty:Number = cy;
for each(var tf:Bitmap in fields){
tf.y = ty;
ty += 16;
if(ty > 640)
ty -= 640;
}
}
private function onMouseDown(e:MouseEvent):void{
inertia = false;
startY = e.stageY;
yCurrent = e.stageY;
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseMove(e:MouseEvent):void{
yCurrent = e.stageY;
}
private function onMouseUp(e:Event):void{
inertia = true;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
}
I would suggest looking at writing a custom effect on Adobe's website over registering for ENTER_FRAME event. What you've put up there means this code will forever run as long as the program is running. If you wanted to stop the effect or run for 10 frames and stop then you'll have to write more code. It gets even more complex if you want to apply this to several instances. You're going to have to resolve problems that custom effects framework solves.
I'd read how to write custom effects here:
http://livedocs.adobe.com/flex/3/html/help.html?content=createeffects_1.html

Actionscript: Am I deleting this class instance correctly?

Okay by deleting correctly I mean am I actually getting rid of the instance or is it just not being drawn anymore? I should mention that I'm trying to delete the instance from within its own class, that is it deletes itself. It 'works' in that the square it draws no longer appears on the screen but again I'm not sure if it's really gone or just not being drawn. Anyway here's the class:
package
{
import flash.display.*;
import flash.events.*;
public class OBJECT_bullet_1 extends Sprite
{
public var X:int = 0; public var Y:int = 0;
public var Y_SPEED:int = 5;
public var DEPTH:int = 9;
public var CONTAINER:Sprite = new Sprite();
public function CREATE(CONTAINER:Sprite,X:int,Y:int):void
{
this.CONTAINER = CONTAINER;
CONTAINER.stage.addEventListener(Event.ENTER_FRAME,STEP);
this.X = X; this.Y = Y;
DRAW();
}
public function STEP(event:Event):void
{
this.graphics.clear();
Y -= Y_SPEED;
if (Y < 20) {Y = 300; CONTAINER.removeChild(this); CONTAINER.stage.removeEventListener(Event.ENTER_FRAME,STEP); CONTAINER.(delete this); CONTAINER = null; return;}
DRAW();
}
public function DRAW():void
{
this.graphics.beginFill(0xCCCC00,1);
this.graphics.drawRect(X - 2,Y - 2,4,4);
this.graphics.endFill();
CONTAINER.addChild(this);
}
}
}
The part I'm concerned about is in the STEP function when it checks to see if Y < 20. You'll notice that it does several things afterwords. Am I deleting it correctly? If so is there anything I am doing to delete it that I don't need to?
Yes to both questions. To ensure an object is deleted, all you have to do is remove all references to it. The child reference and event callback are the only ones the above code is aware of, and you have taken care to remove them both. Nullifying your own container reference is unnecessary, as is whatever you think CONTAINER.(delete this) does.
There are some other significant problems with your supplied code. I made some improvements and heavily commented all changes to explain why I made them.
// You should avoid using the default package. Using the default package
// can make it difficult later on if you start having naming conflicts.
package com.stackoverflow.example {
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.utils.getTimer;
// Class names are spelled in CamelCase by convention. Also, note
// that "Object" has a special meaning in AS3 so you should avoid
// using it to refer to anything else. I used here "Entity" instead.
public class EntityBullet1 extends Sprite {
// ALLCAPS when used are reserved for static const names.
// A good use of static consts is to store "magic numbers".
public static const DEFAULT_COLOR:uint = 0xCCCC00;
public static const DEFAULT_SPEED_X:Number = 0;
public static const DEFAULT_SPEED_Y:Number = -100;
public static const DEFAULT_SIZE:Number = 4;
// I'm calculating the time between frames for smoother movement.
public var lastTime:int;
public var color:uint = DEFAULT_COLOR;
public var size:int = DEFAULT_SIZE;
// Instead of separate x and y vars, you can use the Point class.
public var pos:Point;
public var speed:Point;
// Instead of a "create" method do all creation inside the constructor!
public function EntityBullet1(x:Number = 0, y:Number = 0) {
pos = new Point(x, y);
speed = new Point(DEFAULT_SPEED_X, DEFAULT_SPEED_Y);
// You don't need the parent container to access the ENTER_FRAME
// event. Every DisplayObject has its own. Much simpler.
addEventListener(Event.ENTER_FRAME, firstStep);
}
public function draw():void {
// Keep all drawing inside the draw function. Previously,
// clear() was being called inside the step method.
graphics.clear();
graphics.beginFill(color);
graphics.drawRect(pos.x - size/2, pos.y - size/2, size, size);
graphics.endFill();
}
// On the first frame, the field "lastTime" is still uninitialized.
// This method initializes it to the current time and hands off
// future events to the proper step() method.
public function firstStep(event:Event):void {
removeEventListener(Event.ENTER_FRAME, firstStep);
addEventListener(Event.ENTER_FRAME, step);
lastTime = getTimer();
step(event);
}
public function step(event:Event):void {
// To move at a fixed rate regardless of how fast the framerate is,
// you need to calculate the time delta.
var cur:int = getTimer();
var delta:Number = (cur - lastTime) / 1000.0;
lastTime = cur;
// Position equals velocity times time.
pos.x += speed.x * delta;
pos.y += speed.y * delta;
draw();
// Note that all DisplayObjects already have references to their
// parent containers called "parent"!
if (pos.y < 20) {
if (parent != null) parent.removeChild(this);
removeEventListener(Event.ENTER_FRAME, step);
}
}
}
}