Mouse Events for objects in an array in Flash - arrays

I am not sure how to make this clear but : Is there a way to allow mouse event register with objects in an array? I have multiple objects being added to stage from an array and i would like to call different functions after said objects are clicked on ? I have this:
function makeEnemies():void
{
//create humans
var tempEnemy:MovieClip;
var wolf:MovieClip;
tempEnemy = new Enemy2();
tempEnemy.cacheAsBitmap = true;
tempEnemy.speed = 20;
tempEnemy.x = Math.round(Math.random() * 800);
tempEnemy.y = Math.round(Math.random() * 480);
addChild(tempEnemy);
enemies.push(tempEnemy);
}
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy = enemies[i];
if (tempEnemy.x > stage.stageWidth)
{
tempEnemy.x = stage.stageWidth;
}
if (tempEnemy.y > stage.stageHeight)
{
tempEnemy.y = stage.stageHeight;
}
tempEnemy.x += Math.round(Math.random() * tempEnemy.speed);
tempEnemy.y -= Math.round(Math.random() * tempEnemy.speed);
tempEnemy.addEventListener(MouseEvent.CLICK, scoreM);
function scoreM(event:MouseEvent):void
{
makeBite(tempEnemy.x, tempEnemy.y);
removeEnemy(i);
score++;
score_txt.text = String(score);
}
function removeEnemy(idx:int)
{
removeChild(enemies[idx]);
enemies.splice(idx,1);
}
And i get an error
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at veinsVtest_fla::MainTimeline/removeEnemy()
at MethodInfo-67()

You won't need an Array for this job. Like Man of Snow said. Use event.currentTarget will point to the "clicked on Enemy"
tempEnemy.addEventListener(MouseEvent.CLICK, scoreM);
function scoreM(event:MouseEvent):void
{
var clickedOnEnemy:MovieClip = event.currentTarget as MovieClip;
//Now you have your enemy, do whatever you please with him.
makeBite(clickedOnEnemy.x, clickedOnEnemy.y);
//And farewell, my enemy ... time to remove him.
removeChild(clickedOnEnemy);
clickedOnEnemy = null;
score++;
score_txt.text = String(score);
}
* EDIT **
There are several ways to implement "removeAllEnemies".
One way to do it is to use have another MovieClip to hold all enemies created.
So create a movieClip and add it to stage.
var enemiesWrapper : MovieClip = new MovieClip();
addChild(enemiesWrapper);
And then instead of add enemy to root
addChild(tempEnemy); //Instead of doing this
Add them to this MovieClip instead.
enemiesWrapper.addChild(tempEnemy); //Do this instead
Note that your removeChild has to be updated accordingly
enemiesWrapper.removeChild(clickedOnEnemy);
And for "removeAllEnemies" function
function removeAllEnemies() {
while(enemiesWrapper.numChildren > 0) {
enemiesWrapper.removeChildAt(0);
}
}

Replace removeEnemy(i); with removeEnemy(event.currentTarget);
You cannot remove an integer, because it isn't a child. However, I'm assuming you want to remove the MovieClip that was clicked, and it looks like removeEnemy calls removeChild() on the parameter. If not, do you mind showing your removeEnemy function?

Related

AS3 Array items not splicing properly

I'm attempting to make a small game where the user mouses over the circles that fall from the ceiling for points. The circles are added to a container and pushed into an array to hold them, and are removed and spliced when they are mouse-over'd or go off stage.
Everything works fine, until two circles are removed at nearly the same time, whether it be from falling off stage at the same time or mousing over two of them extremely fast. When this happens, the child on stage is removed, but the object is still left in the array, meaning another circle cannot take its place, leaving one less circle spawning every time the issue happens.
Code on main timeline:
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.display.Sprite;
var ballContainer:Sprite = new Sprite();
addChild(ballContainer);
var maxBalls:uint = 10;
var balls:Array = [];
var ballTypes:Array = [GreenBall];
var ballChances:Array = [800];
var ballVelocities:Array = [1.5];
var ballAccelerations:Array = [1.02];
stage.addEventListener(Event.ENTER_FRAME, onTick);
function onTick(e:Event):void {
while (balls.length < maxBalls){
addBall();
}
}
function addBall():void {
var ballType = ballTypes[0];
var ball = new ballType;
ball.x = Math.ceil(Math.random()*(stage.stageWidth - ball.width));
ball.y = 0 - (ball.height*1.5);
ballContainer.addChild(ball);
balls.push(ball);
}
Code in GreenBall:
import flash.events.Event;
var mainStage = Sprite(root);
var index = mainStage.balls.indexOf(this);
var velocity:Number = mainStage.ballVelocities[0]*randomNumber(0.5, 1.5);
var acceleration:Number = mainStage.ballAccelerations[0];
this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
function onMouseOver(e:MouseEvent):void {
this.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeBall();
}
this.addEventListener(Event.ENTER_FRAME, onTick);
function onTick(e:Event):void {
this.y += velocity;
velocity = velocity*acceleration;
if (this.y > stage.stageHeight + this.height){
this.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeBall();
}
}
function removeBall():void {
mainStage.balls.splice(index, 1);//doesn't get spliced if balls are removed too quickly
mainStage.ballContainer.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, onTick);
}
function randomNumber(min:Number, max:Number):Number {
return Math.random()*(max - min) + min;
}
So what's going on? Did I set something up incorrectly? How can I go about fixing this issue?
Any help would be appreciated greatly.
Your logic is flawed - the index should be calculated when the removal occurs. When you remove objects from an array via splice, the index of all the elements after the one you removed is decreased by one.
This means that if you have 10 balls and remove the first, the index value you have for every other ball will be incorrect and you'll be removing the wrong ball from your array on subsequent removals.
Moving the indexOf statement to the removeBall method should solve the issue:
function removeBall():void
{
var index:int = mainStage.balls.indexOf(this);
if(index >= 0)
{
mainStage.balls.splice(index, 1);
mainStage.ballContainer.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, onTick);
}
}
To make it easy on yourself, you could extend Array and make a remove function:
public dynamic class List extends Array
{
public function remove(item:*):void
{
var i:int = indexOf(item);
if(i >= 0) splice(i, 1);
}
}

