How to check if a sprite has been removed - cocos2d-x

I create 10 sprites in a for loop and add them to an array:
this.mysprite = new mysprite()
this.addChild(this.mysprite)
sprite_array.push(this.mysprite)
I then remove a sprite:
shared_game_layer.removeChild(sprite_array[0], true);
I then use another for loop to perform some actions on the sprites that have not been removed, wanting to bypass the removed sprites. The following code is executed before and after the sprite has been removed however the console always logs "y"
if (sprite_array[0])
console.log("y");
else
console.log("n");

You can call the getParent function and see if the pointer to the Parent returned is nil.
if (sprite_array[0]->getParent())
console.log("y");
else
console.log("n");

Related

AS3: how do i stop two of the same function from playing at once?

i am using AS3 to create a function that will automatically play a movieclip all the way through and then remove it. my project is going to have a lot of animated cutscenes, so id like to be able to call this function, use the cutscene id like as a parameter, and then move on to the next. the problem is, im trying to use the function multiple times in a row to play clips sequentially, but they're all playing at the same time. is there a fix, or a better way to do this altogether?
playClip(new a_walk); //find a way to make these stop playing at the same time
playClip(new a_door);
//a_walk and a_door are the AS linkage class names for the movieclips im referring to
function playClip (clip:MovieClip):void {
addChildAt(clip, 0);
clip.mask = myMask;
clip.x=412.4;
clip.y=244.5;
clip.addEventListener(Event.ENTER_FRAME, checkframes);
function checkframes(event:Event) {
if (clip.currentFrame == clip.totalFrames) {
//trace("wow! youre an idiot!");
if (clip.parent) {
clip.parent.removeChild(clip);
trace (100);
return;
}
}
}
}
Sounds like you want a mechanism to play a queue of MovieClips? If so, here is a way you can accomplish this:
//create an array with the clips you want to play (in order), in my example here, the items can be a MovieClip derived Class, or a MovieClip instance
var playQueue:Array = [a_walk, a_door];
//create a var to store the currently playing clip
var currentClip:MovieClip;
playNext(); //call this when you want the queue of clips to start playing
function playNext():void {
//if there was an item previously playing (currentClip has a value), stop it and remove it/dispose of it
if(currentClip){
currentClip.stop(); //stop it from playing
currentClip.addFrameScript(currentClip.totalFrames-1, null); //remove the frame script that was added
currentClip.parent.removeChild(currentClip); //remove it from the display
currentClip = null;
}
//check if there's anything left to play
if(playQueue.length < 1) return;
var nextItem:* = playQueue.shift(); //shift retrieves and removes the first item in the array;
if(nextItem is Class){
//if it's a class, instantiate it
currentClip = new nextItem();
}else{
currentClip = MovieClip(nextItem);
}
//initialize the movie clip
addChildAt(currentClip, 0);
currentClip.gotoAndPlay(1);
//this is just what you were doing before:
currentClip.mask = myMask;
currentClip.x=412.4;
currentClip.y=244.5;
//add a command on the last frame of the movie clip to play the next item in the queue
currentClip.addFrameScript(currentClip.totalFrames-1, playNext);
//addFrameScript is 0 based, so 0 would refer to the first frame. This is why we subtract 1 to get the last frame
}
I should note, that addFrameScript is an undocumented function. It serves as a nice shortcut so you don't have to have an ENTER_FRAME listener checking currentFrame vs. totalFrames. Being undocumented however, one can not count on it's continued existence in future versions of the Flash/AIR runtimes (though it's been around for a long long time)
note
This answer is a work in progress. I'm waiting on a response from the OP.
// playClip(new a_door); don't call this yet, or they will just both play.
var clipData:CustomClass = new CustomClass(); // add an instance of a custom class to hold the value of the movie
//clip being played (so that you can use this value later in the event handler.)
// it will also hold a value of the next clip
clipData._currentClip = a_walk;
clipData._nextClip = a_door;
playClip(new a_walk);
function playClip (clip:MovieClip):void {
addChildAt(clip, 0);
clip.mask = myMask;
clip.x=412.4;
clip.y=244.5;
clip.addEventListener(Event.ENTER_FRAME, checkframes);
}
function checkframes(event:Event) {
if (clipData._currentClip.currentFrame == clipData._currentClip.totalFrames) {
//trace("wow! youre an idiot!");
if (clipData._currentClip.parent) {
playClip(clipData._nextClip);
clipData._currentClip.parent.removeChild(clipData._currentClip);
clipData._currentClip = clipData._nextClip; // moves the clips down
//clipData._nextClip = here we have
//a problem. Do these clips play in a set
//order, first to last? Or do the play out of
//order jumping back and forth? If so, how
//are you planning on controlling which clip
//plays next?
trace (100);
return;
}
}
}
I haven't checked this in Flash yet to see if it works, but I noticed that you are defining a function inside another function, which I don't think is good practice, so this might clean things up for you. Give it a try and let us know.
I'll try to fix my code above when I get a chance. In the meantime, you answered my question about playing the clips in order, so a simple solution would be to put all the clips in an array and then play them by playClip(clipArray[i]) and then when the clip ends and gets removed, do i++ and call the same function playClip(clipArray[i]) which will play the next clip in the array.

var mC:mc = new mc is adding the mc immediately?

I am a baffled noob here. I have the following code:
var mC:mc = new mc();
I do NOT instantiate mC at all with an addChild(mC);
But, later in the code, I have a loop using onEnterFrame and in this loop I have the following trace function:
if(mC){
trace("mC is here");
}
This returns "mC is here" in the output window. HUH???
The problem is that I want to use this 'if' statement to removeChild(mC); [I will be adding it in the code later with an addChild(mC); based on certain stuff that happens] but it keeps throwing dang "ERROR child of the caller" messages even with the 'if' condition...
WHAT am I doing wrong? I did not know declaring variables would add them to the stage/display list, I thought you needed an addChild(); statement. Am I smoking something I shouldn't be?
Thanks in advance, ~Frustrated Inc.
When you new up an object it exists in memory, even if you have not added it to the stage. That is why when you check if mC exists, it returns true. You want to check if it exists on the stage. Something like:
var mc:MovieClip = new MovieClip();
mc.name = "test";
if (this.getChildByName("test") != null) {
trace("mc is on stage");
}
I have not used Flash for a long time, so I did not test this code. Hopefully it works.
Complex objects in AS3 (that means anything that is not a string or a number) have a default value of null. WHen evaluated that default value of null equals false:
var mymc:MovieClip;//that MC is NOT instantiated yet so it has a default value of null
if(mymc)
{
//mymc is null so this evaluates to false
//and this statement DOES NOT execute
Now when a complex object is instantiated and exist its value would now evaluates to true
var mymc:MovieClip = new MovieClip();//that MC IS instantiated
if(mymc)
{
//mymc exits so this evaluates to true and this statement EXECUTE
//notice that "!= null" is not necessary
Now your problem has to do with display list. A DisplayObject has a parent property that is null when that object is not added to a display list, and that property point to the parent when that object is added to a display list:
var mc:MovieClip = new MovieClip()
trace(mc.parent);//this is null
addChild(mc);
trace(mc.parent);//this is not null anymore and points to the parent
SO what you mean to do is:
if(mC.parent){//this means mC has a parent and can be removed from it
trace("mC is here");
}
In your code, you just control whether your variable is null or not.
You can use contains method on the display object you are trying to add to.
If you are adding mC to some sprite named container, you can simply check whether it exists in that container with:
if (!container.contains(mC))
container.addChild(mC);
Edit: The safer method to control whether a movieclip is on the stage is to control its stage value.
if (mC.stage) {
mC.parent.removeChild(mC); // this is how you remove, if you simply want to check existence, don't remove it
}
It has to have a stage value if you added the movieclip to the stage or a container that is added to stage.
Hope it is clearer this way.

What's code sequence in the below cocos2dx code?

I'm new to cocos2dx. In the below code, _nextProjectile is added to the children of "this" in the callback function which is executed after _nextProjectile->runaction (since "finish shoot" is always printed after "run action"). However, I print the position of _nextProjectile in finishShoot(), which is exact the same with that I set when I created the _nextProjectile.
So my question when does the _nextProjectile->runAction is executed actually?
Thank you.
{
_player->runAction(Sequence::create(RotateTo::create(rotateDuration, cocosAngle), CallFuncN::create(CC_CALLBACK_0(HelloWorld::finishShoot, this)), NULL));
// Move projectile to actual endpoint
printf("run action\n");
_nextProjectile->runAction(Sequence::create(MoveTo::create(realMoveDuration, realDest), CallFuncN::create(CC_CALLBACK_1(HelloWorld::spriteMoveFinished, this)), NULL));
}
void HelloWorld::finishShoot()
{
// Ok to add now - we've finished rotation!
printf("finish shoot\n");
this->addChild(_nextProjectile);
// Add to projectiles vector
_projectiles.pushBack(_nextProjectile);
_nextProjectile = NULL;
}
Both actions run simultaneously. The player rotates for rotateDuration before calling the callback, at the same time nextprojectile (if not null) already starts moving.
It seems like you may want to move the nextprojectile runaction in the finishShoot method instead.

Flex AS3 - About variable instances, event listeners and garbage collection

QUESTION ONE:
In the following example, ive added a listener to the foundMic. My question is if i re-run the foobar.initMic(); to reinitialize the microphone will i end up with a dead event listener floating in memory, and would it be picked up by garbage collection? Does calling the .getMicrophone() just reattach the same resource to foundMic or does it destroy the old foundMic and create a new instance of foundMic in memory.
As in if its the exact same foundMic then adding the event listener will silently fail and not create a second listener. But if foundMic becomes a new instance then it would be creating a new listener. Then back to the question about garbage collection, would it be considered unreferenced for clean up?
public class foobar {
public static var foundMic:Microphone = null;
public static function initMic():void {
foundMic = Microphone.getMicrophone();
foundMic.codec = SoundCodec.SPEEX;
foundMic.setSilenceLevel(0, 5000);
foundMic.gain = 50;
foundMic.setUseEchoSuppression(true);
foundMic.soundTransform.volume = 1;
foundMic.addEventListener(StatusEvent.STATUS, onMicStatusEvent);
return;
}
public static function onMicStatusEvent(event:StatusEvent):void {
if (foundMic && !foundMic.muted) someButton.enabled = true;
else someButton.enabled = false;
return;
}
} // END CLASS
QUESTION TWO:
Based on question one, if i added a remove listener just before adding the listener, does that actually remove the listener on the second time ran which was created when the method was ran the first time? Or is that foundMic already a new instance from the .getMicrophone() so nothing is removed because the first listener is already floating in memory associated with the previous instance of foundMic?
public static function initMic():void {
foundMic = Microphone.getMicrophone();
.
.
.
foundMic.removeEventListener(StatusEvent.STATUS, onMicStatusEvent);
foundMic.addEventListener(StatusEvent.STATUS, onMicStatusEvent);
return;
}
foundMic will just be assigned a new value which is a reference to to the Microphone - nothing should be garbage collected.
A case where the garbage collector should kick in is something like this:
var someVar:Sprite;
for(var i:int = 0; i < 5; i++)
{
someVar = new Sprite();
}
In this case you've created 5 new Sprite objects but ended up with only one assigned to the someVar variable - the other 4 should get garbage collected at some point because they are not referenced by any variable.
As far as question two, the code you have should give you an error the first time you call initMic() since at that point there is no onMicStatusEvent listener yet. Before trying to remove the event listener first test to see if it exists:
if(foundMic.hasEventListener(StatusEvent.STATUS)
{
foundMic.removeEventListener(StatusEvent.STATUS, onMicStatusEvent);
}
foundMic.addEventListener(StatusEvent.STATUS, onMicStatusEvent);
This code will always remove the event listener first if it exist and then subsequently add a new event listener. However, in this case I don't see any reason for this, as addEventListener by itself should work just fine - multiple calls are all on the same object, so no matter how many times you call initMic() you should only have one event listener running - there's no need to manually remove the listener and then recreate.
Here is what happens with event listeners. someObject.addEventListener('someEvent', someFunction) hands someObject a reference to someFunction. Because of the way scope works in ActionScript, the function will have access to everything in the scope of the code that created it. So until you call removeEventListener, the instance where the function exists will be held in memory as long as someObject is in memory.
However, in your case, there is no instance, because by using static functions your listeners will exist from the time your Class FooBar is first referenced until your swf is taken out of the computer's memory.
There is no net effect of removing the listener prior to adding it, because it is exactly the same function being handed to exactly the same object, which isn't going to call your function twice.
For more on how event listeners work in detail, check out this blog post.

How do I call a particular function at the end of a runAction?

I am performing a runAction on a sprite to move to a position.
[[crBalls[cb.count] getball] runAction:[CCSequence actions:[CCMoveTo actionWithDuration:timeToTravel position:ccp(xIntercept,yIntercept)],[CCMoveTo actionWithDuration:0.05 position:FD],nil]];
Once the sprite moves to the desired position(FD) then I want to call a function at that time. Right now I am scheduling a selector to be called after a delay of 'timeToTravel' which is the time taken by the above action to finish performing.(I am using scheduler instead of performSelector since perform selector is more prone to problems)
[self schedule:#selector(placeThatDamnBall) interval:timeToTravel+0.05];
-(void) placeThatDamnBall
{
[self unschedule:#selector(placeThatDammBall)];
[self ballPlacedIn:FD.x :FD.y :cb.type : cb.count];
}
But this is not entirely reliable and may cause problem in a rare case where the function might get called before the sprite reaches the destination. Is there any way I can avoid having to call a selector and be able to call the function once the sprite has truly reached the destination?
Thanks
Add a CCCallFunc at the end of your sequence:
[[crBalls[cb.count] getball] runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:timeToTravel position:ccp(xIntercept,yIntercept)],
[CCMoveTo actionWithDuration:0.05 position:FD],
[CCCallFunc actionWithTarget:self selector:#selector(placeThatDamnBall)],
nil]];