just after some quick advice on the error below. I have two sprites on the screen running the same action. When one of the sprites is touched that sprite is sent to a method and the action is stopped.
If I touch the other sprite straight after the first, this should be sent to the same method and the action stopped, but when doing this my game crashes and I get the following error.
CCTextureAtlas originalTarget]: unrecognized selector sent to instance 0x15e743c0
I am using
[sprite stopAction:myAction];
to stop the actions. If I only have 1 sprite on the screen at a time, then this works fine, but just not together. Any ideas?
Thanks
You say that the two sprites are running the same action.
CCAction *action = //... Some action
[sprite1 runAction:action];
[sprite2 runAction:action];
Like that? You should never do that. You need two different actions. You're technically stopping the same action twice, which doesn't make much sense.
Related
I have seen several posts (E.g. this one) on this but I still don't get it.
If I call the CCDirector pause method it sets the animation interval to 1/4 and the value isPaused_ to YES (see code below). In the CCDirector.m class the isPaused_ variable
doesn't seem to be used much apart of in the pause and resume methods.
I thus decided to call also the stopAnimation method but in some posts this is not mentioned:
[[CCDirector sharedDirector] stopAnimation]
It doesn't apparently stop accelerometer data and input data to be sent to the main scene. It kind of does make sense to me because the developer of the Game might want to allow the user to resume by shaking the iPhone or tapping a resume button. Is this the reason behind this choice?
Also, why is the animation interval set to 1/4 and why things even with this value don't move (apart of my player entity that moves using accelerometer input)?
Thanks a lot!
-(void) pause
{
if( isPaused_ )
return;
oldAnimationInterval_ = animationInterval_;
// when paused, don't consume CPU
[self setAnimationInterval:1/4.0];
[self willChangeValueForKey:#"isPaused"];
isPaused_ = YES;
[self didChangeValueForKey:#"isPaused"];
}
Pausing CCDirector reduces framerate to 4 fps in order to conserve both battery and CPU cycles. The latter is necessary if you use UIKit views, in some cases it is necessary to pause the director in order for UIKit animations to animate smoothly.
What pausing the director also does is that it stops updating all nodes. Specifically the CCScheduler doesn't fire any scheduled selectors or update methods. It also prevents touches from being passed on to nodes, because they are relayed through the CCTouchDispatcher which is also paused when the director is paused.
But as you noticed, the accelerometer isn't paused. That's because cocos2d doesn't provide a wrapper for UIAccelerometer and therefore you get these notifications directly from iOS, ignoring the pause status of the director. If you then change the position of nodes inside the didAccelerate method or another method called directly from it, that node will change its position despite director being paused.
This is but one reason why director's pause isn't really suitable for a "pause game" feature. Another issue is that pausing simply would prevent any pause menu built with cocos2d features would also be paused, which kind of defies the purpose.
Then startAnimation and stopAnimation are simply extreme measures that prevent cocos2d from updating the screen altogether. This is normally only used in situations where either the cocos2d view is removed temporarily from the view hierarchy, or hidden, or some UIKit view is going fullscreen. In that sense stopAnimation is like a suspend feature.
This only happens once in a while. When I step from a breakpoint in the method called by the menu item when pressed I end up at the end of the method and when I step out I eventually get to ccTouchEnded and then the bad access occurs. Nothing shows up in the debug output window but I get a green arrow pointing to the main method with the error message.
Any ideas why this might occur?
Thanks.
So in case anyone has the same problem, I figured out what was happening. I had a CCMenu containing several children. When a child was tapped I did what I wanted with it and then removed it from the CCMenu via removeChild:cleanup: in the method I passed as the selector for the CCMenuItem. The problem was that Cocos2d deactivates the CCMenuItem while the selector method is executed and then reactivates it when the method is finished. So in the method I was basically destroying the CCMenuItem by removing it from the CCMenu and then at the end of the method Cocos2d tried to reactivate it but it was no longer in memory.
I don't see much of a way around this, so maybe it is not possible to remove a CCMenuItem from a CCMenu in its selector method.
The way I worked around it was to simply call setVisible:NO and setIsEnabled:NO on the menuitem. However, I can imagine cases in which this would not be the best way to do it. Maybe in these cases you could mess with the z position or something to get the menuitem out of the way.
Anyway, I hope this helps someone else, I know I've been stuck on this a while. :)
A better solution, in my humble opinion, is to unwind the scene destruction call from the stack. Using something like NSTimer+BlockKit makes it really clean. Here's an excerpt from my code:
- (void)menuAction
{
// we use a timer here to delay the execution of the action because it
// destroys the current scene and we're mid a call on CCMenu's ccTouchEnded
// that isn't expecting a scene tear down
// http://stackoverflow.com/questions/11165822/exc-bad-access-occurring-when-cocos2d-calls-cctouchended-in-ccmenu
[NSTimer scheduledTimerWithTimeInterval:0 block:^(NSTimer* timer)
{
[[CCDirector sharedDirector] popSceneWithTransition:
[CCTransitionSlideInL class] duration:kTransDur];
}
repeats:NO];
}
I'm wondering if iOS allows one to do the following:
I have a puzzle game and I've been working on saving data when the user returns to the home screen. To do this, using NSNotificationCenter, I had one of my game classes to observe [UIApplication sharedApplication]'s ApplicationWillResignActive method so when that happens I save my game state. But if the user decides to exit while animations are going on, the game will save a midState where the model values are still changing and that will often cause crashes. My question is if it is possible to somehow delay the saving process (even though it is on the background) until the animations are complete (or some variable equals 1)?
My first idea is to create scheduled event with NSTimer to try to save until everything is set. Any ideas would be appreciated. Thank you
You can use performSelector:withObject:afterDelay:
// Call doSomething after a 1 second delay
[self performSelector:#selector(doSomething) withObject:nil afterDelay:1.0f];
Rather than trying to delay the saving, especially in ApplicationWillResignActive, you should look into how to stop the animation and record the expected final values of the animation. Depending on the method of animation (UIView static methods, block based, 3rd party) there is usually a way to stop them, and since you define what the animation does you should already know the final state.
In the case of block based animations:
How to cancel UIViews block-based animation?
I'm currently building a game with cocos2d and I have the following Problem:
I have a MenuScene, where the user can start the game. When he does so, the gestureRecognizer gets initialized with the level in the following way:
CCScene *scene = [LevelScene scene];
LevelScene *layer = (LevelScene *) [scene.children objectAtIndex:0];
UIPanGestureRecognizer *gestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:layer action:#selector(handlePanFrom:)] autorelease];
Everything works fine, and after the level is finished, the user sees a gameOverScene and is sent back to the MenuScene. When I start the game again, everything works (ingame animations, level is displayed, etc.) besides the fact, that the gestureRecognizer isn't recognizing any touches anymore. Do you have any idea why that might be or how to debug this?
Thanks.
In all likelihood the gestureRecognizer retains the target. I've ran into a similar problem which caused the "target" scene that was a delegate of a UIKit class not to be deallocated. Ie the entire scene was leaked.
Due to some unfortunate circumstances the original scene's selector still got called but wasn't processed because the scene was forever locked in a "game over" state. My guess is that something like that is happening to you.
Question: do you relese the gestureRecognizer before changing from the LevelScene to another scene? If not, you absolutely should fix that!
I've implemented a tap-and-hold handler using an NSTimer that I first set in the TouchesBegan overload.
However, what I actually want is for an action to be continuously performed in quick-fire succession while the touch is being held. So, on timer expiry I call a handler to do the work, which then sets another timer and the cycle continues until the TouchesEnded comes in and cancels it, or another terminating condition is met.
This works fine, until my handler code triggers an animation to go off at the same time.
Now we have animation events and timer events going off, and in all that we need to handle TouchesEnded as well.
What I am finding is that, if the animation is triggered, and I set my timer to less than 0.025 seconds, my TouchesEnded event doesn't come through until the timer cycle stops (the other terminating condition). Setting a slower timer, or not triggering the animation, make it work (TouchedEnded comes in straight away), but are not what I want.
Obviously this is all on the device (release build - no NSLogs) - in the sim it all works fine
Is there any way of setting the relative priorty of these events - or is it likely I'm missing something else obvious here?
[Update]
I've worked around this in this instance by doing the continuous part without visual feedback until it's done (which from this users perspective is instant). I think this is ok for now. I'd still like to hear any more thoughts on this (Jeffrey's idea was good), but I'm not waiting on tenterhooks now.
Try writing your own Timer-type class by spawning off onto a thread. Example:
BOOL continue = YES; //outside of your #implementation
-(void)doLoop
{
while(continue){
[NSThread sleepForTimeInterval:.025];
[self performSelectorOnMainThread:#selector(whateverTheFunctionIs) waitUntilDone:YES];
}
}
and this would be started by [NSThread detatchNewThreadSelector:#selector(doLoop) toTarget:self withObject:nil]. This is not exactly threadsafe, but you can choose to wrap the boolean into a NSNumber and then do #synchronize on it if you so choose. Alternatively, after I wrote that little snippet I realized it would be better to do a check against the current NSTime instead of sleepForTimeInterval: but you get the point. :)