kill unnamed generated instances after time in flash (as3) - actionscript-3

I am making a little game, where instances are spawned and I want them to be killed after two seconds.
The problem I was running into is that the instances have a generated name, and I don't know how to talk to them after they have spawned.
I tried things like Timeout or a normal Timer, but I still can't talk to them.
function spawn(): void {
if (Math.floor(Math.random() * 70) == 0) {
plane = new Plane();
plane.x = Math.random() * (stage.stageWidth - 100) + 50;
plane.y = Math.random() * (stage.stageHeight - 100) + 20;
plane.addEventListener(MouseEvent.CLICK, shoot);
var killtimer: Timer = new Timer(2000);
killtimer.addEventListener(TimerEvent.TIMER, timerListener);
//setTimeout(kill, 2000);
addChild(plane);
killtimer.start();
}
if (Math.floor(Math.random() * 30) == 0) {
bird = new Bird();
bird.x = Math.random() * (stage.stageWidth - 100) + 50;
bird.y = Math.random() * (stage.stageHeight - 100) + 20;
bird.addEventListener(MouseEvent.CLICK, shoot);
//setTimeout(kill, 2000);
addChild(bird);
}
if (Math.floor(Math.random() * 300) == 0) {
g_bird = new Golden_bird();
g_bird.x = Math.random() * (stage.stageWidth - 100) + 50;
g_bird.y = Math.random() * (stage.stageHeight - 100) + 20;
g_bird.addEventListener(MouseEvent.CLICK, shoot);
//setTimeout(kill, 2000);
addChild(g_bird);
}
}
function timerListener(e: TimerEvent): void {
trace("Killtimer: " + flash.utils.getQualifiedClassName(e.currentTarget));
e.currentTarget.parent.removeChild(e.currentTarget); <- Problem e is the timer, not the instance
}
Can anybody help me?

I'll present you with two options.
1. (Easy-ish way) - Use dynamic properties and an Array (or Vector)
Create an Array or Vector to hold all your spawned items.
var items:Array = [];
Then, when you spawn your items, add them to the array/vector, and give them a made up property, let's call it duration and it will store the current time plus 2 seconds:
plane = new Plane();
items.push(plane);
plane.duration = flash.utils.getTimer() + 2000; //this is when the item expires and can be removed
Then, make ONE master timer that ticks 10 times per second (or however long you'd like)
var mainTimer:Timer = new Timer(100);
mainTimer.addEventListener(TimerEvent.TIMER, timerListener);
mainTimer.start();
Or instead of that, you could listen every frame: (more accurate, but less performant)
this.addEventListener(Event.ENTER_FRAME, timerListener);
In the timer tick handler (or enter frame handler), check the duration of each item and see if it needs to be removed yet:
function timerListener(e:Event):void {
//get the current time
var time:Number = flash.utils.getTimer();
//iterate backwards through all the items
for(var i:int=items.length-1;i--){
//if the current time is the same or greater
if(items[i]).time >= time){
removeChild(items[i]); //remove it from the screen
items.splice(i,0); //delete it from the array
}
}
}
2. Create a base class that does all the work
The best way to do this, would be to make a base class for all those objects who you wish to last a specific duration.
You could create a file called MyBaseClass.as next to your .fla. In that file, you could do something like this:
package {
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.Event;
import flash.events.TimerEvent;
public class MyBaseClass extends Sprite {
private var timer:Timer = new Timer(2000,1);
public function MyBaseClass():void {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStage, false, 0, true); timer.addEventListener(TimerEvent.TIMER, kill,false,0,true);
}
private function addedToStage(e:Event):void {
timer.start();
}
private function kill(e:Event):void {
if(parent) parent.removeChild(this);
}
}
}
Anything that extends this base class, will kill itself after 2 seconds of being added to the display.
To extend it, right click your assets in the library (in FlashPro), and in the "export for actionscript" settings, put MyBaseClass in the base class text field.
Of course there are other ways to accomplish this as well, and you could also do some combination of the two I've shown, as having just one timer is more efficient than every item having it's own timer running.
3. Use setTimeout (not ideal)
If want to just understand how you could use setTimeout, this would be the correct usage:
plane = new Plane();
setTimeout(kill, 2000, plane);
function kill(itemToKill:DisplayObject):void {
removeChild(itemToKill);
}

