Главная » Программирование игр под Android » СПРАЙТ-АНИМАЦИЯ – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

Если вы когда-нибудь играли в 20-видеоигру, то заметили, что мы до сих пор не занимались одним очень важным компонентом – спрайт-анимацией. Анимация состоит из так называемых ключевых кадров, которые создают иллюзию движения. На рис. 8.25 вы можете увидеть прекрасный анимированный спрайт, созданный Ари Фельдманном (взят из его безгонорарной библиотеки SpriteLib).

Рис. 8.25. Идущий пещерный человек, созданный Ари Фельдманном (в оригинале – без сетки)

Изображение имеет размеры 256 х 64 пиксела, каждый кадр – 64 х 64 пиксела. Для создания анимации мы просто рисуем спрайт, используя первый ключевой кадр, некоторый период времени – скажем, 0,25 секунды, затем переключаемся на следующий ключевой кадр и т. д. Когда мы достигаем последнего кадра, есть два варианта: мы можем остаться на последнем кадре или снова начать сначала (и реализовать так называемую циклическую анимацию).

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

Класс Animation

Исходя из этого, мы можем определить требования к классу Ani mat i on, который будет хранить данные об одной анимации, такой как анимация ходьбы на рис. 8.25.

Animation будет содержать несколько TextureRegion, которые будут хранить информацию о позиции каждого кадра в атласе текстур. Порядок TestureRegion должен быть таким же, как при воспроизведении кадров.

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

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

Последнее требование особенно важно, потому что оно позволяет нам хранить только один экземпляр Animation для использования разными объектами нашего мира. Объект просто следит за своим текущим состоянием (например, стреляет ли объект, ходит или прыгает, а также как долго он находится в этом состоянии). Когда мы рендерим этот объект, мы используем его состояние для выбора анимации, которую мы хотим воспроизвести, и продолжительность состояния, чтобы выбрать нужный TextureRegion из Animation. В листинге 8.19 показан код нашего нового класса Ani mation.

Листинг 8.19. Animation.lava, простой класс Animation

Сначала определяем две константы, которые будем использовать в методе getKeyFrame. Первая указывает, что анимация должна быть циклической, вторая – что анимация должна остановиться на последнем кадре.

Далее задаем два члена: массив, содержащий TextureRegi on, и переменную f1oat, которая хранит длительность кадра.

Передаем длительность кадра и TextureRegi on, которые содержат ключевые кадры, в конструктор, который просто их сохраняет. Мы могли бы создать внутреннюю копию массива keyFrames, но для этого нужно будет выделить еще один объект, который явно сведет с ума программу по очистке памяти.

Самое интересное тут заключается в методе getKeyFrameC. Мы передаем ему время, которое объект провел в состоянии, представленном анимацией, а также режим Ani mati on. AN IMATI0N L00P I NG или Animtion. N0N L00P I NG. Сначала подсчитываем на основе stateTime, сколько кадров уже было отображено к данному моменту. Если анимация нециклическая, просто закрепляем frameNumber за последним элементом массива TextureRegi on. В другом случае берем модуль, который автоматически создает эффект цикличности, который нам нужен (например, 4 % 3 = 1). Осталось только вернуть соответствующий TextureRegi on.

 

Пример

Создадим пример под названием Ani mati onTest с соответствующим экраном AnimationScreen. Как и обычно, обсудим только экран.

Мы хотим отрендерить нескольких пещерных людей, идущих влево. Мир будет таких же размеров, как и конус отображения, то есть 4,8 х 3,2 м (этот размер произвольный, на самом деле мы можем использовать любой размер). Пещерный человек является Dynami cGameObject размером 1 х 1 м. Выведем из DynamicGameObject новый класс Caveman, в котором будет храниться дополнительный член класса, следящий за тем, как долго пещерный человек уже находится в движении. Каждый пещерный человек будет двигаться со скоростью 0,5 м/с вправо или влево. Мы также добавим в класс Caveman метод update для обновления позиции пещерного человека в соответствии с дельтой времени и его скоростью. Если пещерный человек достигает правого или левого края нашего мира, устанавливаем его с другой стороны мира. Мы используем изображение с рис. 8.25 и создадим TextureRegion и экземпляр Animati on соответственно. Для рендеринга применяем экземпляр класса Camera2D и SpriteBatcher. В листинге 8.20 показан код класса Caveman.

Листинг 8.20. Фрагмент из AnimationTest, внутренний класс Caveman

