How to remove object from index in array AS3 - arrays

Hey Everyone so I've been killing myself over this and this is probably the last thing I need help on to finally finish this project.
So I have a timer event that puts the Movie clip in the array onto the Stage. The MovieClip is named mSquare and from the timer event I add it like so:
private function addSquare(e:TimerEvent):void
{
mSquare = new mcSquare();
stage.addChild(mSquare);
mSquare.x = (stage.stageWidth / 2);
mSquare.y = (stage.stageHeight / 2) + 450;
aSquareArray.push(mSquare);
trace(aSquareArray.length);
}
Now what I am trying to Accomplish is when the user clicks or has its mouse down on the object, I want that object that the user clicked to be removed from the stage.
Now I can't get it to function properly when the user clicks on one of the MovieClips from the array thats added to the stage. Or if I do then MovieClip that was currently clicked doesnt get destroyed but rather another instance of it on the stage gets destroyed. Its just real bad.
So Here is how I have it set up so far...
In my Constructor:
stage.addEventListener(MouseEvent.MOUSE_DOWN, movieClipHandler);
Then the movieClipHandler MouseEvent:
private function movieClipHandler(e:MouseEvent):void //test
{
mSquare.addEventListener(MouseEvent.MOUSE_DOWN, squareIsBeingClicked);
}
Then in the squareIsBeingClicked Function:
private function squareIsBeingClicked(e:MouseEvent):void
{
var square:DisplayObject = e.target as DisplayObject; // clicked square
var i:int = aSquareArray.indexOf(square); // index in the array
if (i < 0) //THIS IS WHERE I GET CONFUSED
{
// the MC is out of the array
trace("Clicked");
mouseIsDown = true;
checkSquareIsClicked();
} else
{
// the MC is in the array
}
}
And finally i have a function called checkSquareIsClicked to do all the work:
private function checkPopIsBeingClicked():void
{
//Loop through all pops
for (var i:int = 0; i < aPopArray.length; i++)
{
//Get current pops in i loop
var currentpop:mcPop = aPopArray[i];
if (mouseIsDown)
{
trace("Current Pop Destroyed")
aPopArray.splice(i, 1);
currentpop.destroyPop();
nScore ++;
updateHighScore();
updateCurrentScore();
mouseIsDown = false;
//Add Explosion Effect
popExplode = new mcPopExplode();
stage.addChild(popExplode);
popExplode.y = currentpop.y;
popExplode.x = currentpop.x;
}
}
}
I know I am doing something really wrong because it shouldnt take this much functions to initiate this array of mouse event. Also someone on here helped me out with the display object and idex as shown above but not sure how to truly implement it. I've exhausted all ideas. Any Help would be appreciated or any pointers. Thank you!

As #Rajneesh answer indicates, adding your mouse event listener to your MovieClip instead of using the stage mouse event to add the listener, is the correct way to go. That means instead of doing this...:
stage.addEventListener(MouseEvent.MOUSE_DOWN, movieClipHandler);
private function movieClipHandler(e:MouseEvent):void {
mSquare.addEventListener(MouseEvent.MOUSE_DOWN, squareIsBeingClicked);
}
Would be replaced in your addSquare function:
private function addSquare(e:TimerEvent):void {
mSquare = new mcSquare();
stage.addChild(mSquare);
mSquare.x = (stage.stageWidth / 2);
mSquare.y = (stage.stageHeight / 2) + 450;
aSquareArray.push(mSquare);
//look here
mSquare.addEventListener(MouseEvent.MOUSE_DOWN, squareIsBeingClicked);
trace(aSquareArray.length);
}
Now you can change your removal functions. I'm going to alter some of this code to help you optimize it and still achieve the desired effect. Let's start with squareIsBeingClicked:
private function squareIsBeingClicked(e:MouseEvent):void {
var square:DisplayObject = e.target as DisplayObject; // clicked square
var i:int = aSquareArray.indexOf(square); // index in the array
//this is where you will no longer be confused :)
if (i != -1) {
trace("Clicked");
mouseIsDown = false;
onSquareIsClicked( square ); //this is in place of your checkSquareIsClicked
square.parent.removeChild( square );
aSquareArray.splice( i, 1 ); //remove element from the array
//remove the listener, very important
square.removeEventListener( MouseEvent.CLICK, squareIsBeingClicked );
square = null; //null it if we are not using it again, ready to be garbage collected
}
}
You now no longer need all the removal pieces in your previous checkSquareIsClicked. As you probably noticed I added a function onSquareIsClicked(), this is to help with any functionality you might need to do when this event is happening. Since you are adding an "explosion" where the object was selected, here's what onSquareIsClicked() looks like:
private function onSquareIsClicked( square:DisplayObject ):void {
popExplode = new mcPopExplode();
stage.addChild(popExplode);
popExplode.y = currentpop.y;
popExplode.x = currentpop.x;
//square.destroyPop(); //obviously this function will not work on a type displayObject
//in which case you just need to cast square to the object containing the method
nScore ++;
updateHighScore();
updateCurrentScore();
}

