Cocos2d и SpriteBatchNode: не могут определить, какой кадр спрайта вызывает сбой утверждения.

Я уже спрашивал что-то подобное, но я не могу понять, как правильно это отлаживать. Это вопрос.

Я добавил несколько обработчиков исключений (перехватывает все исключения Objective-C), и это результат того, что я вижу:

трассировка стека вызовов

консольный вывод

Проблема связана с методом setTexture, и он не выполняет утверждение, проверяющее, совпадает ли имя текстуры, которое необходимо отобразить, с именем в текущем узле пакета спрайтов.

Это происходит при попытке заменить одну сцену другой, но не всегда. Это связано с новой сценой, поскольку я пытался «изолировать» проблему, вызвав замену из другой части игры, и это все еще вызывает проблемы.

В игровой сцене у меня есть пара листов спрайтов и узлов пакета спрайтов, но, поскольку мне не удается изолировать идентификатор листа спрайтов, я не могу понять, какой кадр спрайта вызывает у меня проблему, а также я не понимаю, почему это бывает только иногда.

Я бы хотел:

  • понять, какое имя кадра спрайта дает мне 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;
}

person mm24    schedule 20.02.2013    source источник
comment
Единственный «плохой» вопрос — тот, который не задан. Не беспокойтесь слишком сильно о «близких» поклонниках: вокруг много догм.   -  person YvesLeBorg    schedule 20.02.2013
comment
@YvesLeBorg спасибо, Ив, я ценю помощь и поддержку, которые вы оказывали мне в прошлом году на этом форуме. Это очень помогает :)   -  person mm24    schedule 23.02.2013


Ответы (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. Вам также может потребоваться добавить дополнительное ведение журнала, если вы кэшируете CCAnimations и повторно используете их позже (регистрируйте каждую CCAnimation при повторном использовании).

Теперь, когда вы получаете эту ошибку, вы должны выбрать метод [CCSprite setDisplayFrame:] в стеке вызовов. Затем отладчик покажет вам значение CCSpriteFrame, которое он хочет установить. Найдите значение указателя, оно будет выглядеть примерно так: 0x254fb22e.

Найдите это значение в своем журнале, это вернет вас к одному из журналов «Создание CCAnimate..». Из приведенных выше строк журнала вы можете увидеть имена фреймов спрайтов, которые он содержит. Поскольку вы также зарегистрировали «arrayOfSpriteFrames», вы можете получить значение их указателя, сравнить его со значением указателя CCSpriteFrame, вызвавшего утверждение.

Когда у вас есть совпадение, и это четвертый элемент в массиве кадров спрайта, просто найдите имя четвертого имени кадра спрайта, добавленного в CCAnimate.

Может быть более быстрый способ сделать это в зависимости от того, какая информация доступна в отладчике (и насколько хорошо вы разбираетесь в отладке), но это один из подходов, который определенно приведет вас к названию оскорбительного кадра спрайта за относительно короткое время.

Обратите внимание, что значения указателя не уникальны — возможно, другой CCAnimate был создан с тем же значением указателя. Особенно, если есть высокая частота воспроизведения и остановки CCAnimate, может случиться так, что другой CCAnimate будет выделен в той же ячейке памяти, что и предыдущий. Так что будьте осторожны, если результаты не совпадают. Быстрый способ выяснить это — протестировать на разных устройствах или на симуляторе и на устройстве, поскольку значения указателя и стратегии распределения различаются.

Вам не нужно регистрировать, какое имя кадра спрайта принадлежит какому атласу текстуры. Просто откройте plist каждого атласа и найдите имя кадра спрайта. plist представляет собой XML-файл.

Совет: частой причиной этой проблемы может быть наличие одного и того же имени кадра спрайта в двух разных атласах текстур — cocos2d может использовать любую текстуру, когда вы запрашиваете кадр спрайта с повторяющимся именем.

Еще один совет: если ведение журнала кажется сложным, я бы просто сделал следующее:

  • открытый исходный код для класса CCSpriteFrame
  • добавить свойство NSString * «имя файла» с атрибутом «копировать»
  • каждый раз, когда вы создаете объект CCSpriteFrame, назначайте имя файла объекту CCSpriteFrame.

Теперь всякий раз, когда вы видите CCSpriteFrame в отладчике, он покажет вам связанное имя файла в представлении отладчика.

person LearnCocos2D    schedule 20.02.2013
comment
Спасибо, мне потребуется некоторое время, чтобы попробовать ваш ответ должным образом в выходные. - person mm24; 23.02.2013
comment
Я попробовал ответ, но не смог присвоить имя файла. Отладчик говорит, что информация недоступна. Я добавил скриншот файла EDIT и как я инициализирую значение имени файла. Другой подход, логирование создания анимации, у меня не работает, так как будет логировать всю информацию, а не только ту, которая мне нужна. - person mm24; 04.03.2013

хорошо, похоже, вы были там раньше ... Прокомментируйте первый NSAssert, раскомментируйте блок if. Затем поставьте точку останова на только что раскомментированном NSAssert. Выполнение будет приостановлено ПЕРЕД исключением, и вы должны проверить iVars каждого экземпляра класса в стеке вызовов (по крайней мере, я могу с AppCode, надеюсь, что xCode позволяет это). Возможно, вы получите достаточно подсказки, чтобы понять, какая текстура/анимация/батчнод вызывает у вас затруднения.

Также ... к вашему сведению. Всякий раз, когда я начинаю проект с cocos2d (любой версии), я применяю к нему некоторые «стандартные» исправления, чтобы мою жизнь было легче отлаживать. Стандартный «патч» заключается в добавлении имени к CCNode, которому я присвоил какое-то значимое значение: всегда. Также я переопределяю метод описания, чтобы вернуть МОЕ имя. Присвоение осмысленных имен помогало мне много раз, особенно когда я спускался (или поднимался) по иерархии узлов, чтобы найти ошибку. Я также удаляю многие из NSAsserts и, когда это возможно (особенно в ctors), возвращаю nil и регистрирую сообщение об ошибке. Согласно предложению Стефана, если ctor cocos2d возвращает мне nil, я выдаю сообщение журнала ошибок. Затем я могу остановиться на этом операторе журнала и детализировать проблему.

person YvesLeBorg    schedule 20.02.2013
comment
Спасибо, Ив, я никогда не слышал об AppCode и обязательно посмотрю. Исходя из фона Java, я привык к Eclipse, и я часто чувствовал потребность в чем-то отличном от XCode (однако я думаю, что в последнее время он немного улучшился). Ваше предложение по CCNode очень хорошее, я применю его к основным классам игры. Любой другой совет, подобный этому, будет иметь для меня большое значение. Я поддерживаю вопрос и принимаю вопрос Штеффена, поскольку он дал мне более конкретный совет о том, как зарегистрировать проблему, но оба ответа содержат очень ценную помощь для меня, спасибо! - person mm24; 23.02.2013
comment
AppCode — это «продукт» с номинальной лицензионной платой, созданный jetbrains. Затем создатели IntelliJ, моей любимой IDE при работе с Java. Не бесплатно, поэтому, если вы загружаете версию EAP версии 2.0 (бесплатно), убедитесь, что у вас не появится зависимость, если вы не готовы платить за лицензию :) - person YvesLeBorg; 23.02.2013
comment
Я пытался, но не очень помогает, он останавливается там все время. Я также пробовал некоторые варианты .. Я немного застрял :( - person mm24; 04.03.2013