Рисование 2D объектов

Окно для рисования

Для рисования объектов с помощью графического модуля, вам необходимо использовать специальный класс окна: sf::RenderWindow. Этот класс является наследником класса sf::Window и обладает всем методами класса родителя. Всё что вы изучили о sf::Window (создание, обработка событий, настройка частоты кадров, смешанное использование с OpenGL и т.д.), применимо и к sf::RenderWindow.

Помимо этого, sf::RenderWindow имеет методы облегчающие задачу рисования объектов. В этом уроке мы сосредоточимся на двух таких методах: clear() и draw(). Они просты как и их названия: clear() очищает окно выбранным цветом, а draw() рисует любой переданный ему объект.

Так выглядит типичный главный цикл с окном визуализации (render window):

#include <SFML/Graphics.hpp>

int main()
{
 // создание окна
 sf::RenderWindow window(sf::VideoMode(800, 600), "My window");

 // программа работает столько, сколько открыто окно
 while (window.isOpen())
 {
 // проверяем все события окна которые сработали с момента последней итерации цикла
 sf::Event event;
 while (window.pollEvent(event))
 {
 // событие "закрытия": мы закрываем окно
 if (event.type == sf::Event::Closed)
 window.close();
 }

 // очищаем окно чёрным цветом
 window.clear(sf::Color::Black);

 // рисуем всё здесь...
 // window.draw(...);

 // конец текущего кадра
 window.display();
 }

 return 0;
}

Вызов clear(), перед рисованием чего-либо, является обязательным, поскольку иначе, пиксели с предыдущего кадра наложатся на текущий. Единственное исключение, это когда вы перерисовываете абсолютно все пиксели в окне.

Вызов display() так же обязателен, он берёт всё объекты отданные на отрисовку (методом draw()) с последнего вызова display(), и отображает их в окне. На самом деле объекты рисуются не сразу в окно, а предварительно в скрытый буфер. Этот буфер затем копируется в окно вызовом display() — это называется двойной буферизацией.

Цикл clear()/draw()/display() это единственный хороший способ для рисования объектов. Не пытайтесь использовать другие стратегии, такие как сохранение пикселей с предыдущего кадра, «стирание» отдельных пикселей и т.п. В этих случаях вы можете получить странные результаты из-за двойной буферизации. Современные видеокарты и их API заточены под повторяющиеся циклы clear()/draw()/display(). Не бойтесь рисовать 1000 спрайтов по 60 раз в секунду, это намного меньше миллионов треугольников которые ваш компьютер способен обработать.

Что я теперь могу нарисовать?

Теперь у вас есть главный цикл готовый к рисованию, давайте же рассмотрим что и как вы можете в нём нарисовать.

 SFML поддерживает четыре типа объектов которые можно нарисовать: три из них сразу готовы к использованию (спрайты (sprites), тексты (texts) и фигуры (shapes)), а четвёртый является строительным блоком для создания ваших собственных объектов (массивы вершин (vertex arrays)).

Не смотря на то, что у них есть общие свойства, каждый из этих типов объектов заслуживает отдельных уроков:

  • (Как только переведу их тут будут ссылки..наберитесь терпения)

Рисование за кадром

SFML так же обеспечивает способ рисования в текстуру, вместо рисования в окне. Для этого, следует использовать sf::RenderTexture вместо sf::RenderWindow. Этот класс имеет такие же методы для рисования, унаследованные от их общего предка sf::RenderTarget.

// создаём текстуру 500x500 на которой будем рисовать
sf::RenderTexture renderTexture;
if (!renderTexture.create(500, 500))
{
 // ошибка...
}

// для рисования используются те же функции
renderTexture.clear();
renderTexture.draw(sprite); // или что то другое что можно нарисовать
renderTexture.display();

// получаем текстуру (на которой был нарисован объект)
sf::Texture& texture = renderTexture.getTexture();

// рисуем её в окне
sf::Sprite sprite(texture);
window.draw(sprite);

Метод getTexture() возвращает текстуру, доступную только для чтения, т.е. вы можете использовать её, но не изменять. Если вам необходимо её изменить перед тем как использовать, то скопируйте её в другой объект класса sf::Texture.

Кроме того, sf::RenderTexture имеет те же самые методы что и sf::RenderWindow для управления видами и OpenGL (см. соответствующие уроки). Если вы используете OpenGL для рисования на текстуре, вы можете в конструкторе указать третий параметр, который отвечает за использование буфера глубины (depth buffer).

renderTexture.create(500, 500, true); // включаем depth buffer

Рисование из потока

SFML поддерживает рисование в многопоточном режиме, и вам практически не нужно ничего делать, что бы всё работало как надо. Единственная вещь которую стоит помнить: перед использованием окна в другом потоке, его необходимо деактивировать; всё потому, что окно (точнее его контекст рисования OpenGL) не может быть активным в двух потоках одновременно.

void renderingThread(sf::RenderWindow* window)
{
 // цикл визуализации
 while (window->isOpen())
 {
 // рисуем...

 // конец текущего кадра
 window->display();
 }
}

int main()
{
 // создаём окно (помните: безопаснее всего создавать его в главном потоке из за ограничений ОС)
 sf::RenderWindow window(sf::VideoMode(800, 600), "OpenGL");

 // деактивируем контекст OpenGL
 window.setActive(false);

 // запуск визуализации в потоке
 sf::Thread thread(&renderingThread, &window);
 thread.launch();

 // цикл обработки событий, логики и прочего..
 while (window.isOpen())
 {
 ...
 }

 return 0;
}

Как видите, вам даже не нужно возится с активацией окна в потоке с визуализацией. SFML делает это автоматически за вас когда это необходимо.

Главное помните что создавать окно и обрабатывать его события необходимо в главном потоке (см. в уроке Открытие и управление окном SFML).

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

  1. Владимир

    Рисовать то легко примитивами, а как указать координаты для рисования и задать размеры фигуры? Просто не понимаю как мне нарисовать квадрат в нужном месте…

  2. Владимир

    И есть ли нормальные примеры не для детей. Просто на Линукс я смотрю фиг что найдешь, вот ваши уроки это всё что есть, но тут так сыро. Много текста, мало информации, однотипно и выше основ нет материала. Может что напишете интереснее или поделитесь ссылкой на любые другие источники кроми хабров и киберфорумов. Заранее спасибо.

    1. samuel_unknown (Автор записи)

      Эти статьи про SFML не являются уроками, это перевод (и то незавершённый) документации с оф.сайта. По поводу примеров посложнее, я даже и не знаю.. Если вы пишите прям под линукс то берите движок Unity, я вообще жалею немного что тратил время на SFML при создании первой игры, а не делал её сразу на Unity, но это тоже был полезный опыт. :)

Добавить комментарий для samuel_unknown Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Капча * Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.