Add mouse click events to objects to be removed and get target and remove it like so:
(Note: Also, no need to add those MovieClips to array ( add only if necessary))
private function addSquare(e:TimerEvent):void
{
mSquare = new mcSquare();
mSquare.addEventListener(MouseEvent.CLICK, removeSquare);
stage.addChild(mSquare);
//Position here mSquare
}
private function removeSquare(e:MouseEvent):void
{
var tempObject:MovieClip = MovieClip(e.currentTarget); //get the square object
removeChild(tempObject);
tempObject = null;
}

Related

creating an array for a snapping function with ActionScript 3

I created a puzzle where you can drag and drop 16 pieces. I used an array so that the code doesn't get too big. Now I want to add an function where each puzzle piece snaps into right place once you get near the destination.
My problem is that I don't know how to create an array which can achieve my goal. I tried the following(without an array but that creates too much code if I do it with all 16 puzzle pieces):
if(target1_mc.hitTestObject(piece1_mc.tar1_mc))
{
piece1_mc.x = 207,15;
piece1_mc.y = 119,25;
}
Code:
import flash.events.Event;
import flash.events.MouseEvent;
var puzzleArr:Array = new Array (piece1_mc, piece2_mc, piece3_mc, piece4_mc,
piece5_mc, piece6_mc, piece7_mc, piece8_mc,
piece9_mc, piece10_mc,
piece11_mc, piece12_mc, piece13_mc, piece14_mc, piece15_mc, piece16_mc);
for (var i:uint =0; i < puzzleArr.length; i++) {
puzzleArr[i].addEventListener(MouseEvent.MOUSE_DOWN, drag);
puzzleArr[i].addEventListener(MouseEvent.MOUSE_UP, drop);
}
function drag(event:MouseEvent):void {
event.currentTarget.startDrag();
}
function drop(event:MouseEvent):void {
event.currentTarget.stopDrag();
}
There a few ways to accomplish this. The easiest, is to add a dynamic property to your pieces that stores the target (correct location object) for the piece.
var puzzleArr:Array = []; //you don't really even need the array in my example
var tmpPiece:MovieClip; //this stores the current dragging piece, and I also reuse it in the loop below
//I don't like typing a lot, so let's use a loop for all 16 pieces and their targets
for(var i:int=1;i<=16;i++){
tmpPiece = this["piece" + i + "_mc"]; //get a reference to piece whose number matches i
if(!tmpPiece){
trace("Sorry - there is no piece called: 'piece" + i + "_mc'");
continue;
}
//give the piece a dynamic property that is a reference to it's target spot
tmpPiece.targetTile = this["target" + i + "_mc"];
if(!tmpPiece.targetTile){
trace("Sorry - there is no target called: 'target" + i + "_mc'");
continue;
}
tmpPiece.tar_mc = tmpPiece["tar" + i + "_mc"]; //it would be better to just take the number out of each pieces tar_mc child object making this line uneccessary
//track where the piece is placed
tmpPiece.startingPos = new Point(tmpPiece.x, tmpPiece.y);
//only add the mouse down listener to the piece (not mouse up)
tmpPiece.addEventListener(MouseEvent.MOUSE_DOWN, drag);
//if still using the array, add the piece to the array
puzzleArr.push(tmpPiece);
}
Next, add a mouse move listener only while dragging
function drag(event:MouseEvent):void {
tmpPiece = event.currentTarget as MovieClip; //assign the dragging object to the tmpPiece var
tmpPiece.startDrag();
//add a mouse move listener so you can check if snapping is needed
tmpPiece.addEventListener(MouseEvent.MOUSE_MOVE, moving);
//add the mouse up listener to the stage - this is good because if you drag fast, the mouse can leave the object your dragging, and if you release the mouse then it won't trigger a mouse up on the dragging object
stage.addEventListener(MouseEvent.MOUSE_UP, drop);
}
function drop(event:MouseEvent):void {
//stop all dragging
this.stopDrag();
if(tmpPiece){
//remove the mouse move listener
tmpPiece.removeEventListener(MouseEvent.MOUSE_MOVE, moving);
//ensure a snap at the end of the drag
if(!checkSnapping()){
//if not snapped, reset it's position
tmpPiece.x = tmpPiece.startingPos.x;
tmpPiece.y = tmpPiece.startingPos.y;
}
}
//remove the mouse up listener
stage.removeEventListener(MouseEvent.MOUSE_UP, drop);
}
Now lets do the snapping in the mouse move handler:
function moving(e:MouseEvent = null):void {
checkSnapping();
}
//return true if snapped
function checkSnapping():Boolean {
if(tmpPiece && tmpPiece.tar_mc.hitTestObject(tmpPiece.targetTile)){
tmpPiece.x = tmpPiece.targetObj.x - tmpPiece.tar_mc.x;
tmpPiece.y = tmpPiece.targetObj.y - tmpPiece.tar_mc.y;
return true;
}
return false;
}
for (var i:int = 0; i < puzzleArray.length; i++)
{
if(puzzleArray[i].hitTestObject(puzzleArray[i]._target))
{
puzzleArray[i].x = puzzleArray[i]._xGoal;
puzzleArray[i].y = puzzleArray[i]._yGoal;
}
}
Obviously you'll need to add a couple of properties to the puzzle piece objects (_xGoal, _yGoal,_target) and you can do that however you'd like. You could probably use a loop, but only if there is some sort of order to them. If they are not of equal size and in a grid, then you'll have to obviously just manually type these in.
If they are in a grid and each piece is equal size, let me know if you need help with creating those properties in a loop.