one more variant is to create closure as an event listener, which will enclose the value of a plane, and after being triggered would remove itself as an event listener, and remove the plane. Just like this:
function getKillFn(plane:Plane):Function {
return function handler(event:TimerEvent):void {
Timer(event.currentTarget).removeEventListener(TimerEvent.TIMER, handler);
plane.parent.removeChild(plane);
}
}
so all that you have to change in your code is to replace this line:
killtimer.addEventListener(TimerEvent.TIMER, timerListener);
with this one:
killtimer.addEventListener(TimerEvent.TIMER, getKillFn(plane));
Or you can even create this function inplace (in spawn function):
function spawn(): void {
if (Math.floor(Math.random() * 70) == 0) {
plane = new Plane();
plane.x = Math.random() * (stage.stageWidth - 100) + 50;
plane.y = Math.random() * (stage.stageHeight - 100) + 20;
plane.addEventListener(MouseEvent.CLICK, shoot);
var killtimer: Timer = new Timer(2000);
function killPlane(event:TimerEvent):void {
killTimer.removeEventListener(TimerEvent.TIMER, killPlane);
plane.parent.removeChild(plane);
}
killtimer.addEventListener(TimerEvent.TIMER, killPlane);
//setTimeout(kill, 2000);
addChild(plane);
killtimer.start();
}
...
and one more thing: since you create a new timer on each spawn call, you should stop it and recycle in a handler, or better use TimerEvent.COMPLETE, for it to stop automatically:
var killtimer: Timer = new Timer(2000, 1);
function killPlane(event:TimerEvent):void {
killTimer.removeEventListener(TimerEvent.COMPLETE, killPlane);
killTimer = null;
plane.parent.removeChild(plane);
}
killtimer.addEventListener(TimerEvent.COMPLETE, killPlane);
hope this'll help

Related

Adding additional time to main timer from movieclip?

Hi so yeah in the main timeline I have the timer
var count:Number = 300;//Count down from 300
var myTimer:Timer = new Timer(1000,count);
myTimer.addEventListener(TimerEvent.TIMER, sayHello);
function sayHello(e:TimerEvent):void
{
trace("Current Count: " + myTimer.currentCount);
}
And when you go into the movieclip reimoi_mcand click the useplush button I want to be able to add additional seconds onto the timer. The following is the code in the reimoi_mc clip but yeah I really have no idea how to make this work, please help ;0; (I have to use MovieClip(root) to access the running timer from the main timeline within the movieclip)
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.utils.getTimer;
stop();
useplush.addEventListener(MouseEvent.CLICK, addtime);
function addtime(e:MouseEvent):void
{
MovieClip(root).count += 2;
MovieClip(root).myTimer.repeatCount += MovieClip(root).count; //add time to the timer
trace("new time " + myTimer.currentCount);
}
I think what you are trying to do is add 2 seconds to the timer in the click handler, and then show how much time is left? If so, just a couple tweaks will do:
function sayHello(e:TimerEvent):void {
trace("Time Left: " + myTimer.repeatCount - myTimer.currentCount); //time left is the repeat count - the current count
}
function addtime(e:MouseEvent):void {
MovieClip(root).myTimer.repeatCount += 2 //add 2 more ticks to the timer (currentCount will always remain the same unless the timer is reset)
trace("new time remaining: " + MovieClip(root).myTimer.repeatCount - MovieClip(root).myTimer.currentCount);
}
BONUS CODE!
If you wanted to make it agnostic of the timer delay (let's say you want it to update quicker than 1 second for instance), you could do this:
var startingTime:Number = 20; //the initial time in seconds
var myTimer:Timer = new Timer(200); //your timer and how often to have it tick (let's say 5 times a second)
myTimer.repeatCount = startingTime * Math.ceil(1000 / myTimer.delay); //set the initial repeat count
myTimer.addEventListener(TimerEvent.TIMER, sayHello);
myTimer.start();
function sayHello(e:Event):void {
trace("Time Left: " + ((((myTimer.repeatCount - myTimer.currentCount) * myTimer.delay) / 1000)) + "seconds");
}
And in your other object:
stage.addEventListener(MouseEvent.CLICK, function(e:Event){
myTimer.repeatCount += Math.ceil(2000 / myTimer.delay); //add 2000 milliseconds to the timer
});
You'd better use an external counter to count the time, instead of stuffing it into a Timer object. You would then need timers to measure delays, and listeners to count them.
var myTimer:Timer=new Timer(1000); // no second parameter
public var secondsLeft:int=300; // former "count"
myTimer.addEventListener(TimerEvent.TIMER, sayHello);
function sayHello(e:TimerEvent):void {
secondsLeft--;
trace("Seconds left:", secondsLeft);
if (secondsLeft<=0) {
myTimer.stop();
myTimer.reset();
// whatever else to trigger when time runs out
}
}
And then you just add to secondsLeft and update the scoreboard.

Why are my array movie clips getting harder to click when they get faster?

I am making a game where insects come down from the top of the screen, and the user must kill them. The insects are in an array. Each time the user kills them, the score goes up..after a while the insects get faster and faster. When they get faster, some of them don't get killed when you click them. You have to click multiple times for them to die. I want them to get killed in one click, but this isn't working when they get faster!
function makeEnemies():void
{
var chance:Number = Math.floor(Math.random() * 150);
if (chance <= + level)
{
//Make sure a Library item linkage is set to Enemy...
tempEnemy = new Enemy();
//Math.random(); gets a random number from 0.0-1.0
tempEnemy.x = Math.round(Math.random() * 1000);
addChild(tempEnemy);
enemies.push(tempEnemy);
tempEnemy.speed = enemyBaseSpeed + ((level - 1) * speedLevelInc);
}
}
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy=enemies[i];
if (tempEnemy.dead)
{
score++;
score++;
roachLevel.score_txt.text = String(score);
enemies.splice(i,1);
}
else // Enemy is still alive and moving across the screen
{
//rotate the enemy between 10-5 degrees
tempEnemy.rotation += (Math.round(Math.random()*.4));
//Find the rotation and move the x position that direction
tempEnemy.x -= (Math.sin((Math.PI/180)*tempEnemy.rotation))*tempEnemy.speed;
tempEnemy.y += (Math.cos((Math.PI/180)*tempEnemy.rotation))*tempEnemy.speed;
if (tempEnemy.x < 10)
{
tempEnemy.x = 11;
}
if (tempEnemy.x > stage.stageWidth - offset)
{
tempEnemy.x = stage.stageWidth - offset;
}
if (tempEnemy.y > stage.stageHeight)
{
removeEnemy(i);
lives--;
roachLevel.lives_txt.text = String(lives);
}
}
}
}
function removeEnemy(id:int)
{
removeChild(enemies[id]);
enemies.splice(id,1);
}
There is also code inside the insect.
import flash.events.MouseEvent;
import flash.display.MovieClip;
import fl.motion.Animator;
import flash.events.*;
play();
var MainTimeLine = MovieClip(root);
var mysound:squish = new squish();
this.addEventListener(MouseEvent.CLICK, kill);
this.dead = false;
function kill(e:MouseEvent):void
{
this.dead=true;
mouseChildren=false
mysound.play();
gotoAndPlay(21);
this.removeEventListener(MouseEvent.CLICK, kill);
flash.utils.setTimeout(removeSelf,2000);
}
function removeSelf():void
{
this.parent.removeChild(this);
}
You shouldn't remove an enermy from the array while iterating it.
You make enemies.splice(i,1); in your loop iterating from enemies.length to 0. While you changing your array size, you don't adjust the loop condition.
I think your main issue might be that you are reusing your insects, maybe pooling them. If you do this, you need to make sure that you are adding the eventListener for the click again when recycling.
If you are adding the listener in the constructor, that will only execute when the insect is created, not when you recycle him.
Your issue is the setTimeOut(), which is causing a memory leak. Using a timer is much safer, but if you must use it, keep a reference to the call and clear it when you no longer need it.
Also, the code you posted doesn't show where you're adding a listener to MainTimeline or parent, but if you are you need to remove that as well before the insect can be garbage collected.