Array getting Keyboard Input

This code is from a tutorial, I do understand it - just not the array. I know arrays hold values in their indexes (array[1]=something, array[2]=something...)
But how does the array "keys" have the Keyboard.RIGHT value in the update function?
package
{
-imports here-
public class Main extends Sprite
{
var keys:Array = [];
var ball:Sprite = new Sprite();
public function Main():void
{
ball.graphics.beginFill(0x000000);
ball.graphics.drawCircle(0, 0, 50);
ball.graphics.endFill;
addChild(ball);
ball.x = stage.stageWidth / 2;
ball.y = stage.stageHeight / 2;
ball.addEventListener(Event.ENTER_FRAME, update);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
private function update(e:Event):void {
if (keys[Keyboard.RIGHT]) {
ball.x += 5;
}
}
function onKeyDown(e:KeyboardEvent):void {
keys[e.keyCode] = true;
}
function onKeyUp(e:KeyboardEvent):void {
keys[e.keyCode] = false;
}
}
}
(Because, to give a value to a variable in Pascal for an example, I'd have to write readln (array[1]) - this would give whatever value the user typed to array[1]).
So, I don't see how keys[] is getting keyboard input :P
e.keyCode is an integer. When the onKeyDown gets called, the code uses the integer to set a value in the array to true.
For example, if the RIGHT key is pressed on the keyboard, e.keyCode would be 39, so the code is the same as keys[39] = true;.
If you look at the documentation for Keyboard you will see that Keyboard.RIGHT is defined as 39. http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/ui/Keyboard.html
On the next frame the update event fires.
When you write an if statement without an ==, it kinda does it for, so the if statement is essentially saying
if(keys[39] == true) {
ball.x += 5;
}

as3: how to access a MovieClip as unique Object/MovieClip from Array which loaded from same MovieClip from the Library

I'm trying to add multiple copies of the same movieclip to the stage at once
I have the loop which fills the array and generate the movieclips to the stage
and the loop to add The click EventListener for each movieClip
but I miss the magical code to access every MovieClip separately
to have it removed from the stage by clicking on it
var numOfClips:Number = 5;
var mcArray:Array = new Array();
for(var i=0; i<numOfClips; i++)
{
var usd:mcUSD = new mcUSD();
//genrate random x , y position----------------------------
var randY:Number = Math.floor(Math.random()*460) + 120;
var randX:Number = Math.floor(Math.random()*350) + 60;
usd.x = randX;
usd.y = randY;
//---------------------------------------------------------
mcArray.push(usd);
addChild(usd);
}
for(var m:int = 0; m<mcArray.length; m++){
usd.addEventListener(MouseEvent.CLICK, colectmoney);
}
function colectmoney(e:MouseEvent): void {
removeChild(usd);
}
Try this:
import flash.events.MouseEvent;
var numOfClips:Number = 5;
var mcArray:Array = new Array();
for(var i=0; i<numOfClips; i++)
{
var usd:mcUSD = new mcUSD();
//genrate random x , y position----------------------------
var randY:Number = Math.floor(Math.random()*460) + 120;
var randX:Number = Math.floor(Math.random()*350) + 60;
usd.x = randX;
usd.y = randY;
//---------------------------------------------------------
mcArray.push(usd);
addChild(usd);
}
addEventListener(MouseEvent.CLICK, mouseClickHandler);
function mouseClickHandler(e:MouseEvent) : void {
removeChild(mcArray[mcArray.indexOf(e.target)]);
}
Important things to note: 1) you do not need to call a mouse.click event listener for each mcUSD object. It's more efficient to call it once. 2) removeChild(usd) won't work because you need to tell AS3 which mcUSD object to remove. 3) try to keep function nomenclature consistent - eg colectMoney instead of colectmoney. it will save you confusing times once your program gets bigger. hope this helps! :)

