Должен ли я использовать спрайт-листы из-за большого количества изображений или несмотря на это?

13

Я занимаюсь разработкой 2D-игры, и у меня много спрайтов. Я использовал 3D-анимацию и модели для рендеринга в 2D, чтобы придать им «Fallout» или «Diablo». Это также проще, чем рисовать от руки, смеется.

Мне уже приходилось снижать частоту кадров до 15 кадров в секунду, что было самым низким значением, которое я мог опустить, чтобы они не выглядели прерывисто. Однако было грустно из-за того, как выглядели невероятно плавные 24 кадра.

Я сделал это по двум причинам:

1) Сократить пространство на жестком диске. Чем меньше изображений, тем меньше будет моя общая игра.

2) Сократить потребление оперативной памяти. Чем меньше изображений загружается, тем больше у меня шансов избежать проблем, связанных с ограничением моего объема оперативной памяти.

Однако, если бы был способ сжать изображения как на жестком диске, так и в оперативной памяти, я бы так и сделал. Я проверял это раньше, и большинство из них не получают никаких изменений в качестве при передаче из RGBA8888 в RGBA5555 и лишь небольшое попадание при преобразовании в RGBA4444 в моей программе TexturePacker. Я не делаю этого в настоящее время, потому что SFML, похоже, использует один и тот же объем памяти независимо от того, какой это тип изображения .PNG. Я изучал, как загрузить его по-другому, но не смог найти ничего по этому вопросу.

Я много читал о том, как работать с 2D-видеоиграми. Консенсус ошеломляет: упакуйте свои спрайты в более крупную текстуру для отличной производительности! Поэтому я упаковываю свои крошечные спрайты в гораздо большую таблицу спрайтов, используя TexturePacker.

Тем не менее, я планирую иметь 10-15 анимаций на персонажа, 5 направлений для перемещения и 15-40 кадров на анимацию (вероятно, в среднем 24). 15 анимаций, 5 направлений и в среднем 24 кадра на анимацию; Это 1800 отдельных кадров на символ. Если упаковано в спрайт лист, то это всего 75 изображений. (Один лист спрайтов на Анимацию, на Направление. 15 * 5)

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

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

Если бы я должен был предварительно загрузить текстуры, это имеет смысл для спрайтов. Я только предположил бы, что это плохая идея предварительно загрузить 1800 крошечных изображений для каждого персонажа.

Тем не менее, я думаю, что потоковая передача их в память и из памяти по одному будет чрезвычайно быстрой, поэтому мне нужно было бы иметь только одно изображение в памяти за один раз. Разве это не значит, что в любой данный момент каждый персонаж потребляет всего несколько КБ вместо 45 + МБ?

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

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

Carter81
источник
1. Вы не указали свои ограничения ОЗУ или жесткого диска. Сколько символов должно быть в быстром доступе? 2. В тексте есть несколько вопросов, может быть, вы могли бы выделить их жирным шрифтом или даже разбить вопросы на части?
Кромстер
Ой, простите. Не много. Я предположил бы, что максимальное количество отдельных символов на экране в любой момент времени будет около 40. Если бы люди пытались сбить с толку своих клиентов, тогда ... 130 - абсолютный максимум. Как правило, для типичного максимума должно быть только 10, а абсолютный максимум будет не более <40. Все, что выше 40, было бы крайне, крайне редким явлением, когда пользователи целенаправленно пытались втиснуть персонажей не по какой-либо причине, кроме скриншота или ради забавы втиснуть персонажей. Все, что выше 10, встречается редко, а что-то около 40 крайне редко.
Carter81
Игра представляет собой 2D-ролевую игру только для ПК (без мобильной), однако я не хотел бы исключать мобильную платформу, если она просто неосуществима. Я полагаю, что объем ОЗУ, который у меня есть, ограничен объемом ОЗУ на ПК пользователя и пользовательской VRAM. Я сильно сомневаюсь, что это будет очень большой HDD мудрый. Просто для жесткого диска всегда верно, что чем он меньше, тем лучше.
Carter81
Сколько уникальных символов вам нужно иметь на экране?
Кромстер
Не более 20. Все, что выше, было бы почти невозможным, если бы они не обманули. Обычно 5-10.
Carter81

Ответы:

16

