Главная » Программирование игр под Android » Атлас текстур: не ленись – поделись – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

До этого момента мы использовали в наших программах только по одной текстуре. Что если мы захотим отобразить не только Боба, но и других супергероев, врагов, взрывы или монетки? У нас может быть несколько текстур, каждая из которых содержит изображение одного типа объектов. Но OpenGL вряд ли это понравится, поскольку нам надо будет переключать текстуры для каждого объекта, который мы отображаем (например, привязываем текстуру Боба, отображаем Боба, привязываем текстуру монетки, отображаем монетку и т. д.). Процесс может стать проще, если поместить различные изображения в одну текстуру. Это и будет атлас текстур: одна текстура, содержащая разные изображения. Мы должны сделать эту текстуру текущей (привязать) только один раз и тогда сможем отображать объекты любых типов, изображение которых есть в атласе. Так мы избавляемся от переизбытка смен состояния и ускоряем реализацию. Такой атлас текстур показан на рис. 8.23.

Рис. 8.23. Атлас текстур

На рис. 8.23 находятся три объекта: пушка, пушечное ядро и Боб. Сетка не является частью текстуры – она нужна только для того, чтобы показать, как я обычно создаю мои атласы текстур.

Атлас текстур имеет размер 64 х 64 пиксела, а каждая ячейка сетки имеет размеры 32 х 32 пиксела. Пушка занимает две ячейки, ядро немного меньше, чем четверть ячейки, а Боб помещается в одной ячейке. Теперь, если вы вспомните, как мы определяли границы (и графические прямоугольники) для пушки, ядра и мишеней, вы заметите, что отношение их размеров друг к другу очень напоминает то, которое мы имеем в этой сетке. Мишень в нашем мире имеет размер 0,5 х 0,5 м, пушка – 0,2 х 0,2 м. В нашем атласе текстур Боб имеет размеры 32 х 32 пиксела, а ядро немного меньше 16×16 пикселов. Соотношение между атласом текстур и размерами объектов в нашем мире должно быть понятно: 32 пиксела в атласе равны 0,5 м в мире.

В изначальном примере пушка имела размеры 1×1 метр, но мы, конечно, можем это изменить. В соответствии с нашим атласом текстур, где пушка имеет размеры примерно 64 х 32 пиксела, нам надо сделать пушку в нашем мире размерами 1 х 0,5 м. Совсем просто, правда?

Но почему же я выбрал именно 32 пиксела как эквивалент 1 м в нашем мире? Мы помним, что ширина и длина текстур должны быть степенями двух. Если количество пикселов равно 32, то есть степени 2, для соответствия 0,5 м в нашем мире позволяет художнику легко справиться с ограничениями размера текстур. Это также позволяет проще понять соотношение разных объектов из нашего мира относительно пиксельных изображений.

Вы можете приравнять и другое количество пикселов к единице мира. Например, 64 или 50 пикселов к 0,5 м в нашем мире. Какое же количество пикселов будет наилучшим? Это опять же зависит от разрешения экрана, на котором будет воспроизводиться игра. Проведем некоторые подсчеты.

Наш пушечный мир ограничен координатами (0; 0) в нижнем левом углу и (9,6; 4,8) в верхнем правом углу. Это отображено на экране. Выясним, сколько пикселов на единицу мира нам нужно для экрана Него (480 х 320 пикселов в альбомном режиме):

Наша пушка, которая теперь будет занимать площадь 1 х 0,5 м в мире, будет иметь размеры 50 х 25 пикселов на экране. Мы используем область 64 х 32 пикселов нашей текстуры, так что немного снизим качество изображения пушки во время рендеринга. В зависимости от фильтра минификации, который мы применяем для текстуры, результат будет четким и мозаичным (GL NEAREST) или немного размытым (GL LINEAR). Если нам требуется идеальное отображение на Него, надо немного масштабировать наши текстурные изображения. Мы можем установить размер ячейки сетки 25 х 25 пикселов вместо 32 х 32. Однако даже если мы просто изменим изображение атласа (или перерисуем его от руки), у нас будет изображение размером 50 х 50 пикселов, никак не подходящее для OpenGL ES. Нам надо будет добавить дополнительные участки слева и снизу, чтобы добиться изображения размером 64 х 64 пиксела (поскольку OpenGL ES требует, чтобы ширина и длина изображений были степенью 2). Можно сказать, что OpenGL ES прекрасно подходит для масштабирования нашего изображения текстуры для Него.

Какова же ситуация с устройствами, имеющими более высокое разрешение, как Nexus One (800 х 480 в альбомном режиме)? Произведем подсчеты для такой конфигурации экрана при помощи следующих уравнений:

Количество пикселов на единицу мира на х- и г/-осях будет разным, потому что отношение ширины и длины нашей области видимости (9,6 / 6,4 = 1,5) отличается от отношения стороны экрана (800 / 480 = 1,66).

Тогда мы упоминали об установленном размере в пикселах и отношении сторон. Теперь применим эту схему и выберем ширину и длину конуса отображения для нашего примера. В случае с Nexus One пушка, ядро и Боб будут немного увеличены и растянуты из-за высокого разрешения и иного отношения сторон. Мы примем этот факт, поскольку хотим, чтобы все игроки видели одну и ту же область нашего мира. Иначе игроки с более высоким показателем отношения сторон будут иметь возможность видеть большую часть нашего мира.

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

Здесь и и у – текстурные координаты, ахну – пиксельные координаты. Верхний левый угол Боба имеет пиксельные координаты (32; 32). Если мы вставим их в это уравнение, получим текстурные координаты (0,5; 0,5). Мы можем сделать то же самое для любых других углов, которые нам понадобятся, и, основываясь на этом, назначить правильные текстурные координаты для вершин наших прямоугольников.