Две константы W0RLD WIDTH и W0RLD HEIGHT являются частями внешнего класса AnimationTest и используются внутренним классом. Наш мир имеет размеры 4,8 х 3,2 м.

Далее следует внутренний класс Caveman, который дополняет DynamicGameObject, поскольку мы будем перемещать пещерного человека с какой-то скоростью. Мы определяем дополнительный член класса для отслеживания того, как долго пещерный человек движется на данный момент. В конструкторе помещаем пещерного человека в произвольную позицию и позволяем идти направо или налево. Мы также Присваиваем члену класса wal ki ngTime номер от 1 до 10; таким образом наши пещерные люди не будут двигаться синхронно.

Метод update перемещает пещерного человека в соответствии с его скоростью и дельтой времени. Если человек покидает мир, мы восстанавливаем его с правой или левой стороны. Мы также добавляем дельту времени к wa1kingTime, чтобы следить, как долго он уже движется. Листинг 8.21 показывает класс AnimationScreen.

Листинг 8.21. Фрагмент из Animation.Java: класс AnimationScreen

В классе экрана все члены будут нам уже привычны. Так, у нас есть экземпляр класса GLGraphics, массив Caveman, классы SpriteBatcher, Camera2D, класс Texture, содержащий ключевые кадры ходьбы, и экземпляр класса Animation.

В конструкторе создаем экземпляр класса Caveman, а также классов SpriteBatcher и Camera2D.

В методе resume загружаем текстурный атлас, содержащий ключевые кадры анимации, из ресурсного файла под названием wal kanim. png, который выглядит так же, как рис. 8.25. После этого создаем экземпляр класса Animation, установив длительность кадра в 0,2 секунды и передаем в него TextureRegion для каждого ключевого кадра в атласе текстур.

Метод update просто перебирает все экземпляры класса Caveman и вызывает их метод Caveman. update  с текущей дельтой времени. Это заставляет пещерных людей передвигаться и обновляет их время ходьбы.

И наконец, у нас есть метод present . Мы начинаем с чистки экрана и установления области просмотра и проекционной матрицы с помощью нашей камеры. Затем включаем смешивание и обработку, текстур и устанавливаем функцию смешивания. Начинаем отображение, сообщая сортировщику спрайтов, что мы хотим завести новый пакет, используя анимационный атлас текстур. Затем проходим по всем пещерным человечкам и отображаем их. Для каждого пещерного человека находим сначала нужный ключевой кадр в экземпляре класса Ani mati on в соответствии со временем ходьбы человека. Указываем, что анимация должна быть циклической. Затем рисуем пещерного человека с правильным текстурным фрагментом на его месте.

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

Наши гуляющие пещерные люди изображены на рис. 8.26.

Вот и все, что нужно знать, чтобы создать хорошую 2D -игру с помощью OpenGL. Снова обратите внимание на то, что мы разделяем логическую составляющую игры и графическое представление. Пещерному человеку совсем необязательно знать, что его отображают. Поэтому он не содержит никаких членов, относящихся к рендерингу, как, например, экземпляр класса Animation или Texture. Все, что нам нужно делать, – следить за состоянием пещерного человека и тем, как долго он уже находится в этом состоянии. Добавив к этому его позицию и размер, мы можем легко осуществить отображение с помощью наших маленьких вспомогательных классов.

Рис. 8.26. Гуляющие пещерные люди

Подводя итог

Теперь вы достаточно подкованы, чтобы создать практически любую 2D -игру, какую хотите. Мы обсудили векторы и работу с ними, в итоге получив красивый класс Vector2, который можем использовать многократно. Мы также вспомнили основы физики для создания таких объектов, как баллистические пушечные ядра. Определение столкновений – также важнейшая часть большинства игр, и вы должны знать, как правильно и эффективно определять столкновения между объектами при помощи SpatialHashGrid. Мы рассмотрели, как отделять логическую составляющую игры и объекты от процесса отображения, создав классы GameObject и Dynami cGameObject, которые отслеживают состояние и форму объектов. Мы узнали, как просто при помощи OpenGL ES и одного только метода glOrthof  можно реализовать идею 20-камеры. Мы обсудили атласы текстур, выяснили, почему и зачем мы их применяем. Развив идею, мы поговорили о фрагментах текстуры, спрайтах и том, как можно эффективно отображать их при помощи Spri teBatcher. И наконец, изучили спрайт-анимацию, которая оказалась очень проста.

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

По теме:

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