Actionscript 3.0 : Controlling child movieclips added with an array

I'm trying to build a generic slide show flash file controlled with key presses... right and left arrow to go to next / previous slide, down and up to call / uncall the single animations / bullet points / whatever on a single slide.
So far I managed to get a file that can load all the pages from a movieclip and flick through them with right and left arrow and a nice fade out and fade in effect, even when flicking very fast.
What I do not get at all after several hours and lots of google searches is how to now control the single page movieclips. I uploaded the file so far:
http://www.broesel-brzelius.de/zeug/slide.zip
You can see that my (as of yet crude) attempt to control the pageHolder.Pages.page1 movieclip to gotoAndStop(2) just results in the pageHolder.Pages movieclip that was loaded to position 0 in the array to go to frame 2 and thus display the second page.
Code so far:
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.greensock.*;
import flash.display.MovieClip;
// instantiate a variable to find number of pages
var numberOfPages:Pages = new Pages();
// instantiate an array to hold the page movieclips
var pageArray:Array = new Array();
// instantiate a container to hold the pages
var pageHolder:Sprite = new Sprite();
// declare variables that will hold reference to the current page IDs
var targetIDold:int = 0;
var targetIDnew:int = 0;
// declare a variable that will hold the current direction of slide movement
var movement:int = -1;
// call a function that builds the application
// pass in the number of pages in fl_prevSlide
buildApp(numberOfPages.totalFrames);
// this function builds the application
function buildApp(n:int):void
{
// declare variables for the pages
var p:Pages;
// instantiate a new Page, send its playhead to the current value of i+1
// it is necessary to add 1 to the value of i, since i starts at 0, while the timeline starts at 1
for (var i:int = 0; i < n; i++)
{
p = new Pages();
p.gotoAndStop(i + 1);
pageArray.push(p);
}
// set the position of the pageHolder relative to the buttonHolder
pageHolder.x = pageHolder.y = 0;
// add the first page (at index 0) from pageArray to pageHolder
pageHolder.addChild(pageArray[targetIDnew]);
// add pageHolder and buttonHolder to the stage
addChild(pageHolder);
}
//Adding Listener and corresponding function to change between slides
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeSlide);
function f_changeSlide(evt:KeyboardEvent):void
{
if(evt.keyCode == 37) // left arrow
{
f_prevSlide();
}
else if (evt.keyCode == 39 || evt.keyCode == 32) // right arrow or space
{
f_nextSlide();
}
}
function f_prevSlide():void
{
if(targetIDnew > 0)
{
movement = -1;
targetIDnew -= 1;
f_addPage();
}
}
function f_nextSlide():void
{
if(targetIDnew < (numberOfPages.totalFrames - 1))
{
movement = 1;
targetIDnew += 1;
f_addPage();
}
}
function f_addPage():void
{
// use the targetID variable to access the corresponding index in the pageArray and assign it to a temporary variable
targetIDold = targetIDnew - movement;
var _mcOld:MovieClip = MovieClip(pageArray[targetIDold]);
var _mcNew:MovieClip = MovieClip(pageArray[targetIDnew]);
//avoid flickering
if (_mcNew.alpha == 1) {
_mcNew.alpha = 0;
}
// add the temp variable to pageHolder
pageHolder.addChild(_mcNew);
// tween the temp variable to the specified properties, then call a function to remove the previous page
// new page gets faded in, old page gets faded out
TweenMax.to(_mcNew, 1.5, {alpha:1});
TweenMax.to(_mcOld, 1.5, {alpha:0, onComplete:f_removePage});
}
function f_removePage():void
{
// previous page will always be at index 0; remove it
// the new page just added will drop down to index 0 when the previous page is removed
pageHolder.removeChildAt(0);
}
//Trying to advance / decrease the timeline in the movieclips of the single pages by pressing down / up
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeFrame);
function f_changeFrame(evt:KeyboardEvent):void
{
if(evt.keyCode == 40) // down arrow
{
f_nextFrame();
}
else if (evt.keyCode == 38) // up arrow
{
f_prevFrame();
}
}
function f_nextFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame + 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("down!");
}
function f_prevFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame - 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("up!");
}
Edit: clarified code a bit
Edit2: TL;DR: Problem is the last bit of code with the three functions that fail to control the timeline of mc page1, which is a child of mc pages, which is pushed frame by frame into an array and loaded at runtime into the sprite pageHolder. How to control the timeline of the single page movieclips?
Finally got it to work. This will work as a perfect powerpoint replacement :D
You will need to name the single slides (one movieclip on each frame of the mc pages) as page0, page1 etc. and then can control their timeline with this code (just replace the functions listed in the question above):
function f_nextFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame + 1);
}
function f_prevFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame - 1);
}