Relative coordinate issue in AS3

I've edited the following code in order to let those green rectangles to follow my cursor which is customized by a small rectangle. But I've encountered several problems:
Although I haven't defined any coordinate in the separate class, but the size is abviously wrong in the stage when publish with only half size for the cursor coordinate.
The reset button cannot be activated, although I've tested well in the other code.
Here is the work I've published: http://neowudesign.com/hwu_ex04.html
The code on timeline
//hw//Creating a new cursor
newcursor.startDrag ("true");
Mouse.hide();
//hw//Creating a holder to hold the butterfly objects
var mothHolder = new Sprite();
addChild(mothHolder);
//hw//Creating seven moths at the beginning
makeMoths(7);
//hw//creating a function which can generate limited numbers of moths.
function makeMoths(MothsNumber:Number)
{
for (var i = 0; i < MothsNumber; i++)
{
newMoth = new Moth();
mothHolder.addChild(newMoth);
}
}
//hw//Set the reset button at the top for clicking, but it's failed to work;
//hw//Set the cursor back to the default one, and remove the custom one when hovering;
mothHolder.setChildIndex(reset,mothHolder.numChildren);
reset.addEventListener(MouseEvent.MOUSE_OVER, cursorchange);
function cursorchange(event:MouseEvent):void
{
Mouse.show();
newcursor.visible = false;
trace("alert!!");
}
//hw//creating a function of reset
reset.addEventListener(MouseEvent.CLICK, resetClick, false, 0, true);
function resetClick(evt:MouseEvent):void
{
removeChild(mothHolder);
mothHolder = new MovieClip();
addChild(mothHolder);
var numMoths:Number = Math.round(Math.random() * 6) + 1;
trace("Moths Numeber: "+ numMoths);
makeButterflies(numButterflies);
}
//hw//when the cursor leave the reset region, it turns back to the customized one
reset.addEventListener(MouseEvent.MOUSE_OUT, fl_MouseOutHandler);
function fl_MouseOutHandler(event:MouseEvent):void
{
removeEventListener(MouseEvent.MOUSE_OVER, cursorchange);
Mouse.hide();
newcursor.visible = true;
}
And the code for class "Moth" separately named "angle.as"
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class angle extends MovieClip {
var speed:Number = 8;
function angle() {
//letting every moth follow the moving of the cursor
addEventListener(Event.ENTER_FRAME,mothMove);
function mothMove(myEvent:Event) {
trace(mouseX);
trace(mouseY);
var angle:Number = Math.atan2(mouseY - y, mouseX - x);
x += Math.cos( angle ) * speed;
y += Math.sin( angle ) * speed;
}
}
}
}