How to re add elements in array to their same position on stage?

Hey everyone so in my game I am creating I add all my interactive objects to the stage by simply dragging from the library and placing them on the stage. Then in Flash Develop in my Engine Class I place them inside an array like so:
private var aZebraArray:Array;
In my constructor I add the elements on the stage through their instance names I gave them Like so:
aZebraArray = [startScreen.zebra_Front, startScreen.zebra_Middle, startScreen.zebra_Back, startScreen.zebra_Far];
Then in my Enter_Frame Listener I loop through the objects on stage like so for the Hit Test:
private function Round_1Controls():void
{
trace(aZebraArray.length);
for (var i:int = 0; i < aZebraArray.length; i++)
{
var currentZebra = aZebraArray[i];
if (crosshair.bullet.hitTestObject(currentZebra) && shotGun)
{
trace("HIT");
aZebraArray.splice(i, 1);
currentZebra.gotoAndPlay("DIE");
shotGun = false;
//Add points
nPoints += 50;
updatePointsText();
//Animals hit for stars
animalsHit ++;
}
}
Now in my game if the player doesn't get 3 stars he is able to Retry and restart that round over. The PROBLEM That I have run into is that I don't quite know how I would go about having the objects that are on stage placed back where they first started from because when the round starts the "Zebras" run across the stage through a custom tween I made in each of them. Also I believe I could just create a new array like so:
aZebraArray = new Array();
aZebraArray = [startScreen.zebra_Front, startScreen.zebra_Middle, startScreen.zebra_Back, startScreen.zebra_Far];
but then how could i restart all their animations so they can run across the stage again? I know I can just create their own class and add them in array then control them through a timer object but I add them manually on stage because their are a lot of bushes that they need to hide behind or be in front of and to code that would be exhausting. If anyone can help I would Appreciate it thanks!
Here is what I have in my retry Function but it is only adding 1 or 2 of the objects in the array back to the stage.
private function replayRound_1(e:MouseEvent):void
{
continueScreen.mcRetry.removeEventListener(MouseEvent.CLICK, replayRound_1);
continueScreen.destroyContinueScreen();
round1 = true;
nAmmo = 0;
nAmmo += 5;
updateAmmoText();
animalsHit = 0;
hasAmmo = true;
gameChannel = gameSound.play(0, 999);
//aZebraArray = [];
aZebraArray = new Array();
aZebraArray = [startScreen.zebra_Front, startScreen.zebra_Middle, startScreen.zebra_Back, startScreen.zebra_Far];
for (var i:int = 0; i < aZebraArray.length; i++)
{
var currentZebra = aZebraArray[i];
currentZebra.gotoAndStop("RUN");
}
}

Use value from array to set addeventListner to multiple buttons

I'm making a game that have multiple instance of a MC in stage (a1,b1,c1....a2,b2,c2,d2,etc)
Every time to make a listener i have to write
Object(this).sopa.c1.btn.addEventListener(MouseEvent.CLICK, c1Click);
and create a function like this
function c1Click(event:MouseEvent):void
{
checkString += "c1";
var TFc1:TextFormat = Object(this).sopa.c1.letra.getTextFormat(0,1);
TFc1.color = 0xff0022;
Object(this).sopa.c1.letra.setTextFormat(TFc1);
check();
}
but whit that many buttons (more than 100) is to long writing them one by one.
So i think put the values (instance names) of the buttons in an array. Something like this
var casillas:Array = [a1, b1,c1];
for (var i:uint = 0; i < casillas.length; i++)
{
casillas[i].addEventListener(MouseEvent.CLICK, a1presion);
trace(casillas[i])
}
but i cant get the value of the array element just a [object SimpleButton].Have anyone any ideas how to do this: I'm new in as3 i worked all my life in as2 and this is way to hard to understand for me.
This is where Object Oriented Programming shines. You encapsulate all properties and methods into a single object. You can then create as many of these objects, and watch as they function individually with the behavior specified inside.
Here's a simple button class I sometimes use myself:
package buttons {
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class SimpleButton extends MovieClip {
public function SimpleButton() {
addEventListener(MouseEvent.MOUSE_OVER, mOver);
addEventListener(MouseEvent.MOUSE_OUT, mOut);
addEventListener(MouseEvent.MOUSE_DOWN, mDown);
buttonMode = true;
}
protected function mOver(e:MouseEvent):void {
gotoAndStop(2);
}
protected function mOut(e:MouseEvent):void {
gotoAndStop(1);
}
protected function mDown(e:MouseEvent):void {}
}
}
Then create as many instances as you like, and store them in some container so that you can get rid of them at some point in the destruction of the program.
var buttons:Vector.<SimpleButton> = new Vector.<SimpleButton>();
buttons.push(new SimpleButton());
buttons.push(new SimpleButton());
buttons.push(new SimpleButton());
Firstly, I have to agree with Iggy, what you are trying to do will ultimately be more re-usable and easy to understand if it were written using Object Oriented code.
However, from what you have already written, I think you are saying is that you are struggling to create an event handler that is generic to all your buttons? So if you need to get the string name of the MovieClip as well as the actual clip reference maybe you could do something like this:
var casillas:Array = ["a1", "b1", "c1"]; //Have an array of the names of the buttons
for (var i:uint = 0; i < casillas.length; i++)
{
var mcReference:MovieClip = this.sopa[ casillasNames[i] ]; //Use the string reference to get hold of the button
mcReference.btn.addEventListener(MouseEvent.CLICK, _handleButtonClick); //Add a listener to the same "btn" child of the casilla as you did before.
}
you need a listener
function _handleButtonClick(e:Event)
{
var buttonReference:* = e.target;
So e.target in this instance should be the same object that you added to the array earlier (a1 or b1 or c1 depending on which one is clicked)
Now I'm going to make some leaps about how those MovieClips (a1,b1,c1) are structured (I'm guessing they are all the same just with different data in?)
function _handleButtonClick(event:MouseEvent):void
{
var clickedButtonReference:* = e.target;
for(var i:int = 0; i< casillas.length; i++)
{
if(this.sopa[ casillasNames[i] ].btn == clickedButtonReference) //Check each button on the stage to see if it is the one that has been clicked.
{
checkString += casillasNames[i];
//To find this bit "this.sopa.c1.letra" there are two options either
var selectedLetra = this.sopa[ casillasNames[i] ].letra; //Using the string name of the MC to find it.
//or
var selectedLetra = clickedButtonReference.parent.letra; //Or using the fact that we know that the original MC contains both the btn and the letra as children.
//Once we have it though the rest of your function should work ok.
var TFc1:TextFormat= selectedLetra.getTextFormat(0,1)
TFc1.color = 0xff0022;
selectedLetra.setTextFormat(TFc1);
check();
//Just add a break to stop searching for the button since we have found it.
break;
}
}
}

AS3- how to addchild this array(john) correctly

I do not know how to add the john array and make a hittestobject with it.
Bal is a different class non relevant to this problem.
I've tried to do john[new Bal]
tried john[ k ]
tried z and to specify z as a for-loop but then i would just get Z balls place.
This is supposed to become a space-invader type of game. I'm trying to make a hit test object between HENK and the 'falling balls' (JOHN). I do not know how to work with arrays especially given the fact that is should be timer-triggered.
Thanks
public class Main extends Sprite
{
public var henk:Sprite = new Sprite();
public var level:Timer = new Timer (2000, 0);
public var valTijd:Number = new Number
public var i:Number = 2000;
public var john:Array = new Array();
public var k:Number = 9000;
public function Main():void
{
henk.graphics.beginFill(0xFF00FF);
henk.graphics.drawCircle(0, 500, 20);
henk.graphics.endFill();
addChild(henk);
level.addEventListener(TimerEvent.TIMER, up);
level.start();
henk.addEventListener(Event.ENTER_FRAME, muis);
henk.addEventListener(Event.ENTER_FRAME, hit);
}
public function up(e:TimerEvent):void
{
var tijdje:Timer = new Timer( i, 0)
tijdje.addEventListener(TimerEvent.TIMER, tijdLuisteraar);
tijdje.start();
i = i - 250;
}
public function muis (e:Event):void
{
henk.x = mouseX;
}
public function hit (e:Event): void
{
if ( henk.hitTestObject(john [k] ))
{
if (contains(john[k] ))
{
removeChild(henk);
}
}
}
public function tijdLuisteraar(e:TimerEvent):void
{
john.push(new Bal);
addChild(john[k]);
}
}
}
welcome to stackoverflow!
This problem is actually fairly simple, I will describe how you will probably want to use an array in the case you described.
At the part where you create new Balls you want to append them to an array, which will be something like the following:
var ball = new Bal();
john.push(ball);
addChild(ball);
This will go inside your timer-triggered function, obviously.
Secondly, you want to have a hitTestObject with henk and all of the balls stored in the john array.
for(var i = 0; i < john.length; i++) {
if (henk.hitTestObject(john[i])) {
// well, that's a bummer for your player, henk hit one of the balls in the john array
// display something like a message here
}
}
This will automatically detect the size of the array, so all elements are tested. Be careful with hitTestObject when you have a lot of elements in the john-array, this can slow down your game drastically.
Furthermore, reflecting your code I suggest the following:
remove public var i:Number = 2000; and public var k:Number = 9000;, these have no meaning anymore
use a mouse event to move your henk object, not an ENTER_FRAME. I guess you will be able to find how this works. This will only trigger the function when it has to do something, resulting in less CPU-power needed and a cleaner code.
if you want to make the game even cooler, you could add the support for using the arrow keys

