Понимание, когда использовать CCSpriteBatchNodes?
Я видел в нескольких местах, включая исходный код CCSpriteBatchNode, что "дорого" добавлять / удалять дочерние элементы из него. Насколько я понимаю, весь смысл использования пакетных узлов состоит в том, чтобы предотвратить повторное выполнение дорогостоящих вызовов OpenGL, когда в один контейнер добавляется много спрайтов из одного листа спрайтов.
Что мне интересно, так это 1) насколько "дорого" является добавление / удаление дочерних элементов для узла пакета спрайтов, и 2) когда считается целесообразным использовать один из них?
Например, у меня есть лазерный объект, который создает десять спрайтов... когда он перемещается по экрану, он показывает / скрывает текущий спрайт для данной позиции экрана. Когда он достигает дальнего правого края экрана, лазерный объект отбрасывается, как и десять спрайтов. Итак, мне было интересно, это тот случай, когда узел партии спрайтов не подходит для использования, потому что это всего 10 спрайтов, и это происходит так быстро... Анимация перемещения составляет 0,2 секунды, так что если игрок будет быстро стрелять, это будет означать добавление / удаление 10 спрайтов в узел пакета снова и снова...
В других случаях у меня уже установлен SpriteBatchNode для различных объектов, и иногда я сталкиваюсь с одноразовым спрайтом, который необходимо добавить, и он просто является частью того же листа спрайта, поэтому я испытываю желание добавить его к этому пакетному узлу, так как он там, и он уже назначен этому конкретному листу спрайта... В любом случае, я хотел бы получить некоторые разъяснения по этой теме.
3 ответа
Основное различие между CCSpriteBatchNode
и нормальный CCSprite
тот факт, что CCSpriteBatchNode
отправляет все данные всех спрайтов одновременно в графический процессор вместо того, чтобы делать это для каждого спрайта.
CCSprite
Draw Call работает следующим образом:
glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
В основном делается 3 вызова для установки данных спрайта, а затем - вызов glDrawArrays
готово. Если у вас 100 спрайтов, этот код выполняется 100 раз.
Теперь давайте посмотрим на CCSpriteBatchNode
(Я выбрал реализацию без VAO, что является еще одной возможной оптимизацией):
glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
Теперь этот код устанавливает все данные всех спрайтов одновременно, так как они хранятся в непрерывной памяти. Этот вызов одинаков для 1, 10, 100, независимо от количества спрайтов.
Вот почему он более эффективен, но в то же время, поскольку данные хранятся в памяти непрерывно, при удалении, добавлении или изменении дочернего элемента массив должен быть соответствующим образом изменен и обновлен в графическом процессоре. Отсюда и стоимость добавления и удаления (или даже тот факт, что скрытый CCSprite просто пропускает фазу рендеринга, а скрытый CCSprite в пакетном узле - нет).
Исходя из личного опыта, я могу сказать вам, что стоимость обычно незначительна, и вы всегда должны использовать CCSpriteBatchNode
когда вы можете (так как у них есть свои пределы, например, смешивание по всему узлу, а не по отдельным спрайтам и тому подобное) и когда вы рисуете больше, чем набор спрайтов одного вида / причины.
Хотя сравнительный анализ для себя должен быть легким.
Как уже говорилось ранее, пакетный узел sprite группирует вызовы GPU для всех его дочерних элементов (поскольку они используют одну и ту же текстуру). Однако, чтобы это повлияло на производительность, нужно задействовать большое количество спрайтов. Для 10 спрайтов, я не думаю, что это будет иметь значение...
Тем не менее, обратите внимание, что если вы используете новую версию Cocos2d (например, 3.0), 3.1, которая сейчас находится в бета-версии, предлагает автоматическое пакетирование, поэтому вам не нужно тратить свое время на игры с CCSpriteBatchNode. Cocos2d автоматически пакетирует данные, отправленные в графический процессор.
1) насколько "дорого" является добавление / удаление дочерних элементов для узла спрайта
Единственный сценарий, который мне известен, это может быть "дорого", это когда вам нужно увеличить емкость атласа. Вы видите, что у пакетных узлов есть емкость, и если вы добавите дочерний элемент, который превосходит ее, узлу придется увеличить его емкость и пересчитать координаты текстуры для всех спрайтов.
Чтобы это исправить, вы просто даете своему пакетному узлу разумную емкость для начала - не слишком мало и не слишком много. Это зависит от вас, чтобы определить такой номер, в зависимости от ваших потребностей.
2) когда считается целесообразным использовать один из них?
Всякий раз, когда у вас есть несколько спрайтов, которые могут использовать один и тот же источник текстуры. Для игры Mario ясно, что вам понадобится несколько монет на экране. Это было бы хорошим вариантом использования для узла партии: имейте узел партии для изображения монеты, и тогда все ваши спрайты монет будут использовать этот узел партии.
Иногда вы можете упаковать несколько элементов в одну и ту же текстуру. Скажем, вы можете поместить изображение монеты, изображение монстра и изображение гриба в одну и ту же текстуру. Таким образом, все ваши монеты, монстры и грибы могут использовать один и тот же пакетный узел.
Вам не нужны пакетные узлы для таких вещей, как фоновые текстуры, потому что в любом случае вам, вероятно, нужен только один фоновый спрайт.
Итак, мне было интересно, это тот случай, когда узел партии спрайтов не подходит для использования, потому что это всего 10 спрайтов, и это происходит так быстро... Анимация перемещения составляет 0,2 секунды, так что если игрок будет быстро стрелять, это будет означать добавление / удаление 10 спрайтов в узел пакета снова и снова...
Это допустимый вариант использования для пакетного узла. В конце концов, 10 спрайтов нарисованы одновременно. И, если вы знаете, что вы больше не будете использовать лазерный объект, вы всегда можете выгрузить соответствующий пакетный узел. Я полагаю, что в вашей игре может быть несколько лазерных объектов, поэтому пакетный узел - хорошая идея.
Честно говоря, не волнуйтесь о производительности. Я использую десятки в своей игре все время для всех видов вещей (персонажей, погодных частиц, объектов карты, предметов коллекционирования, интерфейса и т. Д.), И благодаря ним я редко когда-либо вижу, что она падает ниже 55fps.
На самом деле, мне трудно спорить с использованием пакетных узлов. Они редко причиняют вред.