AS3 - If symbol's coordinates arrive here?

I'm using Flash Professional CS5.5 and I need to make an app where there is a ball (symbol) that moves using the accelerometer and I want that, when the ball coordinates A reach this coordinates B it goes to frame 2 (gotoAndPlay(2)). I have to find the ball coord first, right? How do I make this?
Here is the code I've now
c_ball.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void{
c_ball.startDrag();}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void{
c_ball.stopDrag();}
would it work if, after retriving the coordinates?
function f_level (e) if (c_ball.x==100 && c_ball.y==100) {
gotoAndStop(2);}
MOUSE_UP and MOUSE_DOWN are not what you need if you're looking for Accelerometer data. You want the Accelerometer class and associated events.
Try something like this:
import flash.sensors.Accelerometer;
import flash.events.AccelerometerEvent;
var accel:Accelerometer = new Accelerometer();
accel.addEventListener(AccelerometerEvent.UPDATE, handleAccelUpdate);
Update handler:
function handleAccelUpdate(e:AccelerometerEvent):void{
//inside this function you now have access to acceleration x/y/z data
trace("x: " + e.accelerationX);
trace("y: " + e.accelerationY);
trace("z: " + e.accelerationZ);
//using this you can move your MC in the correct direction
c_ball.x -= (e.accelerationX * 10); //using 10 as a speed multiplier, play around with this number for different rates of speed
c_ball.y += (e.accelerationY * 10); //same idea here but note the += instead of -=
//you can now check the x/y of your c_ball mc
if(c_ball.x == 100 && c_ball.y == 100){
trace("you win!"); //fires when c_ball is at 100, 100
}
}
Now this will let you "roll" your MC off the screen so you're probably going to want to add some kind of bounds checking.
Check out this great writeup for more info:
http://www.republicofcode.com/tutorials/flash/as3accelerometer/
An easy and save way is to use colission detection, instead of testing for exectly one position ( what is hard to meet for users) you go for a target area :
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Hittester extends Sprite
{
var ball:Sprite = new Sprite();
var testarea:Sprite = new Sprite();
public function Hittester()
{
super();
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,0,10);
testarea.graphics.beginFill(0x00ff00);
testarea.graphics.drawRect(0,0,50,50);
testarea.x = 100;
testarea.y = 100;
// if testarea should be invisble
/*testarea.alpha = 0;
testarea.mouseEnabled = false;
*/
ball.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
addChild(testarea);
addChild(ball);
}
private function startDragging( E:Event = null):void{
ball.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
private function stopDragging( E:Event = null):void{
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
ball.stopDrag();
test();
}
private function test():void{
if( ! ball.hitTestObject(testarea) ){
ball.x = 10;
ball.y = 10;
}
else{
// here goes next frame command ;)
}
}
}
}

How can I use Action Script 3.0 to make random placed Symbols fly by?

I'm trying to make a simple animation with Flash CS4 and Action Script 3.0 to make a number of Symbols fly by from right to left constantly. What I want is that once a symbol has reached the end of the screen it is destroyed and another one is placed at the start position.
I intend to give each symbol a random speed and create a random symbol each time one is 'destroyed'. Any clues where I can start?
As you seem new to flash as a platform I would think writing classes shouldn't be your first port of call when learning ActionScript. Definitely just play about on the timeline for now and learn the basics. As very simple solution to this, I would suggest creating a MovieClip in the library with a class name like 'MyBall'... then paste this onto the first frame of the main timeline et voila.
// Create some variables to store data
var numberOfBalls : int = 20;
var myBalls : Array = [];
var xVelocities : Array = [];
var maxXVelocitySpeed : Number = 5;
var minXVelocitySpeed : Number = 2;
// Add your orginal balls to the stage
for (var i : int = 0; i < numberOfBalls; i++)
{
var myBall : MyBall = new MyBall();
myBall.x = -(Math.random() * stage.stageWidth);
myBall.y = Math.random() * stage.stageHeight;
var xVelocity : Number = minXVelocitySpeed + (Math.random() * (maxXVelocitySpeed - minXVelocitySpeed));
myBalls.push(myBall);
xVelocities.push(xVelocity);
addChild(myBall);
}
// Add a listener for enter frame events
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//Run this code on every frame to move the balls and reposition them if they are off the stage
function enterFrameHandler(event : Event) : void
{
for each( var myBall : MyBall in myBalls)
{
var ballIndex : int = myBalls.indexOf(myBall);
myBall.x += xVelocity[ballIndex];
if (myBall.x > stage.stageWidth)
{
myBall.x = -(Math.random() * stage.stageWidth);
myBall.y = Math.random() * stage.stageHeight;
}
}
}
First, turn your symbols into MovieClips. Then create a base class MySymbol.as for your symbols, something like:
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class MySymbol extends MovieClip
{
public var speed:Number; // Pixels moved per frame
public function MySymbol(speed:Number, startPosition:Point)
{
this.speed = speed;
this.addEventListener(Event.ENTER_FRAME, update);
this.x = startPosition.x;
this.y = startPosition.y;
}
private function update():void
{
this.x -= this.speed;
if (this.x < 0 - this.width) { // We're at the left edge
this.removeEventListener(Event.ENTER_FRAME, update);
this.dispatchEvent(new Event(Event.COMPLETE));
}
}
}
}
Then make sure your movie clips are exported for AS3 (the "linkage" option on the item in the library). Make the class name for each item unique (e.g. MySymbol1, MySymbol2), and set the base class to MySymbol.
Your document class might look something like this:
package {
import flash.display.MovieClip;
import flash.events.Event;
import MySymbol; // Not strictly needed
public class DocumentClass extends flash.display.MovieClip
{
private static var SYMBOLS:Array = new Array(MySymbol1, MySymbol2);
public function DocumentClass()
{
// Create five symbols:
for (var i:int = 0; i < 5; i++) {
makeSymbol();
}
}
private function makeSymbol():void
{
// Pick a random symbol from the array:
var symType:Class = SYMBOLS[Math.random() * SYMBOLS.length];
// Construct the new symbol:
var loc:Point = new Point(stage.stageWidth, Math.random() * stage.stageHeight);
var sym:MySymbol = new symType(1 + Math.random() * 30, loc);
// Listen for the object hitting the left edge:
sym.addEventListener(Event.COMPLETE, remakeObject);
this.addChild(sym);
}
private function remakeObject(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, remakeObject);
this.removeChild(e.target);
// Replace the dead symbol:
makeSymbol();
}
}
}
It is a lot more efficient if instead of destroying and re-creating an object that flies off-stage you re-use the existing one and move it back to the right. But this is an optimization you can implement later, if things become slow.
Note that all the code above is UNTESTED and I have not coded AS3 in a while, so there's likely at least a few bugs in it. Hopefully it will serve as a good enough starting point.
Define a Circle (symbol) class that extends Sprite/Shape and has a velocity variable
Draw a circle (or whatever) with a random color
Math.floor(Math.random() * 0xffffff)
Assign a random value to velocity
minVelocity + Math.floor(Math.random() * velocityRange)
Create a start() method inside the Circle class that registers an enter frame handler
Increment this.y inside the enter frame handler, and dispatch a 'recycleMe' event if y is more than the max value.
Create N instances of Circle, addChild them, and call their start() methods.
listen to 'recycleMe' events on each of them, and reset the value of y from the handler.
Here's a few prompts to get you started.
MovieClips have an x and y property. If you were to add to these numbers over time you would see the MovieClip move along the x and/or y axis of the stage. Look into doing this using the Event.ENTER_FRAME which will allow you to change the values every time the screen is going to update.
Your stage will have a given width (a stageWidth property). You probably want to monitor when your MovieClip's x property is greater than the width of your stage. If it is remove (removeChild) it and add a new one (addChild) and place it back at the start x/y position.