1

I have already asked something similar but I can't figure out properly how to debug this. That's the question.

I added some Exceptions handler (catches all Objective-C) exceptions and that's the result of what I see:

call stack trace

console output

The problem is with the setTexture method and it fails at the assertion verifying whether the texture name that needs to be displayed is the same as the one in the current sprite batch node.

This happens when trying to replace one scene with another but doesn't happen all the times. It has to do with the new scene as I tried to "isolate" the problem by calling the replace from a different part of the game and it still does give trouble.

In the game scene I have a couple of sprite sheets and sprite batch nodes but as I don't manage to isolate a sprite sheet id I cannot understand which sprite frame is giving me the problem, as well as I don't understand why this happens only sometimes.

I would like to:

  • understand which sprite frame name gives me the AssertionFailure
  • understand to which sprite sheet it belongs

This should help me to understand if it is a naming problem or if this has to do with something else.

Hoping not to be too lame with this question..

EDIT: I tried the answer but I am not able to read the 'fileName' information, here is what the debugger says "Summary unavailable":

Summary unavailable

That's how I create the filename property:

/** TMP: Bug solving filename */
@property (copy) NSString *fileName;

-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
{
    if( (self=[super init]) )
    {
        self.fileName = [NSString stringWithFormat:@"GLUINT texture name: %i", texture.name];
        self.texture = texture;
        rectInPixels_ = rect;
        rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
        offsetInPixels_ = offset;
        offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ );
        originalSizeInPixels_ = originalSize;
        originalSize_ = CC_SIZE_PIXELS_TO_POINTS( originalSizeInPixels_ );
        rotated_ = rotated;
    }
    return self;
}

-(id) initWithTextureFilename:(NSString *)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
{
    if( (self=[super init]) )
    {
        self.fileName = fileName; //TMP
        texture_ = nil;
        textureFilename_ = [filename copy];
        rectInPixels_ = rect;
        rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
        offsetInPixels_ = offset;
        offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ );
        originalSizeInPixels_ = originalSize;
        originalSize_ = CC_SIZE_PIXELS_TO_POINTS( originalSizeInPixels_ );
        rotated_ = rotated;
    }
    return self;
}
Community
  • 1
  • 1
mm24
  • 9,280
  • 12
  • 75
  • 170
  • 1
    The only 'bad' question is the one not asked. Dont worry too much for the 'close' aficionados around here : lots of dogma floating around. – YvesLeBorg Feb 20 '13 at 13:22
  • @YvesLeBorg thanks Yves, I appreciate the help and encouragement you have been giving me in the last year in this forum. It helps a lot :) – mm24 Feb 22 '13 at 23:45

2 Answers2

2

In such cases logging is your friend. Every time you create a CCAnimate action (or CCAnimation) you should log something like this:

// during 'create sprite frames from sprite frame names' loop
NSLog(@"adding sprite frame name for CCAnimate: %@", spriteFrameName);

// after CCAnimate was created
NSLog(@"creating CCAnimate %@ with sprite frames: %@, animate, arrayOfSpriteFrames);

You will probably want to add more detail, like which sprite frame names were added to that CCAnimate. You may also have to add additional logging if you cache CCAnimations and reuse them later (log each CCAnimation when reused).

Now when you receive that error you should select the [CCSprite setDisplayFrame:] method in the call stack. The debugger will then show you the value for the CCSpriteFrame it wants to set. Look for the pointer value, it will read something like 0x254fb22e.

Search for that value in your log, this will bring you back to one of the "creating CCAnimate.." logs. From the log lines above you can see the sprite frame names it contains. Since you also logged the 'arrayOfSpriteFrames' you can get their pointer value, compare it with the pointer value of the CCSpriteFrame that caused the assertion.

When you have a match, and it's the fourth item in the sprite frame array, just look up the name of the fourth sprite frame name added to the CCAnimate.

There may be a quicker way to do this depending on which information is available in the debugger (and how well versed you are in debugging), but this is one approach that will definitely lead you to the offending sprite frame name in relatively short time.

Note that the pointer values are not unique - it's possible that a different CCAnimate was created with the same pointer value. Especially if there's a high frequence of CCAnimate playing and stopping it can happen that another CCAnimate is allocated at the exact same memory location of a previous one. So be wary if the results don't seem to match up. A quick way to find out is to test on different devices or Simulator vs. Device, as pointer values and allocation strategies vary.

You don't need to log which sprite frame name belongs to which texture atlas. Simply open the plist of each atlas and search for the sprite frame name. The plist is an XML file.

Tip: common cause for this problem can be having the same sprite frame name in two different texture atlases - cocos2d may use either texture when you request a sprite frame with a duplicate name.

Another Tip: if the logging seems daunting, I'd simply do the following:

  • open source code for CCSpriteFrame class
  • add a NSString* property 'filename' with 'copy' attribute
  • every time you create a CCSpriteFrame object, assign the filename to the CCSpriteFrame

Now whenever you see a CCSpriteFrame in the debugger, it'll show you the associated filename in the debugger view.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • Thank you, I will take some time to try your answer properly in the weekend. – mm24 Feb 22 '13 at 23:46
  • I tried the answer but didn't manage to assign the filename.. the debugger says "Unavailable information".. I added an EDIT file screenshot and how I initialize the filename value. The other approach, loggin the creation of animation, does not work for me as it would log all information and not only the one I need. – mm24 Mar 04 '13 at 17:29
1

ok , looks like you have been there before ... Comment the first NSAssert, uncomment the if block. Then put a break point on the newly uncommented NSAssert. Execution will pause BEFORE the exception, and you should be to inspect the iVars of each class instance in the call stack (at least i can with AppCode, hope xCode allows that). Maybe you will get enough of a hint to figure out which texture/animation/batchnode is giving you a hard time.

Also ... fyi. Whenever i start a project with cocos2d (any version) , i apply some 'standard' fixes to it so that my life will be easier for debugging. A standard 'patch' is to add a name to CCNode, which i set to some meaningful value : always. Also i override the descpription method to return MY name. Assigning meaningful names has helped me many times, especially when walking down (or up) the node hierarchy to figure out a bug. I also nuke many of the NSAsserts and whenever possible (especially in ctors), return nil and log an error message. As per Stefen's suggestion, if a cocos2d ctor returns me a nil, i spew out an error log message. I can then break on that log statement and drill-down the issue.

YvesLeBorg
  • 9,070
  • 8
  • 35
  • 48
  • 1
    Thank you Yves, I had never heard of AppCode and I will definetely check it out. Coming from a Java background I was used to Eclipse and I often felt the need of something different than XCode (however I think recently it improved quiet a bit). Your suggestion on the CCNode is very good, I will apply it to the main classes of the game. Any other tip like this would be of great value for me. I upvote the question and accept Steffen one as it gave me some more specific tip on how to log the problem, but both answer contain very valuable help for me, thanks! – mm24 Feb 22 '13 at 23:51
  • 1
    AppCode is a 'product' , with a nominal license fee, build by jetbrains. Then are the makers of IntelliJ, ma favourite IDE when Java'ing. Not free, so if you download the version 2.0 EAP version (free), make certain you dont get addicted if you are not ready to pay the license :) – YvesLeBorg Feb 23 '13 at 00:55
  • I tried but doesn't really help, it stops there all the time. I tried some variations as well.. I am a bit stuck :( – mm24 Mar 04 '13 at 17:55