I am having a problem with a cocos2d app for IPhone I am developing. The prolem comes out sometimes during a scene change. The app stucks and the console starts printing this statement:
Assertion failure in -[CCSprite setTexture:]
I would like you to suggest me the right way to debug it since the problem does not always happen and there is not a precise indication of where the bug might be.
Thank you in advance
... some hours after: The problem occurs after a memory warning. It its therefore due to the sprite cache which is flushed while an animation is exploiting a cached texture atlas and related sheet. What can I do to handle it?
I would set a breakpoint in [CCSprite setTexture:] and from there check the stack trace and go back to your offending call. Of course, this will only succeed in case the failure occurs.
In my cocos2d installation (0.9.5), asserts in setTexture can be:
NSAssert( ! usesBatchNode_, #"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode");
// accept texture==nil as argument
NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], #"setTexture expects a CCTexture2D. Invalid argument");
So you are doing either of those wrongly.
EDIT after you comment:
your appDelegate defines presumably:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[CCDirector sharedDirector] purgeCachedData];
}
try using:
[[CCTextureCache sharedTextureCache] removeUnusedTextures];
instead of [[CCDirector sharedDirector] purgeCachedData]. Hope things will improve.
Related
I have almost finished an iPhone game made with cocos2d 2.0, and the last thing I did was add Game Center. I am using an iPod 4 for testing. I followed this tutorial: http://www.raywenderlich.com/23189/whats-new-with-game-center-in-ios-6
The problem is that when I try to submit a score to Game Center, the game starts to stutter / lag like crazy for 5-10 seconds. This happens when I call the following:
[gkScore reportScoreWithCompletionHandler:
^(NSError* error) {
[self setLastError:error];
BOOL success = (error == nil);
if ([_delegate
respondsToSelector:
#selector(onScoresSubmitted:)]) {
[_delegate onScoresSubmitted:success];
}
}];
The score is sent successfully without errors, but the game lags a lot when sending and memory warnings appear in the console. This problem seems to be related to a "CONNECTION INTERRUPTED" message that appears in the console beforehand, that seems to occur when the game's memory usage is around 60MB. The game doesn't lag when I try submitting a score earlier, before that message appears.
I searched around and found it strange that nobody else seems to complain about this, is this normal? Any help is much appreciated, thanks!
I have an app which uses CLLocationManager to track the user's route, drawing dots along the path taken. The app runs in the background using Required background modes > App registers for location updates.
As I understand, anything that happens in the background needs to be called from locationManager:didUpdateToLocation:fromLocation as this is the method that gets called with each location update.
The problem I'm having is that sometimes this stops getting called. It seems to happen when the user's location does not change much within the space of maybe 15 minutes or so. As far as I can tell, calls to locationManager:didUpdateToLocation:fromLocation just stop, presumably to save the battery. Unfortunately, it doesn't resume again when you're back on the move.
I presume there's no way to override this behaviour, so I would like to use Notification Centre to inform the user that the app is no longer recording the route. The problem is, how can the app know that this has happened? If locationManager:didUpdateToLocation:fromLocation is not called, I can't fire my notification. If it is being called, the notification should not fire.
Is there some kind of system notification that says location updates will cease?
I'm finding it quite hard to debug this as I can't take my Mac everywhere when I'm out and about testing the location on the device (there's only so much you can do in the simulator). Any tips for debugging would also be much appreciated!
If you haven't found the answer, I think it is because of a new attribute added to CLLocationManager called pausesLocationUpdatesAutomatically. The attribute defaults to YES, and its behaviour is exactly as you describe. Try setting it to NO and I think it will fix your problem.
Starting in iOS9, make sure you're also setting this property on your location manager:
[locationManager setAllowsBackgroundLocationUpdates:YES]
There's a delegate for location update did Fail
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
There are a few kinds of errors: kCLErrorDenied kCLErrorNetwork Add code here to handle them in the delegate method above not updating location, perhaps a UIAlertView to tell the user.
Personally, I call [locationManager stopUpdatingLocation]; on any error then restart it with an error message depending on the reason for the failure.
ALSO re background, check code in your appDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[self saveContext];
if ([CLLocationManager significantLocationChangeMonitoringAvailable]) {
// Stop normal location updates and start significant location change updates for battery efficiency.
[self.locationHandler.locationManager stopUpdatingLocation];
[self.locationHandler.locationManager startMonitoringSignificantLocationChanges];
}
else {
NSLog(#"Significant location change monitoring is not available.");
}
}
LASTLY re: testing. You can simulate some errors in location by changing the location movement in the simulator. For example, going from running to driving will cause an error. Going from running to a single specific custom location will cause an error. They should all appear in the delegate method for locationManager above.
I've managed to solve the problem by adding a local notification that fires with a 90 second delay every time a new location is added to the route. When the next location is added, the previous notification is cancelled and a new one is scheduled. This way, if it stops updating, a notification is received by the user (albeit with a 90 second delay). It's not ideal, and it may not be great for battery life, but it is a solution and it's the best I've got for the time being.
#Ron, I meet the same problem as beev describe, and i had already set pausesLocationUpdatesAutomatically to NO. I think because iOS will kill some apps that didn't be triggered in 10 minutes when it's under background. So add local notification maybe a good choice at the moment.
I'm using matt gallagaher's AudioStreamer class. I've used it before in a project before ARC came along and it worked fine. When I added the class to a project which uses ARC, I came across lots of errors which I could fix by adding __bridge references etc...
So the app now runs, but when I start the streamer with [streamer start] I keep coming across this error which I don't know how to fix. The compiler stops at the function below in Audiostreamer.m with the error Thread 8: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0) - I don't know what to do from here...please help.
if (CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue) == false)
{
[self presentAlertWithTitle:NSLocalizedStringFromTable(#"File Error", #"Errors", nil)
message:NSLocalizedStringFromTable(#"Unable to configure network read stream.", #"Errors", nil)];
return NO;
}
I have exactly error with using FreeStreamer by muhku (good library, recommended).
Check that you give correct/not nulled url to AudioStreamer.
Give my StreamingKit library a go. It has the same functionality as AudioStreamer but is built with ARC and has quite a few additionally pieces of functionality.
https://github.com/tumtumtum/StreamingKit
I was getting the same problem, but I disable arc and now is working fine, the only problem that I got is that when I slide my slider I get a new value to seekToTime: , but is not playing starting from the new value. Any idea what that is ?
CoreAudio seems to work internally using exceptions, so if you have an exception breakpoint installed, this is what you will see. Just disable the breakpoints and it'll work.
I'm trying to modify the GLCameraRipple sample application from Apple to process video frames on a background thread. In this example, it handles each frame on the main thread using the following code:
// Set dispatch to be on the main thread so OpenGL can do things with the data
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
If I change this code to process in a background thread:
dispatch_queue_t videoQueue = dispatch_queue_create("com.test.queue", NULL);
[dataOutput setSampleBufferDelegate:self queue:videoQueue];
then program crashes.
When I try to create a second EAGLContext with sharing, as specified in Apple's documentation, then I only see a green or black screen.
How can I modify this sample application to run on a background thread?
This was actually fairly interesting, after I tinkered with the sample. The problem here is with the CVOpenGLESTextureCacheCreateTextureFromImage() function. If you look at the console when you get the green texture, you'll see something like the following being logged:
Error at CVOpenGLESTextureCacheCreateTextureFromImage -6661
-6661, according to the headers (the only place I could find documentation on these new functions currently), is a kCVReturnInvalidArgument error. Something's obviously wrong with one of the arguments to this function.
It turns out that it is the CVImageBufferRef that is the problem here. It looks like this is being deallocated or otherwise changed while the block that handles this texture cache update is happening.
I tried a few ways of solving this, and ended up using a dispatch queue and dispatch semaphore like I describe in this answer, having the delegate still call back on the main thread, and within the delegate do something like the following:
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
return;
}
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
CFRetain(pixelBuffer);
dispatch_async(openGLESContextQueue, ^{
[EAGLContext setCurrentContext:_context];
// Rest of your processing
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
CFRelease(pixelBuffer);
dispatch_semaphore_signal(frameRenderingSemaphore);
});
}
By creating the CVImageBufferRef on the main thread, locking the bytes it points to, and retaining it, then handing it off to the asynchronous block, that seems to fix this error. A full project that shows this modification can be downloaded from here.
I should say one thing here: this doesn't appear to gain you anything. If you look at the way that the GLCameraRipple sample is set up, the heaviest operation in the application, the calculation of the ripple effect, is already dispatched to a background queue. This is also using the new fast upload path for providing camera data to OpenGL ES, so that's not a bottleneck here when run on the main thread.
In my Instruments profiling on a dual-core iPhone 4S, I see no significant difference in rendering speed or CPU usage between the stock version of this sample application and my modified one that runs the frame upload on a background queue. Still, it was an interesting problem to diagnose.
I'm trying to use more than one sprite sheet because I can't fit them all on one and having two makes my ordering them easier (sprite sheet one sprites are in the back and have a lower zOrder). I'm currently doing:
spriteSheet1 = [[CCSpriteSheet spriteSheetWithFile:#"spriteSheet1.png" capacity:3] retain];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"spriteSheet1.plist"];
[self addChild:spriteSheet1];
spriteSheet2 = [[CCSpriteSheet spriteSheetWithFile:#"spriteSheet2.png" capacity:4] retain];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"spriteSheet2.plist"];
[self addChild:spriteSheet2];
CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:#"sprite1.png"];
The last line crashes with the error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid spriteFrame for sprite' SIGABRT
Am I not allowed to cache more than one sprite sheet? If not, is there another efficient way to handle this?
If you create your spritesheets using Zwoptex, make sure you use the version of Zwoptex and cocos2d which support each other. Recently there have been major changes to Zwoptex and if you're using an older cocos2d version this might explain your crash.
In general the code you provided should work just fine, given that all the resource files haven been added to Xcode and are named properly. Keep in mind that the iOS devices are case-sensitive, if you try to load "spriteSheet1.plist" but the file is actually named "Spritesheet1.plist" it will also lead to errors (probably an assertion or crash).