AS3 Remove items in an ENTER_FRAME - actionscript-3

I have generated random bubbles, I used a code I found in the net. Now I want a click event that will hide a random bubble.
Here is exactly the code I used,
http://good-tutorials-4u.blogspot.com/2009/04/flash-bubbles-with-actionscript-3.html
I got the bubbles running good...
I have tried this, and so far no luck..
addEventListener(MouseEvent.CLICK, eventListener);
function eventListener(eventObject:MouseEvent) {
bubbles[i].splice(i,1,bubbles[i]);
}
I tried using an array but it returns me this output
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at Function/()
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at Function/()

If you have the bubbles in an array this should work.
var randomIndex:int = int(Math.random() * bubbles.length);
parent.removeChild(bubbles[randomIndex]);
bubbles.splice(randomIndex, 1);
Notice that you have to remove the bubble from the display list too.

You can try creating a new array without a random element from the original array. Then just reassign the old array to the new one, e.g.
// get the random index to remove element at
var randomIndex:int = 0 + bubbles.length * Math.random();
var index:int = 0;
// create new array containing all apart from the chosen one
var newBubbles:Array = [];
for each (var item:Object in bubbles) {
if (index != randomIndex) {
newBubbles.push(item);
}
index++;
}
// here you go new array without one random item
bubbles = newBubbles;
Or something like these.

Just a minor revision on Baris Usakli's code here, this is if you want the one that was clicked on to be removed.
var bubbles:Array = [];
function makeBubbles()
{
for(var i:int=0;i<100;i++)
{
var bubble:Bubble = new Bubble();
bubbles.push(bubble);
addChild(bubble);
bubble.addEventListener(MouseEvent.CLICK, eventListener);
}
}
function eventListener(eventObject:MouseEvent) {
var clickedBubbleIndex:int = bubbles.indexOf(eventObject.currentTarget);
parent.removeChild(eventObject.currentTarget);
bubbles.splice(clickedBubbleIndex:int, 1);
}

try this
bubbles.addEventListener(MouseEvent.CLICK, eventListener); // place this listener in moveBubbles function.
function eventListener(eventObject:MouseEvent):void {
eventObject.currentTarget.visible = false;
}

Related

Remove the clicked objects

I'm trying to remove the array objects that are being clicked and add them into another array to display them else where. I posted the current code.
I think the problem maybe with .currentTarget. I tried replacing the .currentTarget to .target but the function wasn't getting past this line : if (socket_Array[i] == in_event.target) (in this version its .currentTarget, I am just saying when I tried changing it to .target)
The error I get is this:
TypeError: Error #1034: Type Coercion failed: cannot convert []#2c2a8f11 to flash.display.DisplayObject.
Function that creates the objects:
function createSockets():void
{
var socket_one:socket = new socket ();
var socket_two: socketyellow = new socketyellow ();
var socket_three: socketdarkorange = new socketdarkorange ();
var socket_four: socketlightgreen= new socketlightgreen ();
var socket_five: socketpurple = new socketpurple ();
var socket_six: socketdarkgreen = new socketdarkgreen ();
socket_Array.push(socket_one, socket_two,socket_three, socket_four, socket_five, socket_six);
for (var i:int=0; i<socket_Array.length; i++)
{
addChild(socket_Array[i]);
socket_Array [i].x = socket_x_position;
socket_Array [i].y = socket_y_position;
socket_Array[i].addEventListener(MouseEvent.MOUSE_DOWN, removeItemOnClick);
}
temp_update ();
}
Function that is suppose to get rid of the object clicked and add it to an array.
function removeItemOnClick(in_event:MouseEvent):void
{
var i:int = 0;
for (i=0; i<socket_Array.length; i++)
{
if (socket_Array[i] == in_event.currentTarget)
{
trace ("it goes here");
var removed = socket_Array.splice(i, 1);
trace (removed);
trace (socket_Array );
var drop:Sprite = in_event.currentTarget as Sprite;
removeChild (drop);
removedItem[removedItem.length] = removed;
createremovedItem ();
trace (removedItem);
updateDisplay ();
choice_updateDisplay ();
}
}
}
var removedItem_position = 0
function createremovedItem () {
for (removedItem_position; removedItem_position<removedItem.length; removedItem_position++){
addChild (removedItem [removedItem_position]);
}
}
First of all, .currentTarget is correct.
Secondly, there's no point in calling removeChild() and then calling addChild(). The net effect of both calls is nothing.
Almost all of the code in the second function is unnecessary. Here's a shorter version:
function removeItemOnClick(in_event:MouseEvent):void {
var index:int = socket_Array.indexOf(in_event.currentTarget);
var drop:Sprite = socket_Array.splice(index,1) as Sprite;
removedItem.push(drop);
updateDisplay();
choice_updateDisplay();
}
If you want to display the new item elsewhere, just change drop.x and drop.y.
As the error suggests it looks like it is problem with type coercion.
Try to replace the condition of your if statement with this:
if (DisplayObject(socket_Array[i]) == DisplayObject(in_event.currentTarget))
In case this is not working your might have more information while debugging by storing the two objects you want to compare into temporary variables