Пример

Добавим этот атлас текстур к нашему предыдущему примеру и сделаем его красивее. Боб будет нашей мишенью.

Мы просто копируем Camera2DTest и немного его изменяем. Я поместил копию в файл под именем TextureAtl asTest. Java и переименовал два класса, содержащихся в нем, соответственно TextureAtl asTest и TextureAtl asScreen.

Первое, что мы делаем, – добавляем новый член в класс TextureAtl asScreen: Texture texture:

Вместо того чтобы создавать Texture в конструкторе, мы создаем текстуру в методе resume. Помните, что текстуры будут потеряны, когда приложение вернется из состояния паузы, так что нам снова нужно будет воссоздавать их в методе resume:

Я добавил изображение на рис. 8.23 в папку assets нашего проекта и назвал его atl as. png. (Конечно, оно не содержит сетки, изображенной на рисунке.)

Далее необходимо изменить определения вершин. У нас есть один экземпляр класса Verti ces для каждого типа объектов (пушки, ядра и Боба), содержащий один прямоугольник из четырех вершин и шести индексов. В результате получаются два треугольника. Нам нужно добавить текстурные координаты к каждой вершине в соответствии с атласом текстур. Мы также изменим представление пушки в качестве треугольника на представление как прямоугольник размером 1 х 0,5 м. Вот на что мы заменяем старый код создания вершин в конструкторе:

Каждая из наших сетей теперь состоит из четырех вершин, все они, в свою очередь, имеют 2D -позицию и текстурные координаты. Добавляем в сеть 6 индексов, определяя два треугольника, которые хотим рендерить. Мы также немного уменьшаем пушку по оси у. Теперь она имеет размер 1 х 0,5 м вместо 1 х 1 м. Это также отражено ранее при создании объекта Cannon в конструкторе:

Поскольку мы не производим проверку на столкновения с самой пушкой, нет особенной разницы, какой размер мы укажем в данном конструкторе. Мы делаем это скорее для единообразия.

Последнее, что нам надо изменить, – метод отображения:

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

Рис. 8.24. Совершенствование примера с пушкой с помощью атласа текстур

Вот еще несколько вещей, которые стоит знать об атласах текстур.

Когда мы используем GLLI NEAR как фильтр минификации и/или магнификации, могут возникнуть побочные явления, при которых два изображения внутри атласа касаются друг друга. Это происходит из-за того, что в процессе ассоциирования текстуры выбирается по четыре ближайших тексела из текстуры для одного пиксела на экране. Когда выбор выполняется на границе изображения, захватываются также текселы из соседнего изображения в атласе. Мы можем решить эту проблему, создав пустую границу в два пиксела между нашими изображениями. Или еще лучше, мы можем дублировать пограничный пиксел каждого изображения. Первое решение, разумеется, проще – надо просто убедиться, что ваша текстура остается степенью 2.

Нет необходимости располагать все изображения в атласе в фиксированной сетке. Мы можем расположить изображения с произвольными размерами в атлас как можно ближе друг к другу. Нам нужно знать, где каждое изображение в атласе начинается и закачивается, чтобы мы могли подсчитать правильные текстурные координаты для него. Однако скомпоновать изображения с произвольным размером не так просто. В Интернете есть пара хороших программ, которые помогут вам создавать атласы текстур. Задайте запрос в поисковике, результатов будет море.

Часто мы не можем сгруппировать все изображения игры в одну текстуру. Помните, что есть максимальный размер текстуры, который зависит от устройства. Мы точно можем сказать, что все устройства поддерживают текстуры размером 512 х 512 пикселов (или даже 1024 х 1024). Поэтому мы просто создаем несколько атласов текстур. Желательно группировать в один атлас объекты, которые будут на экране вместе, например все объекты первого уровня поместить в один атлас, объекты второго уровня – во второй, все элементы пользовательского интерфейса – в третий и т. д. Продумайте логическое размещение объектов, прежде чем окончательно дорабатывать ваши художественные ресурсы.

Помните, как мы динамически рисовали цифры в игре с мистером Номом? Мы использовали для этого атлас текстур. Вообще, мы можем выполнять любое динамическое отображение текста при помощи атласа текстур. Просто добавьте все символы, которые нужны в игре, в атлас и отображайте их при необходимости при помощи нескольких треугольников, показывая нужные символы из атласа. В Интернете можно найти средства, которые помогут вам создать так называемый растровый шрифт (bitmap font). Однако для наших целей мы будем применять метод, который использовали в примере про мистера Нома: статический текст будет отображен заранее как единое целое, и только динамический текст (например, цифры при достижении рекорда) будет отображаться при помощи атласа.

Вы, вероятно, заметили, что Бобы исчезают немного раньше, чем по ним фактически попадает ядро. Это объясняется тем, что наши ограничивающие фигуры немного больше, чем нужно. Вокруг Боба и ядра по границам есть немного пустого пространства. Что с этим делать? Мы просто сделаем ограничивающие фигуры чуть меньше. Это надо прочувствовать, поэтому поэкспериментируйте с ресурсами, пока столкновение не будет фиксироваться правильно. Во время разработки игр вы будете иметь дело со множеством таких тонких моментов. Умение тщательно отлаживать игру, наверное, один из наиболее важных навыков, кроме качественного проектирования различных уровней. Прочувствовать некоторые вещи трудно, но зато какое чувство удовлетворения вы испытаете, достигнув идеала. К сожалению, этому я не могу вас научить, потому что все зависит от того, как вы сами ви-дите вашу игру. Считайте, что это та изюминка, которая отличает хорошие игры от плохих.

ПРИМЕЧАНИЕ

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

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии