§3 Cпрайты и анимация

Классы Texture и Sprite

Класс Texture предназначен для работы с пикселями текстуры. Текстура представляет собой изображение использующая ресурсы видеокарты. В этой связи, работа с данными в этом классе осуществляется быстро. Каждый пиксель несет 32 bit памяти – по 1 байту на канал: 8 bit красный (R), 8 bit зеленый (G), 8 bit синий (B) и 8 bit альфа-канал (A, прозрачность). Текстура может быть загружена из файла, памяти или потока. Класс позволяет производить различные манипуляции с пикселями. Ну, собственно, этим и ограничивается работа с классом. Таким образом, смысл данного класса – подготовить основу для создания спрайта.
Спрайтом называется “графический” объект в программе, то есть текстура или рисунок взятый из файла. Поддерживаются следующие форматы графических файлов: bmp, png, tga, jpg (но не прогрессивный), gif, psd, hdr и pic.
Прямоугольники класса RectangleShape, как и объекты других классов, могут, в качестве фона, иметь текстуру, т. е. превратиться в спрайт. Однако, спрайт можно показать и без использования какого-либо класса *Shape. Для этого используется одноименный класс Sprite. Создадим проект и в папке проекта создадим папку pic (в дальнейшем мы будем использовать папку с таким именем для хранения изображений и в других проектах). Поместим в эту папку изображение (текстуру) в формате .png.
Программа 3.1

#include <SFML/Graphics.hpp>
using namespace sf;
int main() {
    RenderWindow window(VideoMode(500, 500), "lesson-3-1");
    // Определяем текстуру
    Texture texture;
    if (!texture.loadFromFile("./pic/tux.png"))
        return -1;
    // Определяем спрайт
    Sprite sprite;
    sprite.setTexture(texture);

    while (window.isOpen()) {
        Event event;
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        window.clear();
        // Рисуем спрайт
        window.draw(sprite);
        window.display();
    }
	return 0;
}

Файл изображения пингвина
Вот что у нас получилось:

Перемещение спрайта. Анимация

Все классы, наследники класса Transformable (в том числе Sprite и Shape) содержат метод move(), обеспечивающий перемещение объекта по окну программы. Чтобы создать анимацию объекта, этого метода недостаточно, поскольку необходимо определять временные интервалы (используются секунды, миллисекунды или микросекунды), чтобы задать новую позицию объекта. Такую работу выполняет класс sf::Clock.
В этом классе всего два метода: getElapsedTime(), предназначенный для получения интервала времени с момента последнего запуска, и restart(), предназначенный для перезапуска таймера.
Заставим Тукса ходить по диагонали взад-вперед.
Программа 3.2

#include <SFML/Graphics.hpp>
using namespace sf;
int main() {
    RenderWindow window(VideoMode(500, 500), "lesson-3-2");
    Texture texture;
    if (!texture.loadFromFile("./pic/tux.png"))
        return -1;
    Sprite sprite;
    sprite.setTexture(texture);
    // Включаем таймер
    Clock clock;

    // Скорость определим в переменой
    float speed = 100.f;
    // А в этой переменной мы определяем направление
    float way = 1.f;
    while (window.isOpen()) {
        Event event;
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        // Запускаем таймер; отсчеты выполняем в секундах
        float delta = clock.restart().asSeconds();
        // Смотрим, дошел ли до края?
        // если да, меняем направление
        if (sprite.getPosition().x > 380) way = -1;
        if (sprite.getPosition().x < 0) way = 1;
        // Перемещаем Тукса
        sprite.move(speed * delta * way, speed * delta * way);

        window.clear();
        window.draw(sprite);
        window.display();
    }
	return 0;
}

В этой программе мы используем метод getPosition(), который возвращает текущую позицию объекта (по определенной координате); метод restart(), как мы уже сказали, запускает таймер и определяет отсчеты в секундах от начала запуска и, таким образом, оживляет композицию (посредством использования переменной delta).
Примечание. В данном случае возвращается действительное число. Для милли- и микросекунд возвращается целое число.
В некоторых случаях, после запуска таймера, требуется несколько раз делать его рестарт. Покажем это на полезном примере. В этой программе создается пульсирующая окружность.
Программа 3.3