How to call removeEventListener when using addEventListener with parameters passing in AS3

I am trying to make a object, which is a movieclip, move up and down continuously and print its y-axis value whenever it finished a move. The below code works fine.
var tweenUp:Tween = null, tweenDown:Tween = null;
function up():void {
tweenUp = new Tween(person,"y",None.easeNone,person.y,person.y+20,1,true);
tweenUp.addEventListener(TweenEvent.MOTION_FINISH, finishedUp);
}
function down():void {
tweenDown = new Tween(person,"y",None.easeNone,person.y,person.y-20,1,true);
tweenDown.addEventListener(TweenEvent.MOTION_FINISH, finishedDown);
}
function finishedUp(event:TweenEvent):void {
trace(person.y);
tweenUp.removeEventListener(TweenEvent.MOTION_FINISH, finishedUp);
tweenUp = null;
down();
}
function finishedDown(event:TweenEvent):void {
trace(person.y);
tweenDown.removeEventListener(TweenEvent.MOTION_FINISH, finishedDown);
tweenDown = null;
up();
}
up();
However, I am looking for a solution to pass a object to the callback function of listener. I try to use the way shown below but it does not work.
var tweenUp:Tween = null, tweenDown:Tween = null;
var functionFinishedUp:Function = null, functionFinishedDown:Function = null;
function up(object:MovieClip):void {
tweenUp = new Tween(object,"y",None.easeNone,object.y,object.y+20,1,true);
functionFinishedUp = finishedUp(object);
tweenUp.addEventListener(TweenEvent.MOTION_FINISH, functionFinishedUp);
tweenUp.removeEventListener(TweenEvent.MOTION_FINISH, functionFinishedUp);
tweenUp = null;
}
function down(object:MovieClip):void {
tweenDown = new Tween(object,"y",None.easeNone,object.y,object.y-20,1,true);
functionFinishedDown = finishedDown(object);
tweenDown.addEventListener(TweenEvent.MOTION_FINISH, functionFinishedDown);
tweenDown.removeEventListener(TweenEvent.MOTION_FINISH, functionFinishedDown);
tweenDown = null;
}
function finishedUp(object:MovieClip):Function {
return function(event:TweenEvent):void {
trace(object.y);
down(object);
}
}
function finishedDown(object:MovieClip):Function {
return function(event:TweenEvent):void {
trace(object.y);
up(object);
}
}
up(person);
It just goes up and then do nothing because it seems that the listener is removed just after added.
Is there any good solution to remove the listener which has parameters passing, after the listener finished its task?
Thanks in advance for any help you are kind enough to provide!
If you're looking for an easy way to remove an event listener right after the event was received, you can do this:
function listener(event:Event):void
{
// stop listening to the dispatcher for this event type
EventDispatcher(event.target).removeEventListener(event.type, arguments.callee);
// ...and do whatever else you need to do here
}
That line can be used in any event listener.
Another option would be to use Signals (https://github.com/robertpenner/as3-signals). They have an addOnce method that will only listen once and then remove themselves.
First, I advise against using object as the name of a variable! Although technically allowed it is semantically meaningless and unhelpfully similar to the reserved class name Object.
The reason your code isn't working is that you are removing the event handler immediately after adding -- before it ever has a chance to get invoked. You really don't ever have to remove the listeners, because you want them to be invoked every time the respective tweens are complete. If you insist on attaching and detaching the listener functions every iteration you'll have to remove the event listener in the listener function itself. Of course this gets rather tricky with all those closures you're creating and tossing.
In fact you don't need the closures at all, since the tween objects have a reference to the tweened object in the obj property. In addition, you only need to create one up tween and one down tween for each target person, and can then just rewind() and start() each tween back and forth. You can keep track of which tween goes with which person using a couple of Dictionary objects, which use object references as the keys.
Here's a proof of concept that generates twenty persons all with their own set of tweens -- but only two handlers are defined and neither of them are closures. I've also combined up with finishedDown and down with finishedUp:
var upTweens:Dictionary = new Dictionary();
var downTweens:Dictionary = new Dictionary();
for(var i:uint = 0; i < 20; i++) {
// Make a new Person
var person:Person = new Person();
person.y = 100;
person.x = i * 20;
addChild(person);
// Create the tweens but stop them before they can play at all
var top = person.y - 20; // Up is -y in Flash
var bottom = person.y;
var upTween:Tween = new Tween(person, "y", None.easeNone, bottom, top, 1, true);
upTween.stop();
upTween.addEventListener(TweenEvent.MOTION_FINISH, onUpFinished);
var downTween:Tween = new Tween(person, "y", None.easeNone, top, bottom, 1, true);
downTween.stop();
downTween.addEventListener(TweenEvent.MOTION_FINISH, onDownFinished);
// Associate tweens with this person, and start it up!
upTweens[person] = upTween;
downTweens[person] = downTween;
upTween.start();
}
function onUpFinished(e:TweenEvent):void {
var upTween:Tween = Tween(e.currentTarget);
var person:Person = Person(upTween.obj);
var downTween:Tween = Tween(downTweens[person]);
downTween.rewind();
downTween.start();
}
function onDownFinished(e:TweenEvent):void {
var downTween:Tween = Tween(e.currentTarget);
var person:Person = Person(downTween.obj);
var upTween:Tween = Tween(upTweens[person]);
upTween.rewind();
upTween.start();
}

Why can't my class see an array on the timeline?

I have a class that controls an enemy. From within that class, it checks for collisions with an array on the main timeline. I've done it this way before and it works fine, so I have no idea what I've done wrong this time. It keeps giving me an
ReferenceError: Error #1069: Property
bulletArray not found on
flash.display.Stage and there is no
default value.
error from within the enemy class.
Here's my code (shortened to remove the unimportant parts):
On timeline:
var bulletArray:Array = new Array();
function shoot(e:TimerEvent)
{
var bullet:MovieClip = new Bullet(player.rotation);
bullet.x = player.x;
bullet.y = player.y;
bulletArray.push(bullet);
stage.addChild(bullet);
}
In class:
private var thisParent:*;
thisParent=event.currentTarget.parent;
private function updateBeingShot()
{
for (var i=0; i<thisParent.bulletArray.length; i++) {
if (this.hitTestObject(thisParent.bulletArray[i]) && thisParent.bulletArray[i] != null) {
health--;
thisParent.bulletArray[i].removeEventListener(Event.ENTER_FRAME, thisParent.bulletArray[i].enterFrameHandler);
thisParent.removeChild(thisParent.bulletArray[i]);
thisParent.bulletArray.splice(i,1);
}
}
Any help would be greatly appreciated! Thanks.
My guess is that event.currentTarget is the instance where you declared the bulletArray variable. Using event.currentTarget.parent will refer to stage outside your scope. I donĀ“t know how you declare the listeners. Try using event.target instead of event.currentTarget and see if you get the same error.
My advice is that you put all your code in a class.
If you are going to do it this way you need to pass in a reference to the timeline.
private var _timeline:Object;
// constructor
public function YourClass(timeline:Object) {
_timeline = timeline;
}
private function updateBeginShot() {
// ..
trace(_timeline.bulletArray); // outputs [array contents]
// ..
}

accordion menu error 1010

I'm trying to create an accordion menu off of an online tutorial. I followed every step (i think) on Original Tutorial but changed things according to my size, as well as make the instance names end with _mc or _txt accordingly. But for some reason it doesn't seem to be working.
I'm getting the #1010 error and it doesn't really clarify anything:
TypeError: Error #1010: A term is
undefined and has no properties.
at
tutorial_fla::MainTimeline/placeItemsOnStage()
at
tutorial_fla::MainTimeline/onComplete()
at
flash.events::EventDispatcher/dispatchEventFunction()
at
flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
I have my first xml file that has all the images to insert and that one hasn't been changed at all should work fine.
I know this is long, but I'm hoping someone can check it out anyhow. I'm extremely new to this and would pretty much have to abandon the whole project. Thanks so much!
my code:
//import tweenlite classes
import gs.TweenLite;
import gs.easing.*;
var MENU_ARRAY:Array; //used to save the items data
var OPENED_MENU:int; //to inform the menu that should be open at startup
var MOVE_ON_MOUSE_OVER:Boolean=false; //tha name says everything
var xmlLoader:URLLoader; //the xml loader
loadXML("menu2.xml"); //load the xml
function loadXML(Uri:String):void {
xmlLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE, onComplete);
xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
xmlLoader.load(new URLRequest(Uri));
}
function onError(evt:ErrorEvent):void {
trace("cannot load xml file");
}
function onComplete(evt:Event):void {
//read and load xml into array, by parsing it using prepareMenu
MENU_ARRAY=prepareMenu(xmlLoader.data.toString());
placeItemsOnStage(); //place all required items on stage.
}
function placeItemsOnStage():void {
var pos:Number=0;
//define the container properties
menuContainer_mc.x=0;
menuContainer_mc.y=0;
for(var c:int=0; c<MENU_ARRAY.length; c++) {
var it:menuItem = new menuItem; //load out menuItem, because its exported to AS, we can use it here
it.x=c*51; //its the gray border width
it.y=0; //place on top
it.id="Item-"+c; //id the menuItem
it.name="Item-"+c; //name de menuItem
it.posX=pos; //actual pos, useful to open and know is position
it.itemLabel_txt.text=String(MENU_ARRAY[c].Ititle).toUpperCase(); //load the label in uppercase
it.addEventListener(MouseEvent.CLICK, onMouseClick); //add mouse click listener
if(MOVE_ON_MOUSE_OVER==true) it.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver); //if configured, load the mouse over event
it.useHandCursor=true; //use hand cursor
it.buttonMode=true; //buttonMode
it.itemText_txt.visible=false; //hide the textField
menuContainer_mc.addChild(it); //add the menu item as child
if(String(MENU_ARRAY[c].IcontentType)=="image/swf") { //check the content and load things accordint to it
var ldr:Loader = new Loader();
ldr.x=51;
ldr.y=0;
it.addChild(ldr);
ldr.load(new URLRequest(MENU_ARRAY[c].IcontentData.toString()));
}
else if(String(MENU_ARRAY[c].IcontentType)=="text") {
it.itemText_txt.visible=true;
it.itemText_txt.text=MENU_ARRAY[c].IcontentData.toString();
}
pos+=51; //the next menuItem x position
}
//put mask in place
masker_mc.width=(MENU_ARRAY.length*51+700)
masker_mc.height=menuContainer_mc.height;
masker_mc.x=0;
masker_mc.y=0;
moveItem(OPENED_MENU-1); //open menu confirured in XML
}
function onMouseOver(evt:MouseEvent):void {
if(evt.target.name.toString()=="buttonBack") prepareMove(evt)
}
function prepareMove(evt:MouseEvent):void {
var targetName:String = evt.currentTarget.name.toString(); //get the menuItem
var temp:Array = targetName.split("-"); //split his name: Item-x
var itemNumber:int=(temp[1]); //got item number
moveItem(itemNumber); //move it
}
function onMouseClick(evt:MouseEvent):void {
if(evt.target.name.toString()=="buttonBack") prepareMove(evt); //mouse action was done in buttonBack
else trace("Item "+evt.currentTarget.name+" clicked!"); //mouse action was made on accordion area
}
function moveItem(num:int):void {
var itemToMove:menuItem=menuContainer_mc.getChildByName("Item-"+String(num)) as menuItem;
//get the menuItem child
for(var m=0;m<MENU_ARRAY.length;m++) //move one-by-one to the new position
{
var tempMc = menuContainer_mc.getChildByName("Item-"+m);
if(tempMc.x > itemToMove.x) TweenLite.to(tempMc, 1, {x:((tempMc.posX) + itemToMove.width-51), ease:Quart.easeOut}); //see tweenLite for info about this.
else if(tempMc.x <= itemToMove.x) TweenLite.to(tempMc, 1, {x:(tempMc.posX), ease:Quart.easeOut});
}
}
function prepareMenu (XMLData:String):Array
{
//make sure it cames in XML
var menuXML:XML = new XML(XMLData);
//just in case
menuXML.ignoreWhitespace = true;
//get XML item's entrys
var XMLItems = menuXML.descendants("item");
//load all items into an array
var itemsArray:Array = new Array();
var itemObj:Object;
for(var i in XMLItems)
{
itemObj=new Object();
itemObj.Ititle=XMLItems[i].#Ititle;
itemObj.IcontentType=XMLItems[i].#IcontentType;
itemObj.IcontentData=XMLItems[i].#IcontentData;
itemObj.itemID="menu"+i;
itemsArray.push(itemObj);
}
OPENED_MENU=menuXML.#menuOpen; //get menu for open.
MOVE_ON_MOUSE_OVER=(menuXML.#moveOnMouseOver.toString()=="true" ? true : false); //get the option for load or not mouseOver open
return itemsArray;
}
//finish.
stop();
"term" refers to an identifier, "Error #1010: A term is undefined and has no properties" means you are using an expression which cannot be evaluated. This usually happens when you try to access and use an undefined object property via its string name as in myobject["property"].method(). Can you provide the exact line on which the exception is raised?

Add multiple movieclips, not replacing the old ones

So, in short, my problem is this. I am using a variable which is a movieclip loaded from an external swf. I want to "spawn" multiple instances of the movieclip that all react to the same code, so for example if I say var1.x = 100, they all are at 100x. But my problem is when I run addChild(var1) multiple times(I'm not actually typing in addChild(var1) over and over, I just have it set to add them at random times), the new child just replaces the old one, instead of making multiple movieclips. Should I do something like
var var1:MovieClip
var var2:MovieClip = new var1 ?(which doesnt work for me btw, gives me errors)
Oh, heres the code, and also, I am pretty new to as3 fyi, still don't even know how arrays work, which was my second guess to the problem.
var zombieExt:MovieClip;
var ldr2:Loader = new Loader();
ldr2.contentLoaderInfo.addEventListener(Event.COMPLETE, swfLoaded2);
ldr2.load(new URLRequest("ZombieSource.swf"));
function swfLoaded2(event:Event):void
{
zombieExt = MovieClip(ldr2.contentLoaderInfo.content);
ldr2.contentLoaderInfo.removeEventListener(Event.COMPLETE, swfLoaded2);
//zombieExt.addEventListener(Event.ENTER_FRAME, moveZombie)
zombieExt.addEventListener(Event.ENTER_FRAME,rotate2);
function rotate2 (event:Event)
{
var the2X:int = playerExt.x - zombieExt.x;
var the2Y:int = (playerExt.y - zombieExt.y) * 1;
var angle = Math.atan(the2Y/the2X)/(Math.PI/180);
if (the2X<0) {
angle += 180;
}
if (the2X>=0 && the2Y<0) {
angle += 360;
}
//angletext.text = angle;
zombieExt.rotation = (angle*1) + 90;
}
playerExt.addEventListener(Event.ENTER_FRAME,spawn1);
function spawn1 (event:Event)
{
if(playerExt.y < 417)
{
var someNum:Number = Math.round(Math.random()*20);
if(someNum == 20)
{
addChild(zombieExt)
zombieExt.x = Math.round(Math.random()*100)
zombieExt.y = Math.round(Math.random()*100)
}
}
}
}
addChild() does not create new instances. It is used to add an already created instance to the display list. If you call addChild() multiple times on the same instance then you are just readding itself.
Also each instance is unique, you can not globally change the x position of an instance by changing another one of them. What you would do is as Henry suggests and add each new instance of a MovieClip into an array, then whenever you change something you can loop through the array and apply the changes to each instance.
You can not go var2:MovieClip = new var1 either since var1 is an instance and not a class.
Here's a different method of receiving loaded MovieClips, which i use when i need many copies of the item.
in the swf you are loading, give the target movieclip a linkage name in the library, for this example i will use "foo"
private var loadedSwfClass:Class
private var newZombie:MovieClip;
private var zombieArray:Array = new Array();
function swfLoaded2(event:Event):void
{
loadedSwfClass = event.target.applicationDomain.getDefinition("foo");
for(var n:int = 0; n<100; n++){
newZombie = new loadedSwfClass()
zombieArray.push(newZombie);
addChild(newZombie);
}
}
as per this tutorial
http://darylteo.com/blog/2007/11/16/abstracting-assets-from-actionscript-in-as30-asset-libraries/
although the comments say that
var dClip:MovieClip = this;
var new_mc = new dClip.constructor();
this.addChild(new_mc);
will also work.
It sounds like you might be accessing the same instance some how in your code. It would be helpful to see your code to figure this one out.
If I wanted to load in one swf files and add a MovieClip multiple times I would place it in the library of that SWF file. And then instantiate it and store it into an object pool or a hash or some list.
// after the library were finished loading
var list:Array = [];
for(var i:int=0; i<10; i++) {
var myCreation:MySpecialThing = new MySpecialThing();
addChild(myCreation);
list.push(myCreation);
}
where my library would contain a linkage to the class MySpecialThing.
Calling addChild(var1) multiple times on the same parent doesn't have any effect (unless you have added another child to the same parent in between, in which case it will change the child index and bring var1 to the top). If you call it on different parents, it will just change the parent of var1, doesn't duplicate. Call addChild(new MovieClassName()) at random times instead to add new copies of it. Use an array as suggested here to access them later.
Wow, thanks there henry, just using an array did exactly what I needed, and made things alot simpler.
when you load in using a loader you only get 1 instance, however you can do some funky reflection to determine what class type the given loader.content is, and then instantiate them using that. For Example:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_completeHandler);
loader.load(new URLRequest("ZombieSource.swf"));
var classType:Class;
function loader_completeHandler(evt:Event):void
{
var loadInfo:LoaderInfo = (evt.target as LoaderInfo);
var loadedInstance:DisplayObject = loadInfo.content;
// getQualifiedClassName() is a top-level function, like trace()
var nameStr:String = getQualifiedClassName(loadedInstance);
if( loadInfo.applicationDomain.hasDefinition(nameStr) )
{
classType = loadInfo.applicationDomain.getDefinition(nameStr) as Class;
init();
}
else
{
//could not extract the class
}
}
function init():void
{
// to make a new instance of the ZombieMovie object, you create it
// directly from the classType variable
var i:int = 0;
while(i < 10)
{
var newZombie:DisplayObject = new classType();
// your code here
newZombie.x = stage.stageWidth * Math.random();
newZombie.x = stage.stageHeight * Math.random();
i++;
}
}
Any problems let me know, hope this helps.