У нас похожий случай с нашим RTS Remake. Все юниты и дома являются спрайтами. У нас есть 18 000 спрайтов для юнитов, домов и местности, плюс еще ~ 6 000 для командных цветов (в качестве масок). Длинно растянутые у нас также есть около 30 000 символов, используемых в шрифтах.

Итак, главная причина атласов:

  • меньше тратится ОЗУ (в прежние времена, когда вы загружали NPOT в GPU, он растягивал / дополнял его до POT, я читал, что он все тот же с iOS и некоторыми фреймворками.
  • меньше текстурных переключателей
  • более быстрая загрузка всего за меньшее количество больших кусков

Что не сработало у нас:

  • палитровые текстуры. Эта функция существовала только в OpenGL 1.x 2.x и даже тогда была в основном отброшена производителями графических процессоров. Однако, если вы нацелены на шейдеры OpenGL +, вы можете делать это с помощью шейдеров, кодирующих себя просто отлично!
  • В текстурах NPOT у нас были проблемы с неправильными границами и размытыми спрайтами, что недопустимо в пиксельной графике. Использование оперативной памяти также было намного выше.

Теперь у нас есть все, что упаковано в несколько десятков атласов 1024x1024 (современные графические процессоры поддерживают еще большие размеры), и это прекрасно работает, потребляя всего ~ 300 МБ памяти, что вполне подходит для игр для ПК. Некоторые оптимизации у нас были:

  • добавить опцию пользователя, чтобы использовать RGB5_A1 вместо RGBA8 (тени шахматной доски)
  • по возможности избегайте 8-битной альфы и используйте формат RGB5_A1
  • плотно упаковывать спрайты в атласы (см. Алгоритмы упаковки бина)
  • хранить и загружать все в один блок с жесткого диска (файлы ресурсов должны создаваться в автономном режиме)
  • Вы также можете попробовать форматы аппаратного сжатия (DXT, S3TC и т. д.)

Когда вы серьезно подумываете о переходе на мобильные устройства, вы будете беспокоиться об ограничениях. А пока просто включите игру и привлекайте игроков! ;)

Kromster
источник
Это было лучшее решение на сегодняшний день! Мои спрайты даже не выглядят по-другому в RGB5_A1 или RGBA4444, но это экономит память. Ваше предложение в чате, чтобы предварительно загрузить все мои активы в оперативной памяти и VRAM идеально. Более того, вы предложили иметь дополнительные графические уровни, такие как клиент высокой четкости для тех, у кого есть ОЗУ, или возможность уменьшить частоту кадров вдвое и т. Д. Хорошие предложения повсюду, и именно то, что мне нужно!
Carter81
5

У меня есть тангенциально связанный ответ в здесь , но общая идея состоит в том, что, если вы загружаете и рисования текстур в разное время (вы не загружая дополнительные текстуры в то время как вы будете рендеринга), то есть два места , где то , что вам do повлияет на вашу производительность:

Время загрузки:

Это момент, когда вы загружаете свои текстуры в память. Весь объем данных , которые вы отправляете VRAM, что будет в основном определять , как долго будет ваше время загрузки. Создание текстур меньшего формата, таких как RGBA4444, сделает это быстрее. Однако, если вы не загружаете текстуры в сотни мегабайт в VRAM, у вас, вероятно, не будет узкого места здесь. Если вы это сделаете, хороший экран загрузки может облегчить ожидание.

Объединение ваших текстур в атласы будет малоэффективным, так как весь объем информации, отправляемой вами в VRAM, будет одинаковым. На самом деле, если вы размещаете свои текстуры и вам нужно оставлять пустые места в ваших атласах, то вы фактически будете отправлять больше данных в VRAM, и поэтому эта часть будет работать медленнее!

Производительность рендеринга:

