1

我已经问过类似的问题,但我不知道如何正确调试它。这就是问题

我添加了一些异常处理程序(捕获所有 Objective-C)异常,这就是我所看到的结果:

调用堆栈跟踪

控制台输出

问题在于 setTexture 方法,它在断言验证需要显示的纹理名称是否与当前精灵批处理节点中的相同时失败。

当试图用另一个场景替换一个场景时会发生这种情况,但并非总是发生。它与新场景有关,因为我试图通过从游戏的不同部分调用替换来“隔离”问题,但它仍然会带来麻烦。

在游戏场景中,我有几个精灵表和精灵批处理节点,但由于我无法隔离精灵表 ID,我无法理解哪个精灵帧给我带来了问题,我也不明白为什么会这样只是偶尔发生。

我想:

  • 了解哪个精灵帧名称给了我 AssertionFailure
  • 了解它属于哪个精灵表

这应该有助于我了解这是命名问题还是与其他问题有关。

希望不要对这个问题太蹩脚..

编辑:我尝试了答案,但我无法读取“文件名”信息,这是调试器所说的“摘要不可用”:

摘要不可用

这就是我创建文件名属性的方式:

/** 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;
}
4

2 回答 2

2

在这种情况下,日志记录是您的朋友。每次创建 CCAnimate 动作(或 CCAnimation)时,都应该记录如下内容:

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

您可能想要添加更多细节,例如向 CCAnimate 添加了哪些精灵帧名称。如果您缓存 CCAnimation 并稍后重用它们(重用时记录每个 CCAnimation),您可能还必须添加额外的日志记录。

现在,当您收到该错误时,您应该选择[CCSprite setDisplayFrame:]调用堆栈中的方法。然后调试器将显示它想要设置的 CCSpriteFrame 的值。查找指针值,它会读取类似0x254fb22e.

在您的日志中搜索该值,这将带您回到“正在创建 CCAnimate..”日志之一。从上面的日志行中,您可以看到它包含的精灵帧名称。由于您还记录了“arrayOfSpriteFrames”,您可以获得它们的指针值,将其与导致断言的 CCSpriteFrame 的指针值进行比较。

当您有匹配项时,它是精灵帧数组中的第四项,只需查找添加到 CCAnimate 中的第四个精灵帧名称的名称。

根据调试器中可用的信息(以及您对调试的精通程度),可能有更快的方法来执行此操作,但这是一种方法,肯定会在相对较短的时间内让您找到有问题的精灵帧名称。

请注意,指针值不是唯一的 - 可能使用相同的指针值创建了不同的 CCAnimate。特别是如果 CCAnimate 播放和停止的频率很高,则可能会发生另一个 CCAnimate 分配在与前一个完全相同的内存位置的情况。因此,如果结果似乎不匹配,请小心。快速找出答案的方法是在不同的设备或模拟器与设备上进行测试,因为指针值和分配策略会有所不同。

您不需要记录哪个精灵帧名称属于哪个纹理图集。只需打开每个图集的 plist 并搜索精灵帧名称。plist 是一个 XML 文件。

提示:此问题的常见原因可能是在两个不同的纹理图集中具有相同的精灵帧名称 - 当您请求具有重复名称的精灵帧时,cocos2d 可能会使用任一纹理。

另一个提示:如果日志记录看起来令人生畏,我只需执行以下操作:

  • CCSpriteFrame 类的开源代码
  • 添加带有“复制”属性的 NSString* 属性“文件名”
  • 每次创建 CCSpriteFrame 对象时,将文件名分配给 CCSpriteFrame

现在,每当您在调试器中看到 CCSpriteFrame 时,它​​都会在调试器视图中向您显示关联的文件名。

于 2013-02-20T13:13:45.230 回答
1

好的,看起来你以前去过那里...评论第一个 NSAssert,取消注释 if 块。然后在新的未注释的 NSAssert 上放置一个断点。执行将在异常之前暂停,您应该检查调用堆栈中每个类实例的 iVar(至少我可以使用 AppCode,希望 xCode 允许这样做)。也许你会得到足够的提示来弄清楚哪个纹理/动画/批处理节点给你带来了困难。

还有……仅供参考。每当我使用 cocos2d(任何版本)开始一个项目时,我都会对其应用一些“标准”修复,以便我的生活更容易调试。一个标准的“补丁”是为 CCNode 添加一个名称,我将其设置为一些有意义的值:总是。我也重写了描述方法以返回我的名字。分配有意义的名称对我有很多帮助,尤其是在向下(或向上)遍历节点层次结构以找出错误时。我还对许多 NSAsserts 进行了核对,并尽可能(尤其是在 ctors 中)返回 nil 并记录错误消息。根据 Stefen 的建议,如果 cocos2d ctor 返回 nil,我会吐出一条错误日志消息。然后我可以打破该日志语句并深入研究问题。

于 2013-02-20T13:19:11.600 回答