What's code sequence in the below cocos2dx code? - cocos2d-x

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.

Related

Cocos2D-X Layer::init() gets stuck when waiting for Leaderboard Service

I'm trying to integrate App42 Leaderboard Service to my Cocos2D-X Game. The core functionality (Sending Scores to Server and retrieving them, just the way shown on the App42 site...) is working fine.
Now i want to visualize my leaderboard data using a CCTableView.
So I got a Leaderboard class (inherited from CCLayer) and am doing something like this :
bool Leaderboard::init() {
...
// Initialize and send App42 Scoreboard API call
App42API::Initialize(API_KEY, SECRET_KEY);
ScoreBoardService *scoreBoardService = App42API::BuildScoreBoardService();
scoreBoardService->GetTopNRankers(gameName,MAX_SCORES, this,app42callfuncND_selector(Leaderboard::onGetTopNRankings));
// responseArrived is boolean, indicates if onGetTopRankings was called
while(!responseArrived);
CCTableView* tableView = CCTableView::create(this, CCSizeMake(400, 100));
tableView->setDirection(kCCScrollViewDirectionVertical);
tableView->setPosition(winSize.width/3 , winSize.height/2);
tableView->setDelegate(this);
tableView->setVerticalFillOrder(kCCTableViewFillTopDown);
this->addChild(tableView,5);
tableView->reloadData();
return true;
}
void HelloWorld::onGetTopNRankings(App42CallBack *sender, void *response){
App42GameResponse *scoreResponse = (App42GameResponse*)response;
if (scoreResponse->isSuccess)
{
// Save User scores to Array
responseScores = scoreResponse->scores;
}
else
{
printf("\nerrordetails:%s",scoreResponse->errorDetails.c_str());
printf("\nerrorMessage:%s",scoreResponse->errorMessage.c_str());
printf("\nappErrorCode:%d",scoreResponse->appErrorCode);
printf("\nhttpErrorCode:%d",scoreResponse->httpErrorCode);
}
// Response Data is saved, or Error occured, go back to init()
responseArrived = true;
}
So as you see, I am waiting for onGetTopNRankings to get called, because the data for my TableView would be empty else. But what happens is, that the I can't get back to init() when onGetTopNRankings returns, it gets stuck.
So anybody got an idea why i can't return to Leaderboard::init() or got any good idea to solve this in any other way, I am open for each suggestion ?
while(!responseArrived);
This blocks the thread (endless loop). You need to fill your table view in the callback method when you have actual data. It will be empty until then. That's something your app's design has to deal with. For instance you could display a "loading" animation in the meantime, with a cancel button on it.
I tested your code and it is working fine in my App42Cocos2dXSample
The only possible reason for the issue you are getting is the owner class name of callback method in your code snippet.
scoreBoardService->GetTopNRankers(gameName,MAX_SCORES,
this,app42callfuncND_selector(Leaderboard::onGetTopNRankings));
In the above statement, onGetTopNRankings belong to the class Leaderboard, but while defining the callback method, it belongs to the class Helloworld:
void HelloWorld::onGetTopNRankings(App42CallBack *sender, void *response){
So, try changing the class name from Helloworld to Leaderboard in the above statement. I hope it will work.

How to check if a sprite has been removed

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");

in AS3, removeEventListener(Event.ENTER_FRAME) is not working

I have been dealing with this problem for days already. I am at my wits' end!
I can't seem to find a definitive answer anywhere on any of the forums, documentation, etc.
Everything looks fine at first run, or when I load a next level for the user to play. But if the user hits the ESC key to load a different level, the ENTER FRAME listener does not get removed and it duplicates all the triggers in it, showing the player going really fast, and all funky, because it builds on top of the previously instantiated ENTER FRAME listener.
I don't know if I have a problem of an anonymous function, or an unknown instance being referenced in my removeEvent... command... Bottom line, I give up and I need this working HELP!!!
Here's the code:
function initPlay():void
{
//code here determining what display object to add to the list and assign it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel){
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
removeChild(currentLevel);
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME)); //outputs TRUE if called from loadNextLevel but FALSE if called from initPlay() !!!
}
}
I also tried to add and remove the eventListener to stage, MovieClip(Root), or nothing at all and the result is always the same.
I know that there may be other ways to design such a process, but please note I am not really flexible at the moment on doing this because the project is very long (about 4000 lines of code) and removing the ENTER FRAME this way, crazy or not should still work!!
THANK YOU in advance for anyone willing to help.
The problem appears to be the nested functions inside the initPlay() method.
Each time you call initPlay() you are defining new functions. Some of these nested functions call initPlay() themselves.
Functions are objects (memory references). So each time you call initPlay() you are making new references to new functions. So when you try to remove an event listener, you're only able to remove one of these event handlers (the one in the current scope of execution).
I'm not sure if I'm explaining this clearly, perhaps this example will help. I'll use numbers to represent the references to each function, and a simple scenario that is similar to yours:
function example():void
{
addEventListener(MouseEvent.CLICK, mouseClickHandler);
function mouseClickHandler(event:Event):void
{
if (someCondition)
{
example();
}
else
{
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
}
}
When we run this function the first time, a new function is defined within the scope of the example() function. Lets use the number 1 to represent the reference to this nested function. someCondition is true on the first time around, and so the example() function is called again.
On the second execution of the example() function, a new reference to the mouse event handler is created (#2). We also add the event listener again. At this point, there are two event handling functions in memory, and both will be executed when the event is dispatched.
Let's say that in the second invocation of example() that someCondition is false and now we want to remove the listener. When we call:
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
It's referring to event handler #2. Event handler #1 still exists, and because it's hidden in the scope of the first invocation of example() it can't be removed here.
My simple example breaks down after this... but I hope it makes it clear why your event handlers shouldn't be nested inside a function. Admittedly, this is difficult to describe and even more so in a real world example like yours. But I'm pretty confident that this is the source of most, if not all, of the issues you describe.
Here's how I was able to get around this without changing the scope of the nested functions (although I agree that would be the preferred solution) by creating a boolean variable called "loadingNewGame" and changing it to true from outside the onEnterFrame (in fact, this assignment was done from initPlay() and then from onEnterframe I called removeEnterFrameListener() function. This did the trick.
here's the code in case anybody is interested:
// package, and other code here.
var loadingNewGame:Boolean = new Boolean(false);
function initPlay():void
{
//code here determining what display object to add to the list and assign
//it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel)
{
loadingNewGame = true;
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
loadingNewGame:Boolean = false;
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
if(loadingNewGame)
removeChild(currentLevel);
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME));
//outputs true
}

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]];