Remove Array and its contents with mouse click AS3 Flash CS5.5

I have been trying to do this for two nights now and haven't had any joy, please help if you can...
Simple throwing game with darts and other weapons, what I am trying to do is
Remove an array and all of its Children when I change weapon,
I feel sure there is a simple snippet of code that will remove all the children and array with no hassle but I haven't yet figured it out, if you know or could suggest anything, I would really appreciate it.
something like "removeArrayAndAllInstances(balls);" if only...
at the moment I have....(balls is the array in question)
for(var inter:int = balls.length - 1; inter > -1; inter--)
{
balls.splice(1);
balls.splice(1, balls.length);
}
but this docent work for some reason, the array and all of its children are all still on the stage.
I also tried
balls[];
No luck...
Please don't judge my code I am a novice as I am sure was evident and I know its a disgusting mess, sorry (Its the only way it makes sense to me).
I have tried numerous things, hope someone can help
Thanks in advance.....
var mouseTarget:MovieClip;
var balls:Array = new Array();
var ball:MovieClip = new dart();
var hammers:MovieClip = new hammer();
ball.x = 150;
ball.y = 50;
hammer_btn.addEventListener(MouseEvent.MOUSE_DOWN, hammerweapon);
dart_btn.addEventListener(MouseEvent.MOUSE_DOWN, dartweapon);
function removealldartsfromstage(e:MouseEvent):void
{
for(var inter:int = balls.length - 1; inter > -1; inter--)
{
balls.splice(1);
balls.splice(1, balls.length);
}
stage.removeEventListener(MouseEvent.MOUSE_UP, addDart);
}
function dartweapon(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, addHammer);
dart_btn.removeEventListener(MouseEvent.CLICK, dartweapon);
stage.addEventListener(MouseEvent.MOUSE_UP, addDart);
//removeChild(balls);
}
function hammerweapon(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, addDart);
dart_btn.addEventListener(MouseEvent.MOUSE_DOWN, dartweapon);
//stage.addEventListener(MouseEvent.MOUSE_UP, addDart);
stage.addEventListener(MouseEvent.MOUSE_UP, addHammer);
}
function addHammer(e:MouseEvent):void
{
var hammers = new hammer();
addChild(hammers);
removeChild(dart_btn);
addChild(dart_btn);
dart_btn.addEventListener(MouseEvent.CLICK, dartweapon);
balls.splice(10);
}
function addDart(e:MouseEvent):void
{
str.alpha = 0;
var ball = new dart();
addChild(ball);
removeChild(hammer_btn);
addChild(hammer_btn);
ball.x = 150;
ball.y = 50;
balls.push(ball);
trace(balls);
addEventListener(Event.ENTER_FRAME, checkIfHitTest);
hammer_btn.addEventListener(MouseEvent.MOUSE_DOWN, removealldartsfromstage);
function checkIfHitTest(Event)
{
for (var i:int = 0; i<balls.length; i++)
{
if (balls[i].dart_point.hitTestObject(eyeleft))
{
trace("hitleftbullseye");
ball.gotoAndStop("hitlefteyeframe");
Event.currentTarget.removeEventListener(Event.type, checkIfHitTest);
balls.splice(i, 1);
}
}
}
}
The balls array is just a storage for ball references. It bears no relation to the stage or the DisplayObjectContainer which they have been added at all. So you have to remove them individually.
This is what I'd do:
while(balls.length > 0)
{
var ball:MovieClip = balls.pop();
if (ball.parent) // Just to make sure you are referencing the correct container.
{
ball.parent.removeChild(ball);
}
}
I can't follow the logic of your game very well. That said, you'd do well to create conatiners for separate group of clips in order to make managing them easier. For example, in the creation stage:
var ballContainer:Sprite = new Sprite();
addChild(ballContainer);
for (var i:int = 0; i < ballLimit; i++)
{
var ball:Dart = new Dart();
ballContainer.addChild(ball);
}
This way, you can clear them of children all at once:
function removeAllChildren(container:Sprite) // Or just DisplayObjectContainer
{
while(container.numChildren > 0)
{
container.removeChild(container.getChildAt(0));
}
}

Resources