Issues correctly removing childs from flash - actionscript-3

hey im having issues removing my enemy blocks. at the moment if i hit everyone of them everything is fine but when i avoid one i get an error message of
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
at EnergyJump/onTick()
at flash.utils::Timer/flash.utils:Timer::_timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()
here is my code i have:
public function onTick( timerEvent:TimerEvent ):void
{
//if ranrom number is than than i
if ( Math.random() < i )
{
//place block on stage at location X=550, Y=330
var randomX:Number = Math.random() * 550;
var newBlock:Blocks = new Blocks( 550, 335 );
army.push( newBlock );
addChild( newBlock );
//increase speed of spawn
i = i + 0.0001;
}
//move blocks in correct direction
for each ( var block:Blocks in army )
{
block.move();
//if block is hit then remove health and remove child object
if ( avatar.hitTestObject( block ) )
{
hp.checkHP(20);
army.splice(army.indexOf(block), 1);
removeChild( block );
}
}
}
can anyone help me, i dont really know what slice is to be honest or how to use it...

You should have a look at the documentation for Array.splice() here.
The first argument needs to be the index (0, 1, 2 etc.) of the item you want to remove, not the item itself. Flash is trying to read block as an integer, but it defaults to 0, so instead of removing the block that has been hit it's just removing the first block in the list. Try this instead:
army.splice(army.indexOf(block), 1);
I assume you have some code which is clearing any remaining blocks in the list at the end of the game, but because the wrong blocks are being removed from the list it's trying to remove some that were actually hit already.

Are you sure there is a corresponding addChild() call for each of those objects that has been made before the call to removeChild()? There's not enough code being shown, at the moment, to be able to really tell what's going on, but also make sure removeChild() isn't being called more than once on the same object without addChild() being called in between each time.

Okay, I had a quick look through your files. It's getting a bit off topic for this question, but I'll list the problems I found. In general though you need to look at the parts Flash is complaining about and make sure you're really working with the right variables (eg. if you write block, try to make sure you know which block Flash is going to look at, and remember that the order matters when you change things.)
It's easy to accidentally remove the wrong items or try to use things that are null, so check each line and think about what each variable actually is at that point (maybe try tracing the variables out too).
In avatarEnterFrame you're checking for blocks that have gone off the side of the screen, but you haven't added a for each loop like in onTick, so when you use block there Flash is looking at your main public var block:Blocks; instead of the blocks in army.
In onPowerTick you need to adjust your splice in the same way as before, so that you remove the powerups object you're checking instead of the item at 0.
In restartGame you're setting gameOverScreen to null just before trying to remove it, so Flash doesn't know what to remove. Make sure you leave setting it to null until you're done with everything else.
I'll post a separate answer for your game over screen problem so that it's in the right place.

Related

Check the existence of an object instance