Movie Clips is working before it is added to DisplayList?

I have to add a list of objects into an Array so I can access them Later, but for some reason the object I have added plays before, I add them on to the display list. here is the code:
the Frame Where the list is Added:
sunny.addEventListener(MouseEvent.CLICK, sunny_choice);
function sunny_choice(E:MouseEvent)
{
var sunny_walkcycle: Sunny_Walkcycle = new Sunny_Walkcycle ();
var sunny_busstop : Sunny_BusStop = new Sunny_BusStop ();
var sunny_opening: Teacher_Opening_Sunny = new Teacher_Opening_Sunny ();
clothingArray.push( sunny_walkcycle);
clothingArray.push( sunny_busstop);
clothingArray.push( sunny_opening);
trace("Sunny");
cleaner();
//cleaner just removes the event listener and asks it to go to the next frame
}
Th next Frame:
clothingArray [0].scaleX = -1;
addChild (clothingArray [0]);
clothingArray[0].x = 633;
clothingArray[0].y = 174;
clothingArray [0].stop ();
clothingArray [0].addEventListener (MouseEvent.CLICK, transforming);
function transforming (e:MouseEvent) {
if (clothingArray [0].currentFrame == clothingArray [0].totalFrames) {
clearall2 ();
}
else{
clothingArray [0].nextFrame();
moving_background.nextFrame ();
}
}
function clearall2 () {
clothingArray [0].removeEventListener (MouseEvent.CLICK, transforming);
removeChild (clothingArray [0]);
gotoAndStop (3);
}
The problematic One:
addChild (clothingArray [1]);
clothingArray[1].addEventListener (Event.ENTER_FRAME , busstop);
trace ("The Current Frame is " + clothingArray [1].currentFrame);
function busstop (d:Event) {
if (clothingArray [1].currentFrame == clothingArray [1].totalFrames) {
clearall3 ();
}
}
function clearall3 () {
removeChild (clothingArray [1]);
clothingArray[1].removeEventListener (Event.ENTER_FRAME , busstop);
}
So what Exactly it does is the movieclip in Frame 3 starts playing before it's even added to the Display list and i am not sure what is causing it...I cannot separately add the variable at this frame because there are other options in frame 1 that leads to me making an array.
Assuming that Sunny_Walkcycle, Sunny_BusStop, Teacher_Opening_Sunny are MovieClip, why don't you use stop() directly after the memory allocation ?
var sunny_walkcycle:Sunny_Walkcycle = new Sunny_Walkcycle ();
var sunny_busstop:Sunny_BusStop = new Sunny_BusStop ();
var sunny_opening:Teacher_Opening_Sunny = new Teacher_Opening_Sunny ();
sunny_walkcycle.stop();
sunny_busstop.stop();
sunny_opening.stop();
Be sure there is no code in your MovieClip inner frames that will mess with your control code.
Like a play()... hidden somewhere.
You could also try (for sunny_busstop), just after pushing in the array :
clothingArray[1].stop();
or
clothingArray[1].gotoAndStop(1);
Yes, a MovieClip will start playing once it is created. It doesn't need to be added to the Display List to be activated. This is very important detail to understand.
The Display List only determines whether or not a Display Object is connected to the stage hierarchy.
solution... call the stop() method after creation and then the play() method when you add them to the Display List.
If you don't call the stop() method when removing from the Display List, it'll just keep on eating up cpu cycles. If you have numerous MovieClips with tweens etc, that can be quite significant.

Resources