Статьи

Анимации со спрайт-листами в SDL2

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

Наши настройки проекта для этой статьи такие же, как в « Загрузка изображений в SDL2 с помощью SDL_image », и фактически наш стартовый код адаптирован из этой статьи:

#include <SDL.h>
#include <SDL_image.h>

int main(int argc, char ** argv)
{
    bool quit = false;
    SDL_Event event;

    SDL_Init(SDL_INIT_VIDEO);
    IMG_Init(IMG_INIT_PNG);

    SDL_Window * window = SDL_CreateWindow("SDL2 Sprite Sheets",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
    SDL_Surface * image = IMG_Load("spritesheet.png");
    SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);

    while (!quit)
    {
        SDL_WaitEvent(&event);

        switch (event.type)
        {
            case SDL_QUIT:
                quit = true;
                break;
        }

        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_FreeSurface(image);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();

    return 0;
}

sdl2-spritesheet-границы

Это изображение 128 пикселей в ширину и 64 пикселей в высоту. Он состоит из 4 подизображений (называемых спрайтами  или кадрами ), каждое из которых имеет ширину 32 пикселя. Если мы сможем быстро рендерить каждое изображение, как мультфильм, то у нас есть анимация!
Теперь эти уродливые границы на изображении выше только для демонстрационных целей Вот то же изображение, без границ и с прозрачностью:

sdl2-spritesheet-фактическое

Если мы сейчас попытаемся нарисовать выше на черном фоне по умолчанию, мы ничего не увидим, не так ли? К счастью, легко изменить цвет фона, и мы делали это раньше в разделе « Обработка событий клавиатуры и мыши в SDL2 ». Просто добавьте следующие две строки перед  while циклом:

    SDL_SetRenderDrawColor(renderer, 168, 230, 255, 255);
    SDL_RenderClear(renderer);

Теперь мы посмотрим, как будет выглядеть вывод. Нажмите Ctrl + Shift + B, чтобы построить проект, а затем скопируйте SDL2.dll , все библиотеки DLL SDL_image и таблицу спрайтов в папку Debug, где создается исполняемый файл.

Как только это будет сделано, нажмите F5:

sdl2-spritesheet быстротвердеющий

Итак, на данный момент есть две проблемы, которые мы хотим решить. Во-первых, мы не хотим, чтобы наше изображение занимало все окно, как это делается выше. Во-вторых, мы хотим рисовать только один спрайт за раз. И то, и другое довольно легко решить, если вы помните последние два параметра SDL_RenderCopy () : исходный прямоугольник (для рисования только части изображения) и целевой прямоугольник (для рисования изображения только в части экрана) ,

Итак, давайте добавим следующее в начале  while цикла:

        SDL_Rect srcrect = { 0, 0, 32, 64 };
        SDL_Rect dstrect = { 10, 10, 32, 64 };

… А затем обновите наш  вызов SDL_RenderCopy ()  следующим образом:

        SDL_RenderCopy(renderer, texture, &srcrect, &dstrect);

Обратите внимание , что синтаксис мы используем для инициализации нашего  SDL_Rect s только обсчитывать , чтобы установить все x, y, w (ширина) и h (высота) члены все сразу.

Давайте снова запустим программу и посмотрим, как она выглядит:

sdl2-spritesheet-обрезается

Итак, вот так мы просто визуализируем первый спрайт для части окна. Теперь давайте поработаем над анимацией. В начале  while цикла добавьте следующее:

        Uint32 ticks = SDL_GetTicks();

SDL_GetTicks () дает нам количество миллисекунд, прошедших с момента запуска программы. Благодаря этому мы можем использовать текущее время при расчете, какой спрайт использовать. Затем мы можем просто разделить на 1000, чтобы преобразовать миллисекунды в секунды:

Затем мы делим секунды на количество спрайтов в нашей таблице спрайтов, в данном случае 4. Использование оператора модуля гарантирует, что число спрайтов будет округлено, поэтому оно никогда не будет больше 3 (помните, что подсчет всегда начинается с нуля, поэтому наш спрайты пронумерованы от 0 до 3).

       Uint32 sprite = seconds % 4;

Наконец, мы заменяем нашу srcrect декларацию следующим:

        SDL_Rect srcrect = { sprite * 32, 0, 32, 64 };

Вместо использования x нулевого значения, как мы делали раньше, мы передаем sprite значение (от 0 до 3, основанное на текущем времени), умноженное на 32 (ширина одного спрайта). Таким образом, с каждой прошедшей секундой спрайт будет извлечен из изображения при x = 0, затем x = 32, затем x = 64, затем x = 96, обратно в x = 0 и так далее.

Давайте запустим это снова:

sdl2-spritesheet-нерегулярная

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

К счастью, обе эти проблемы можно решить с помощью кода, который мы уже использовали в « Обработка событий клавиатуры и мыши в SDL2 ». Первая проблема заключается в том, что мы используем  SDL_WaitEvent () , поэтому программа ничего не делает, если не происходит какое-либо событие. Таким образом, нам нужно заменить наш вызов  SDL_WaitEvent ()  на вызов  SDL_PollEvent () :

        while (SDL_PollEvent(&event) != NULL)
        {
            switch (event.type)
            {
                case SDL_QUIT:
                    quit = true;
                    break;
            }
        }

Вторая проблема заключается в том, что мы рисуем спрайты без очистки тех, которые мы рисовали раньше. Все, что нам нужно сделать, это добавить вызов  SDL_RenderClear (),  прежде чем мы вызовем  SDL_RenderCopy () :

        SDL_RenderClear(renderer);

Большой! Теперь вы можете восхищаться нашим маленьким персонажем,  тасующим со  скоростью один кадр в секунду:

sdl2-spritesheet-goodslow

Это хорошо, но немного медленно. Мы можем сделать это быстрее, заменив код анимации перед  srcrectобъявлением следующим (10 кадров в секунду):

        Uint32 ticks = SDL_GetTicks();
        Uint32 sprite = (ticks / 100) % 4;

Woohoo! Посмотри на этого маленького парня, танцуй! (Изображение ниже является анимированным, но, похоже, это работает только в Firefox.)

sdl2-spritesheet анимированный

Итак, в этой статье мы узнали, как анимировать простых персонажей, используя спрайт-листы, которые на самом деле являются просто цифровой версией мультфильма. Мы использовали параметр  SDL_RenderCopy () , srcrect чтобы рисовать только один спрайт из листа за раз, и выбрали этот спрайт, используя текущее время, на основе  SDL_GetTicks ().

Это обновленная версия « SDL2: Анимации со списками спрайтов », первоначально опубликованная 30 марта 2014 года на ранчо программистов. Исходный код доступен в репозитории Gigi Labs BitBucket .