I'm surprised I don't know how to do this, but as it turns out I really don't; simply put, I'm trying to make a side-scrolling shooter game, a basic one and in it, I have 50 stars spawned on-screen through a "for" loop upon the game starting. There is a function which does this and a listener is at the beginning. Problem is, when you lose the game and go back to main menu, 50 more stars would be spawned, which isn't what I want. So, I'm trying to make an "if" statement check at the beginning, so that the game checks whether there is an instance/movie clip of the star object/symbol before determining whether the function that spawns stars should be called out with a listener. So, how do I do this? I looked through some other checks and they didn't help as the codes presented were vastly different there and so I'm just getting errors.
Let me know if a better explanation is needed or if you would like to see some of the code. Note that the game overall already has a lot of code, so just giving all of it would probably not be helpful.
I suggest you rethink your approach. You're focusing on whether stars have been instantiated. That's ok but not the most basic way to think about it.
I would do this instead
private function setup():void{
loadLevel(1);
addListeners();
loadMusic();
// etc...
// call all functions that are needed to just get the app up and running
}
private function loadLevel(lev:int):void{
addStars();
// call all functions that are needed each time a new level is loaded
}
private function restartLevel():void{
// logic for restarting level,
// but this *won't* include adding star
// because they are already added
}
There are other ways to do this but this makes more sense to me than your approach. I always break my game functions into smaller bits of logic so they can be reused more easily. Your main workhorse functions should (IMHO) primarily (if not exclusively) just call other functions. Then those functions do the work. By doing it this way, you can make a function like resetLevel by assembling all the smaller functions that apply, while excluding the part about adding stars.
Here's what I did to solve my problem... Here's what I had before:
function startGame():void
{
starsSpawn();
//other code here
}
This is what I changed it to:
starsSpawn();
function startGame():void
{
//other code here
}
when you said existance, so there is a container, i named this container, (which contain stars , and stars was added to it) as starsRoot, which absolutely is a DisplayObject (right?)
now, to checking whole childrens of a DisplayObject, we have to do this :
for (var i:int=0; i<starsRoot.numChildren; i++) {
var child = starsRoot.getChildAt[i];
}
then, how to check if that child is really star!?
as you said
whether there is an instance/movie clip of the star
so your stars's type is MovieClip, and they don't have any identifier (name), so how to find them and make them clear from other existing movieclips. my suggestion :
define a Linkage name for stars from library, thats a Class name and should be started with a capital letter, for example Stars
now, back to the code, this time we can check if child is an instance of Stars
for (var i:int=0; i<starsRoot.numChildren; i++) {
var child = starsRoot.getChildAt[i];
if (child is Stars) {
// test passed, star exist
break;
}
}

AS3: Addchild dynamically using varying identifier names

Im currently learning as3 but I come from several years of as2 experience, so what I was used to do is attach movieclips using string with a numeric variable to get the right identifier like this and maybe I was thinking about using eval here but of course its not available in as3 so here is what I was doing in as2:
currentlevel=1;
myString = "levelMc"+currentlevel;
And then of course use the constructed string in "attachMovie" function to get the right clip attached.
attachMovie(myString,myString+mydepth,mydepth);
But I see in as3 all is about classes and all different so I cant seem to find a way to dynamically get the right class name for the child to be added, is there an actual way to make that possible? because as far as I have gone I see that I should get the class name to make new object and then put that in "addChild" so therefore I cant find a way to pass any kind of dynamically constructed string or element refering my needed clip there, is it still possible?.
Thanks beforehand for any clues =)
EDIT I added the attachmovie call as a clarifying line, I know all you got what I was asking but as I cant find a right approach I prefer to clarify. Additionally I shouldnt have mentioned the "eval" here, sorry, I just can ask my as2 with string usage
EDIT Solved, thanks to you all guys, you saved me, I wish I could give you all kudos because all of your ideas and tips are awesome and really helpful, I marked that answer as correct because it was the one that lead me to getDefinitionByName which is what Im going to use in the end, but as said all tips and ideas to face new as3 world will help me greatly, so as said I wish I could give reputation to you all, thanks a lot =)
You still can set a name for your object, for instance:
var mc : MovieClip;
for (var i : int = 0; i < 5; i++) {
mc = new MovieClip();
mc.name = "myMc"+ i;
addChild(mc);
}
and you can get a specific child using:
this["myMc1"] as MovieClip
or
this["myMc"+ i] as MovieClip;
How about a simple array?
Because if you only need to access a set of levels in a ascending numeric fashion, this approach is easier.
var levels:Array = new Array();
levels[0] = //your level1 movieclip
levels[1] = //your level2 movieclip
...
function jumpToLevel(index:int):void{
//remove added levels beforehand
removeChildren(); //assuming the only children you added to this displaylist
//are levels, otherwise you will need another approach
addChild(levels[index]);
}
In AS2, we would store information about instances in instance name - button_0, button_1, etc. In AS3, you don't need/use instance names for anything other than keeping your code clear to you and to reference stuff you create by hand.
Use arrays to reference your instances. So, if you're making buttons, you create a button and push it into an array of buttons. Then just reference it as aMyButtons[i] instead of mcMyButtons["button_" + i]
Here's example code.
function fCreateButtons(): void {
var i: int = 0;
for (i = 0; i < 5; i++) {
var mcNewButton: McButton = new McButton();
mcNewButton.x = i * 200;
mcNewButton.iButtonNumber = i;
mcNewButton.tMain.text = "About Us";
mcNewButton.bButton.addEventListener(MouseEvent.MOUSE_DOWN, fButtonPress);
mcNewButton.bButton.addEventListener(MouseEvent.MOUSE_OVER, fButtonOver);
mcNewButton.bButton.addEventListener(MouseEvent.MOUSE_OUT, fButtonOut);
aButtons.push(mcNewButton);
}
}
function fButtonPress(e: MouseEvent): void {
var iButtonNumber: int = e.currentTarget.parent.iButtonNumber;
trace("fButtonPress(): iButtonNumber: " + iButtonNumber);
}
This is how you'd add the buttons to a holder clip and remove them.
mcButtonsHolder.addChild(aButtons[i]);
mcButtonsHolder.removeChild(aButtons[i]);
Well as I said above I managed to solve it finally using getDefinitionByName which is the closer I think I can get to "eval", that function managed to bring dinamycally formed class name to be used to call the new clips on run time which is perfect for me, at least make the code cleaner and easier to change if needed. As I said in last edit, thanks a lot to you all who helped, all your tips and ideas will be of great use for me in the as3 time, wish I could give you all reputation, thanks a lot =).

