2D Анимация

2D анимация – техника, используемая для создания иллюзии движения, используя статические изображения. Данная статья описывает, как создать анимацию в libGDX приложении.

Детали

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

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

libGDX анимация, спрайт-лист бегущего человека

В зависимости от того, как быстро бежит персонаж, нужно определить, сколько времени кадр будет оставаться на экране. Кадровая частота – количество сменяемых кадров за секунду. Посмотрев на спрайт-лист, мы увидим полный цикл бега, состоящий из 30 кадров. Если полный цикл бега персонажа укладывается в одну секунду, то мы должны показывать 30 кадров в секунду. Это дает нам кадровую частоту в 30 FPS (Frames Per Second). Двигаясь дальше, нетрудно рассчитать время состояния (время кадра), которое показывает, сколько времени кадр должен отображаться на экране перед тем, как его сменит следующий кадр. 1 секунда / 30 = 0,033.

Другими словами, чтобы анимация была в 30 FPS, кадр должен сменяться каждые 0.033 секунды.

Анимация является простым конечным автоматом. Так, бегущий человек имеет 30 состояний, согласно спрайт-листу. Пронумерованные кадры бегущего человека представляют состояния, через которые он проходит.

libGDX анимация, состояние кадров в спрайт-листе бегущего человека

В любой момент времени, конечный автомат может находиться в двух или более состояниях. Когда персонаж находится в состоянии 1, то рисуется спрайт, связанный с этим состоянием. Персонаж находится в этом состоянии 0.033 секунды и как только это время проходит, он движется (переходит) в следующее состояние, которое равно 2. Это продолжается пока не будет достигнуто последнее состояние или кадр.

Циклическая анимация означает переход на начало, когда анимация достигает последнего кадра.

Использовать анимацию в libGDX чрезвычайно просто. Есть одно ограничение, касающееся размера спрайт-листа, которое нужно запомнить: когда используется OpenGL 1.x, размер спрайта должен быть степенью двойки.

Следующий фрагмент кода создает объект Animation (код) класса, используя animation_sheet.png спрайт-лист и визуализирует анимацию на экране. В данном случае ApplicationListener будет очень простым.

public class Animator implements ApplicationListener {

    private static final int FRAME_COLS = 6; // #1
    private static final int FRAME_ROWS = 5; // #2

    Animation walkAnimation; // #3
    Texture walkSheet; // #4
    TextureRegion[] walkFrames; // #5
    SpriteBatch spriteBatch; // #6
    TextureRegion currentFrame; // #7

    float stateTime; // #8

    @Override
    public void create() {
        walkSheet = new Texture(Gdx.files.internal("animation_sheet.png")); // #9
        TextureRegion[][] tmp = TextureRegion.split(walkSheet, walkSheet.getWidth()/FRAME_COLS, walkSheet.getHeight()/FRAME_ROWS); // #10
        walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];
        int index = 0;
        for (int i = 0; i < FRAME_ROWS; i++) {
            for (int j = 0; j < FRAME_COLS; j++) {
                walkFrames[index++] = tmp[i][j];
            }
        }
        walkAnimation = new Animation(0.025f, walkFrames); // #11
        spriteBatch = new SpriteBatch(); // #12
        stateTime = 0f; // #13
    }

    @Override
    public void render() {
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // #14
        stateTime += Gdx.graphics.getDeltaTime(); // #15
        currentFrame = walkAnimation.getKeyFrame(stateTime, true); // #16
        spriteBatch.begin();
        spriteBatch.draw(currentFrame, 50, 50); // #17
        spriteBatch.end();
    }
}

Настройка

# 1 и # 2 определяют константы, представляющие, сколько спрайтов будет расположено по вертикали и горизонтали в спрайт-листе.

Спрайт-лист содержит кадры одинакового размера, и все они выровнены.

#3 – объявление walkAnimation объекта, который является реализацией libGDX анимации.

#4 – Текстура, которая будет содержать весь лист в виде одного изображения (текстуры).

#5 – Объявление walkFrames как массива объектов TextureRegion. Массив будет содержать каждый кадр (спрайт) анимации. Первый элемент содержит верхний левый кадр, второй элемент содержит следующий справа кард и так далее. При достижении последнего элемента ряда, следующий ряд начинается с самого левого элемента.

#6 – SpriteBatch используется для рисования текстуры на экране.

#7 – Переменная currentFrame будет содержать текущий кадр и представляет собой регион, который рисуется при каждом вызове визуализации.

#8 – stateTime представляет количество секунд, прошедших с начала анимации. Используется для определения состояния анимации. Представляет простой аккумулятор, на основе которого анимация знает, когда перейти к следующему состоянию.

Например, если анимация 30 FPS, то изменение состояния должно происходить каждые 33,3 миллисекунды. Если обновление происходит каждые 10 миллисекунд, то stateTime содержит прошедшее время с начала анимации и переход анимации к следующему состоянию (кадру) будет на четвертом обновлении.

#9 – Создает текстуру из animation_sheet.png файла, находящегося в assets директории проекта (смотрите настройку проекта).

#10 и #11 – Используя TextureRegion.split() метод и текстуру, получаем двумерный массив кадров из текстуры. Имейте в виду, что это работает только, если кадры имеют одинаковый размер. С помощью временной переменной tmp происходит наполнение walkFrames массива. Это необходимо, так как анимация работает только с одномерными массивами.

#12 – инициализирует SpriteBatch, который будет рисовать кадр.

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

Метод визуализации

#14 – Очищает экран при каждом кадре.

#15 – Добавляет время в stateTime, прошедшее с момента последней визуализации.

#16 – Получает текущий кадр, основываясь на текущем времени анимации. Вторая переменная для цикличности. Передавая true, мы говорим анимации о возобновлении после достижения последнего кадра.

#17 – Визуализирует текущий кадр на экране, используя SpriteBatch на 50,50.

Запуск вышеуказанного фрагмента кода покажет хорошую гладкую анимацию бега человека.

Результат libGDX анимации

Создание анимации, используя следующий конструктор, очень простое.

Метод и описание
Animation(float frameDuration, TextureRegion... keyFrames)

Первым параметром является время кадра, а второй параметр представляет собой массив из регионов (кадров), составляющих анимацию.

Лучшие практики

  • Упаковка кадров в одну текстуру для оптимизации визуализации.
  • Разумное число кадров в зависимости от типа игры. Для аркады в ретро стиле достаточно 4 кадров, в тоже время для более реалистичного движения требуется больше кадров.

Assets

Спрайт-лист бегущего человека.