Как только все ваши текстуры будут в VRAM, количество имеющихся у вас текстур не повлияет на производительность рендеринга. Есть четыре элемента, которые влияют на производительность рендеринга:

  1. Изменения состояния рендеринга : каждый раз, когда вы меняете изображение, с которого вы хотите рендерить, серьезно увеличивается время, необходимое для его рендеринга. В общем, вы хотите минимизировать количество изменений состояния, и вы можете уменьшить количество изменений состояния, сгруппировав несколько изображений, которые вы будете последовательно рисовать, в атлас текстуры.

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

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

  2. Тиражные звонки: В общем, ты можешь хотеть свести к минимуму количество твоих звонков. Хорошее практическое правило заключается в том, что если между двумя вызовами отрисовки нет изменений состояния рендеринга, вы можете объединить их в один вызов отрисовки. Для более высокого прироста производительности вы можете использовать, скажем, 8 текстурных сэмплеров и групповые вызовы рисования для каждых 8 текстур, так что вам нужно только менять текстуры каждые 8 ​​текстур.

  3. Количество треугольников: на самом деле, чем больше треугольников вы рисуете, тем больше времени уходит на их рисование. Тем не менее, в современных компьютерах и для большинства 2D-игр вы будете очень далеки от этого. Вы можете безопасно рисовать сотни тысяч спрайтов за кадр и при этом получать безумно хорошие частоты кадров. Вероятно, вы будете более загружены процессором, если будете рисовать огромное количество спрайтов до того, как у вас возникнут проблемы с вашим графическим процессором.

  4. Настройки API: Если вы все делаете правильно, и у вас все еще странно низкая частота кадров, проверьте настройки, с которыми вы рисуете свои спрайты. Я не знаю SFML, но, например, в Direct3D 9 создание буфера вершин с D3DUSAGE_DYNAMICили в D3DPOOL_MANAGEDможет легко увеличить время рендеринга в десять раз. Конечно, использование vSync ограничит частоту кадров при частоте обновления вашего монитора. Кроме того, использование невыровненных FVF может снизить производительность в некоторых графических процессорах. Это тоже для Direct3D 9.

    В вашем случае, проверьте документацию для API, который вы используете.

Если у вас есть только небольшое или среднее количество текстур (менее 1 ГБ), и вы рисуете небольшое количество спрайтов (менее миллиона на кадр), то первое, на что я бы посмотрел, это изменение настроек API, и затем уменьшить количество состояний рендеринга и отрисовки вызовов.

Панда Пижама
источник
Если меня не волнует время загрузки, могу ли я предположить, что, если у меня не хватает ОЗУ или VRAM, мне просто нужно предварительно загрузить все в память?
Carter81
Меня интересует только все, ПОТОМУ ЧТО я боюсь нехватки ОЗУ / ВРАМ. Я не знаю почему, но меня пугает то, что пользователи, играющие в мою игру, вылетают всякий раз, когда они пытаются загрузить в область, которая имеет слишком много уникальных спрайтов, или вылетают, когда на их экране появляется слишком много персонажей. Если я не ошибаюсь, и каждый отдельный спрайт потребляет 96 КБ, то, если каждый уникальный персонаж имеет 15 анимаций, 5 направлений и в среднем 24 кадра на анимацию, каждый отдельный загруженный персонаж имеет 173 МБ. На экране одновременно может быть 10, а может и больше уникальных персонажей.
Carter81
@KromStern: если вы атлас, и вам нужно оставить пустые места (заполнение), то данные будут больше, и, следовательно, время загрузки будет больше. Ясно, что причиной более длительного времени загрузки является пустое пространство, и что общее время загрузки связано с общим объемом данных, а не с количеством текстур. Я не вижу в этом ничего вводящего в заблуждение, и я думаю, что человек, обладающий достаточными знаниями, чтобы понять исходный вопрос, сможет объединить точки и сделать свои собственные выводы для всех случаев, когда текстуры и атласы - это PoT и nPoT.
Панда Пижама
1
@ Carter81: Вам нужно выбрать целевую аппаратную конфигурацию, такую ​​как «i5, 1 ГБ ОЗУ, NVidia GT260, 400 МБ HDD» и работать с этим. Всегда будут ПК, которые слабее и имеют меньше оперативной памяти.
Кромстер
Очевидно, им не нужны все 15 анимаций в любой момент времени. Однако что, если все 10 уникальных персонажей перейдут в «Боевой режим» одновременно и, следовательно, потребуется заменить 5 наборов анимаций (ходьба, бег, холостой ход, не боевые действия и т. Д.) Еще на 5 (ходьба в бою, боевой режим, боевой и т. д.)? Мне страшно менять текстуру, потому что, когда я пытался сделать это с SFML, это приводило к заметному зависанию или паузе клиента при переключении текстурных атласов. Я не знаю, каким было бы мое узкое место с различными стратегиями для обработки такого количества спрайтов.
Carter81