Box2d MovieClip to original position

I want to try a simple task where if i move a object inside the world and then press a button it should go back to its original position , but its not working , below is the code i am using - the file is here - http://www.fastswf.com/yAnIvBs (when i remove the event listener)
with event listener - http://www.fastswf.com/rpYsIt8
////////========================
stop();
var startXPos:Number = level1WorldObj.box1.x;
var startYPos:Number = level1WorldObj.box1.y;
function areaS(e:Event) {
level1WorldObj.box1.y= startYPos;
level1WorldObj.box1.x= startXPos;
level1WorldObj.box1.removeEventListener(Event.ENTER_FRAME, areaS);
}
but1.addEventListener(MouseEvent.CLICK,nClick3);
function nClick3(event:MouseEvent):void{
level1WorldObj.box1.addEventListener(Event.ENTER_FRAME, areaS);
level1WorldObj.box1.y= startYPos;
level1WorldObj.box1.x= startXPos;
}
/////////////////======================
Now i want to be able to do it many time so i kept the variables that detect the initial x, y as global ...
Here you can see how it behaves in debugdraw mode , strangely only the clip moves not the actual body - http://www.fastswf.com/-Ijkta4
Can some one please guide me here ...
Thanks in advance ...
Jin
The graphics that you see (box1) aren't related to the physical object behind the scenes - you're currently only moving the graphics not the object itself.
You need to use either SetPosition() or SetTransform() on the b2Body of the object
Edit 07/7
As you're using the Box2D World Construction Kit, I took a look at the source code (available here: https://github.com/jesses/wck). The main class seems to be BodyShape (https://raw.githubusercontent.com/jesses/wck/master/wck/BodyShape.as).
Looking through it, you should be able to access the b2Body directly. If it's null (which is probably the source of the TypeError that you're getting, then you haven't called createBody(), which is what actually takes all of your properties as creates the physical object behind the scenes.
Once you have a b2Body, if you want to position it based on the graphics, there's a function syncTransform() to do just that.
You should turn on debugDraw on your World class to make it easier to see what's going on in the background. NOTE: this needs to be done before calling create()
I was able to find solution to this problem , i found the starting point by using this -
trace(level1WorldObj.box1.b2body.GetPosition().x);
trace(level1WorldObj.box1.b2body.GetPosition().y);
then once i had the position manually i took down the coordinates and used the below code ....
level1WorldObj.box1.b2body.SetTransform(new V2(-2, 2),0 );
Thanks #divillysausages for all the help ...
Regards

AS3 Not Adding Sprite to MovieClip

