Argument Error #2025:
The supplied DisplayObject is not a child of the caller
In my program I have a structure of display objects representing enemy units on screen, arranged like this:
0: "enemy handler" ➔ [object EnemyHandler]
0: "enemy" ➔ [object Enemy1]
0: "enemy's skin"(MovieClip) ➔ [object EnemySkin1]
0: "skin image" ➔ [object Shape]
The document class contains one instance of EnemyHandler, and it contains multiple instances of Enemy1, Enemy2 ect.. each of these has a skin MovieClip attatched.
The Enemy class (from which Enemy1/2/3 ect inherits) contains a property expired, which is set to true, from within the Enemy class (enemy.update), when the Enemy has reached a certain point.
In the enemy handler class is a function, and is where the problem lays, that loops through an array of all the Enememy display objects, updating there position and then,
if(tempEnemy.expired)
{
tempEnemy.destroy(); // removeChild(skin)
enemyList.splice(tempEnemy);
removeChild(tempEnemy)
}
when run, if all of the enemies reach the end-point in the order in which they were created, there is no problem, however if one for instance travels faster, and reaches the endpoint before, an error #2025 (The supplied DisplayObject is not a child of the caller) is thrown.
I've narrowed it down to the fact that the program attempts to remove the enemy twice, for reasons that i cannot discern. It loops through and removes the desired enemies, then attempts to remove it again, even though it is spliced from the array (which happens correctly, and the first attempt at removeChild is allways succesfull)
It is probably something fairly simple on my behalf, so forgive me, but any help is appreciated. here's the files;
[code on frame]
pastebin.com/vcXzQpr9
[Enemy.as]
pastebin.com/RNXgK8Ex
[EnemyHandler.as]
pastebin.com/6fytxbMW
[Enemy0.as] & [Enemy1.as]
pastebin.com/5bW3Aa0H
[Utils.as]
pastebin.com/PQ2LPV0v
[traceDl.as] {debuggnig the display list}
pastebin.com/9vQGKcYP
Array.splice() takes an integer for the position where you want to start splicing, then how many items you want to remove. So you need to use
enemyList.splice(enemyList.indexOf(tempEnemy), 1);
instead of passing the Enemy instance. It may have appeared to work correctly when the order didn't change, because coercing tempEnemy to int (which splice() will have done automatically) produces a zero, so in your destroyEnemy function it was actually just removing the first item in the list each time.
Related
I am making a simple jumper game in AS3 in Flash, and at this moment everything works but I get this note:
Error #1034: Type Coercion failed: cannot convert 2 to flash.display.Scene.
at scratch_theGame_kat_fla::MainTimeline/startkeyPressed()
I understand there must be a type of an instance that is incorrect when this note appears, but as I search around in my code to find the perpetrator, I realized the "2" that it must be referring to is this:
function startkeyPressed(event:KeyboardEvent):void
{
if (event.keyCode,Keyboard.ENTER)
{
trace("new player");
startGame = true;
gotoAndPlay(( 1, Scene (2)));
}
};
This part of the code is what makes it go (when ENTER is pressed) from scene 1 to scene 2, where the actual game begins. Does anyone have any idea what I should change?
That line makes absolutely no sense in terms of AS3 logic:
gotoAndPlay(( 1, Scene (2)));
If you look into "Example" section of the official gotoAndPlay(...) help, there are two possible uses of this method:
You pass a single "frame" argument: it is either a 1-based int frame index, or it is a String label of the desired frame.
Two arguments where the second one is the String name of a Scene that is a portion of the main timeline, and the first is, again, the frame index or frame label inside the said Scene.
Now let's decipher what you have there. You are passing as gotoAndPlay(...) arguments. Feel the difference:
// Your version: one argument grouped with additional brackets.
gotoAndPlay(( 1, Scene (2)));
// Alternate version: no additional brackets, two arguments.
gotoAndPlay(1, Scene (2));
Then, what is Scene (2) expression, you might probably ask? It is called typecasting, an operation where you:
Explicitly state the exact class of something you are working with, because there are moments where compiler does not know exactly, like what is the exact class of Event.target object. There's a nice example, well explained: AS3: cast or "as"?
You want to explicitly convert data from one type to another (the said type coercion), mostly used between int or Number and String types.
Lets see:
function onEvent(e:Event):void
{
// Explicit typecasting.
MovieClip(e.target).nextFrame();
// Explicit typecasting through "as" operator.
var aFrame:int = (e.target as MovieClip).currentFrame;
// Explicit type conversion.
trace("The current frame is", String(aFrame));
So, you are trying to convince Flash Player that integer value 2 is somehow a Scene object, but there's no way it is, hence the exception thrown by the Flash Player because it does not understand what do you want it to do.
The correct notation of what you are trying to achieve (moving the playhead to the second Scene) would be probably:
gotoAndPlay(1, "Scene2");
The "Scene2" argument is a string literal that should represent the exact name of that second scene of yours, I couldn't know what it is but it should probably be named "Scene2" or "Scene 2" by default.
I want to nullify the event.target for garbage collection as the moveclip is no longer needed. What I have roughly is this:
mc.addEventListener(MouseEvent.CLICK, destroy);
public function destroy(event:MouseEvent):void {
event.target.parent.removeChild(event.target);
event.target.removeEventListener(MouseEvent.CLICK, destroyShape);
event.target = null; //THIS IS WHAT I WANT TO ACHIEVE
}
I'm sure this is relatively simple but I'm not sure how to do it.
thanks
You can't change MouseEvent.target value. It's a read only property. If your MovieClip doesn't exist (removeChild) and you removed event handler
mc.removeEventListener(MouseEvent.CLICK, destroy);
then the garbage collector automatically will remove it.
What you are trying to achieve (if it was even possible) would achieve nothing in term of Garbage Collection.
Unless redispatched the event will be garbage collected as soon as the method is done running. All properties of the event will also be discarded. This garbage collection of the event itself and its properties will have absolutely NO EFFECT on the object they point to in term of Garbage collection.
In the scope at which the event was dispatched in the first place the object will continue to exist after the event itself has been discarded. That is at that scope that the object reference must be nullified, not at the event listener scope where it will have no effect since the object still exists at the dispatching scope.
The solution accepted also does nothing. It is as relevant as applying a bandage to a wooden leg. Any local variable in function/method qualify for GC immediately after the method runs. Nullifying those variables has no effect whatsoever and does not constitute a valid answer to any problem and certainly not a GC problem. That those variables are GC does also not constitute a guaranty that the object they point to will be GC.
This is a case where a question about an inexistent and misunderstood problem is asked and answered by posting a false and irrelevant solution.
Case in point: Only a DisplayObject currently in a display list and attached to the stage has the ability to be the target of a MouseEvent. It is simply impossible for that object to become available for garbage collection before it is removed from the display list it belongs to. For those reasons that object cannot qualify for GC at the time the MouseEvent listener runs since that object still has at least one strong reference because it is attached to a display list. This is the proof that what the PO asks is misguided and any code examples are misguided as well since they cannot qualify the object for GC at that point in time.
As #subdan states, the target property of any Event is a readonly property.
You can still null your movie clip but not like in your sample.
mc.addEventListener(MouseEvent.CLICK, destroy);
public function destroy(event:MouseEvent):void
{
var myMC:MovieClip = event.target as MovieClip;
if( myMC )
{
myMC.removeEventListener(MouseEvent.CLICK, destroyShape);
myMC.parent.removeChild(myMC);
myMC = null; //THIS IS WHAT I WANT TO ACHIEVE
}
}
Are there any general guide lines for using retain and release for objects in cocos2d-X ? When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns. When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Kind Regards
Generally in c++ you have this behaviour:
void foo() {
Object a;
Object *pA = new Object();
(…)
}
This would result in a being destroyed automatically at function end, as it was allocated on stack. The *pA would not get destroyed, as it was allocated on the heap (thus, you only loose the reference to it, but the object itself still lives).
Cocos implements a thing called "Automatic Reference Counting" : each CCObject has a reference counter and two methods retain() and release(). The way this works is, that every time you create an object, it gets registered in cocos structers (CCPoolManager). Then with every frame (between them being drawn) there is a maintenance loop which checks the reference counter of all objects : if it is 0 this means (to cocos) that no other objects reference it, so it is safe to delete it. The retain count of an object is automatically incresead when you use this object as an argument for an addChild function.
Example :
void cocosFoo() {
CCSprite *a = CCSprite::create(…);
CCSprite *b = CCSprite::create(…);
this->addChild(b);
}
What happens here is this :
Two CCSprites are created, cocos knows about them.
The b sprite is added to this object (say a CCLayer)
The function ends, no objects are destroyed (both of them being on heap).
Somewhere between this and next frame, the maintanance gets run. Cocos chcecks both sprites and sees that a has reference count == 0, so it deletes it.
This system is quite good, as you don't need to worry about memory management. If you want to create a CCSprite (for example), but not add it as a child yet, you can call retain() on it, which will raise its reference counter, saving it from automatic deletion. But then you'd have to remember about calling release() on it (for example, when adding it as a child).
The general things you have to remeber about are :
Each call to retain() by you needs to be paired with release().
You generally shouldn't delete CCObjects yourself. If you feel that you need to, there is a conveniece macro : CC_SAFE_DELETE(object)
So to answer your questions in short :
Are there any general guide lines for using retain and release for objects in cocos2d-X ?
Yes, you should generally not need to do it.
When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns.
Answer to this is the whole text above.
When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Yes, as will adding it as a child to another (retained in any way) object.
Here is the thing,
cocos2dx has an autorelease pool which drains the objects which have retain count=0 which is a variable to keep in check the scope of the cocos2dx object.
Now when you create new object using the create method it is already added to the autorelease pool and you don't need to release it or delete it anywhere , its like garbage collector in java, takes care of garbage objects behind your back.
But when you create new object using 'new' you definitely need to release it in its destructor or after its use is over.
Second thing,
when your object is added to the autorelease pool but you need it somewhere else you could just retain it , this increments its retain count by one and then you have to manually release it after its use is over.
Third Thing,
Whenever you add child your object it is retained automatically but you don't need to release it rather you remove it from the parent.
I've just started to learn actionscript for a month or so, and I've been stuck on this part of the code for about 2 days now ><
So basically, I wanted to write a hitTestObject in a movieclip ("fire_mc") to detect if it overlaps the the child I added on stage (enemy)
So here's what I wrote in the main stage...
var newtarget:enemy=new enemy();
stage.addChild(newtarget);
newtarget.x=40;
newtarget.y=30;
and every time I traced the newtarget in the fire_mc, it turns out to be NULL...
So should I be tracing enemy or newtarget? (Cuz I tried both and nothing works).
And how can I hitTestObject in the fire_mc movieclip?
Is it possible for me to hitTestObject newtarget if I were to create multiple newtarget-s?
If not, what should I do?
And can someone tell me the difference between root, and MovieClip(root) (Because sometimes in a movieclip I have to use the former one to work, and sometimes the latter, and I have no idea why cuz I'm all referencing to the same place)
Sorry for these simple and lengthy questions, I'm really just a noob and I tried looking up for answers in the web for hours, but nothing helpful turned up><
Thanks in advance :)
In this statement
var newtarget:enemy=new enemy();
var - keyword used to define varibles, newtarget - variable name in which pointer to new class instance stored, :enemy - data type (the class name), new - keyword used to create new class instances, and finally enemy is class constructor (by the way there is a rule of good manners by which class names are capitalized)
So answer for you question which should you use when you want check is some is overlapping is 'newtarget'.
Now about hit test - all you need do to check if two objects hit each other is correctly use their references from the part of project where your code is writen.
For example if you have your fire_mc on MainTimeline created by IDE, and code of creation of you enemy instance stored in newtarget sameplace, then if you check will be placed in frame of MainTimeline where both object present it will be like this
fire_mc.hitTestObject(newtarget);
or this
newtarget.hitTestObject(fire_mc);
All these statements give you same result - if objects intersect each other you have true returned.
If you created 'newtarget' on MainTimeline and checks will be from fire_mc (which is on MainTimeline to) frame, then code will something like that
this.hitTestObject(MovieClip(root).newtarget);
Now about root. Primarily it is a property of DisplayObject in which reference to the top-most display object in swf-file structure is stored. So as such it is inherited by all child classes. So here's the trick. When you try get your objects in swf structure using root here the differences are based on the fact that root always return object as DisplayObject and that means what if you object contains something that DisplayObject shouldn't you can't get it. So you must convert returned reference to need data-type usign MovieClip(...).
I have these three objects that i need to add on stage, when I extract them from the array on a later stage of the program.
I can't write var mall: Mall = new Mall (); because somebody told me before that, This will add in the object in the memory and will be really bad if a lot of objects are being handled. I will have a lot more objects to work with later on.
So I would like to know a way that when i run a for loop or an event listener i can add ... = new object (); automatically and then call addchild to make an instance of the object.
var mall:Mall;
var library:Library;
var music:Music;
var choices:Array = new Array ();
choices.push(mall,library,music);
test Code for Garbage Collection :
var waking: WakingUp = new WakingUp ();
addChild (waking);
waking.y=-30;
waking.addEventListener (Event.ENTER_FRAME, waking_up);
function waking_up (g:Event){
if (waking.currentFrame == waking.totalFrames)
{
waking.stop ();
waking.removeEventListener (Event.ENTER_FRAME,waking_up);
removeChild (waking);
removeChild(alarm_slide);
gotoAndStop (6);
}
}
This is all the ref I have to this code :D
Specific to instantiating your array with elements, you can:
var choices:Array = [ mall, library, music ];
Flash Player runtime uses both reference counting as well as mark and sweep.
So, any reference to the instance adds a count - collections, display list, event listeners, class members... as instances are dereferenced, the memory will ultimately be freed.
Reference Counting
Each object on the heap keeps track of the number of things pointing to it. Each time you create a reference to an object, the
object's reference count is incremented. When you delete a reference,
the object's reference count is decremented. If the object has a zero
reference count (nothing is pointing to it), it is added to the Zero
Count Table (ZCT). When the ZCT is full, the stack is scanned to find
any references from the stack to an object on the ZCT. Any object on
the ZCT without a stack reference is deleted.
One of the problems of deferred reference counting is circular references. If ObjectA and ObjectB refer to each other but no other
objects in the system point to them, they will never have a zero
reference count and will therefore never be eligible for garbage
collection using reference counting. This is where mark and sweep
garbage collection helps.
Mark/Sweep
Applications that run in Flash Player or AIR have multiple GCRoots.
You can think about a GCRoot as the trunk of a tree with the objects
of the application as the branches. The Stage is a GCRoot. Loaders are
GCRoots. Certain menus are GCRoots. Every object that is still in use
by the application is reachable from one of the GCRoots within the
application. GCRoots are never garbage collected.
Every object in an application has a "mark bit." When the Mark phase
of garbage collection begins, all of those mark bits are cleared. The
MMgc keeps track of all GCRoots in the application. The garbage
collector starts from those roots, traces through each object and sets
the mark bit for every object it reaches. Any object that is no longer
reachable from any of the roots is no longer reachable from anywhere
in the application – its mark bit does not get set during the Mark
phase. Once the collector is done marking all of the objects it finds,
the Sweep phase begins. Any object that doesn't have a set mark bit is
destroyed and its memory reclaimed.
addChild does not make an instance of the object, it simply adds the object to the displaylist of the current display container.
Using new Mall() is fine - just ensure that you remove references to it when it's no longer required so that it may be garbage collected. Alternatively you can just hold an array of primitives.