#include  <SFML/Graphics.hpp>
using namespace sf;

int main() {
	int W = 500;
	int H = 500;
	RenderWindow window(VideoMode(W, H), "lesson-3-3");
    CircleShape Pls;
    Pls.setOutlineColor(Color(178, 34, 34, 255));
    Pls.setFillColor(Color(30, 144, 255, 255));
    // Скорость пульсаций
    float speed = 10000.f;
    // Для смены расширения сжатием
    float way;
    // Максимальный и минимальный радиус
    float Max = 100;
    float Min = 5;
    Clock clock;

    while (window.isOpen()) {
    	Event event;
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        window.clear();
        // Для каждого из процессов делаем рестарт таймера
        if (Pls.getRadius() > Max) { way = 0; clock.restart(); }
        if (Pls.getRadius() < Min) { way = 1; clock.restart(); }
        // Имеет значение когда вызывать getElapsedTime()
        // до рестарта или после него
        float delta = clock.getElapsedTime().asMicroseconds();
        float R = delta / speed;
        if (way) {
        	Pls.setRadius(Min + R);
        	Pls.setOutlineThickness(3 + R * 0.1);
        	// Размер окружности изменяется не от центра,
        	// поэтому центрируем по величине радиуса
        	Pls.setPosition(W / 2 - Min - R, H / 2 - Min - R);
        } else {
        	Pls.setRadius(Max - R);
        	Pls.setOutlineThickness(8 - R * 0.1);
        	Pls.setPosition(W / 2 - Max + R, H / 2 - Max + R);
        }
        window.draw(Pls);
        window.display();
    }
    return 0;
}


Метод getRadius() позволяет запросить текущее значение радиуса окружности.
Создадим программу “Секундомер”. Для этого нам потребуется текстура циферблата, а стрелку мы нарисуем силами SFML.
Программа 3.4

#include <SFML/Graphics.hpp>
using namespace sf;
int main() {
    RenderWindow window(VideoMode(500, 500), "lesson-3-4");
    Texture texture;
    if (!texture.loadFromFile("./pic/ciferblat.png"))
        return -1;
    Sprite ciferblat;
    ciferblat.setPosition(0,0);
    ciferblat.setTexture(texture);
    // Создаем стрелку
    RectangleShape strelka(Vector2f(5, 225));
    // Устанавливаем позицию откуда начинать рисовать
    strelka.setPosition(250, 250);
    // И поворачиваем, чтобы установить конец стрелки вверху
    strelka.rotate(180);
    strelka.setFillColor(Color(255, 0, 0));
    Clock clock;

    while (window.isOpen()) {
        Event event;
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        window.clear();
        // Перекрашиваем окно в белый
        window.RenderTarget::clear(Color::White);
        float delta = clock.restart().asSeconds();
        // Каждую секунду поворачиваемся на 6 градусов
        strelka.rotate(6 * delta);
        // Рисуем спрайт
        window.draw(ciferblat);
        // Показываем текущее положение стрелки
        window.draw(strelka);
        window.display();
    }
	return 0;
}


Циферблат в формате .png
В программе 3.4 использовался метод rotate() для поворота линии, изображающей стрелку. Метод RenderTarget::clear перекрашивает окно в заданный цвет.

Задания для самостоятельной работы

  • Создайте программу “Часы”. В программе должны быть реализованы три стрелки: часовая, минутная и секундная.
  • Измените программу 3.3 так, чтобы в окне выводились различные пульсирующие объекты
  • Придумайте свой эффект для пульсирующего объекта, например, эхо пульсаций
  • Создайте программу имитирующую движение тела брошенного под углом к горизонту
Оцените материал
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 5,00 из 5)
Загрузка...

Print Friendly, PDF & Email

Comments are closed.