My code is simply looping through an xml file and creating 'pages' (which are later animated).
This has all worked fine but now I want to add a sprite over the entire contents of the page if the contents of the xml contain a URL.
At run-time I can see that the checks for the URL are being processed correctly and that the overlay is being generated, but I cannot "see" it on the page.
The following code is located in a for loop for every page in the xml file:
var page:Page = new Page(); //MovieClip in my library
// ... other stuff
var textMC:FadeText = new FadeText(xml); //load the text from the xml fragment for this page
//if the text contains a URL (using RegExp)
if(textMC.hasLink())
{
var button:Sprite = new Sprite();
button.graphics.beginFill(0x000000);
button.graphics.drawRect(0, 0, 1, 1);
button.name= textMC.getLink();
button.x = button.y = button.alpha = 0;
button.width = rectangle.width;
button.height = rectangle.height;
button.buttonMode = true;
button.addEventListener(MouseEvent.CLICK, goToUrl, false, 0, true);
page.addChildAt(button, page.numChildren);
}
//... more code - such as add page to stage.
From the console (using FireBug and FlashBug) the button is being created, but I cannot see it on screen so I am guessing the addChild bit is at fault.
What is wrong and how do I fix it?
[edit]
Having set the alpha to 1 I can see that the overlay IS being added to the page, but it is not changing my cursor or responding to mouse clicks.
I now believe it is something wrong with the XML. It is correctly parsed XML (otherwise FlashPlayer would throw exceptions in my face) and it appears that this code works on every page except the second. Further more, if the second page is set as visible (a flag in the XML determins if the page is created or not) then none of the other pages overlay works.
Sorry to necro this thread but one thing I can think of is that because you specify a z-position to place your page it might be that the z-position generated by (i+1) is not the next one in line. AS3 doesn't allow display-objects to be placed on 'layers' with empty 'layers' between them which was allowed in AS2.
My guess is that during the loop at one point or another the loop doesn't generate a page which leaves an empty layer. The reason why the stage.addChild(page) actually works is because it simply searches for the next empty layer in that stack because you don't specify it.
button.x = button.y = button.alpha = 0;
set alpha to 1
button.alpha = 1;
and check for rectangle.width , rectangle.height
last thing, check for textMC.hasLink() if its true or not. If its true, there is another problem with your code that is not related to this sample code.
Illogical answer:
Replaced stage.addChildAt(page,i+1); with stage.addChild(page);.
I was clutching at straws. Have spent FAR too long working on this blip, but it works! I don't know WHY it works, and at this point I don't care; IT WORKS!!! (sorry for the unprofessionalism however I have spent two and a half days working on this and have just got it working!)
If someone wants to explain why it works, feel free. I would VERY much prefer to learn why this occurs that struggle to work around it.

AS3 stage.addChild / stage.removeChild << Must be child of caller

If im usin function to add a mc to the stage like so:
var myChild:MC= new MC();
function somefunc()
{
stage.addChild(myMC)
}
but when I try to remove the mc by:
stage.removeChild(myMC)
I get The supplied DisplayObject must be a child of the caller error...
any suggestions or work arounds?
Your code should work if the item is on the stage. Perhaps qualifying it with a conditional statement like so:
if (myMC.stage != null)
stage.removeChild(myMC);
Alternatively you could use the following code but it is probably not best practice.
if (myMC.parent != null)
myMC.parent.removeChild(myMC);
The problem is not with removeChild. It's with the displaylist. If you check the parent property of the displayobject, when you call "removeChild" it will be null.
Why does it become null could be because of lots of reasons:
Parent is nulled before the child.
The child or parent have event listeners that won't let them die.
The Display Object is really not the instance you're trying to remove. THIS one can be very tricky to find out. Look at the "name and parent properties" of the variable you're trying to remove while calling removeChild.
You could try hiding and showing the movieClip, if possible.
I think its a bit faster than removing and adding consistantly, code permitting.
Keep in mind this is just a suggestion, someone smarter than me outta be able to help you out..
You could also use this fail safe:
if(myMC.parent) myMC.parent.removeChild(myMC);
I could fix this problem by simply removing every EventListeners I added to that object before removing it.