Ты всю жизнь ощущал, что мир не в порядке. Странная мысль, но её не отогнать. Она — как заноза в мозгу. Она сводит с ума, не дает покоя. Это и привело тебя ко мне… Примешь синюю таблетку — и сказке конец. Ты проснешься в своей постели и поверишь, что это был сон. Примешь красную таблетку — войдешь в страну чудес. Я покажу тебе, насколько глубока библиотека SFML кроличья нора.
Круги
Предлагаю для начала потренироваться на простых геометрических фигурах типа Михаила круга. В этом деле большую помощь нам окажет класс CircleShape:
конструктор данного класса принимает в качестве параметра радиус нашей будущей фигуры (например, circle(50.f)
);
закрасить фигуру можно с помощью метода setFillColor()
, который очень похож на уже знакомый нам метод window.clear()
;
ну и для отображения круга в окне используется метод window.draw()
.
Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <SFML/Graphics.hpp> using namespace sf; // подключаем пространство имен sf int main() { // Объект, который, собственно, является главным окном приложения RenderWindow window(VideoMode(200, 200), "SFML Works!"); // Главный цикл приложения: выполняется, пока открыто окно while (window.isOpen()) { // Обрабатываем очередь событий в цикле Event event; while (window.pollEvent(event)) { // Пользователь нажал на «крестик» и хочет закрыть окно? if (event.type == Event::Closed) window.close(); // тогда закрываем его } // Установка цвета фона window.clear(Color(250, 220, 100, 0)); // Создаем фигуру - круг радиусом 50 CircleShape circle(50.f); // Закрашиваем наш круг circle.setFillColor(Color(230, 0, 230)); // Отрисовка круга window.draw(circle); // Отрисовка окна window.display(); } return 0; } |
Результат выполнения программы:
Наша фигура может иметь контур. Для его создания используется метод setOutlineThickness(), а для цвета контура — setOutlineColor():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// ...вышеприведенный код до строки №21 // Установка цвета фона window.clear(Color(250, 220, 100, 0)); // Создаем фигуру - круг радиусом 50 CircleShape circle(50.f); // Закрашиваем наш круг circle.setFillColor(Color(230, 0, 230)); // Устанавливаем толщину контура круга circle.setOutlineThickness(15.f); // Устанавливаем цвет контура circle.setOutlineColor(Color(80,220,50)); // Отрисовка круга window.draw(circle); // Отрисовка окна window.display(); } return 0; } |
Результат выполнения программы:
Как уже знаем из предыдущего урока, при закрашивании фигур или фона можно еще указать значение прозрачности. Например, строкой setOutlineColor(Color(80, 220, 50, 150))
мы устанавливаем 150
в качестве значения прозрачности контура:
А теперь с помощью строки setOutlineColor(Color(80, 220, 50, 50))
мы установим 50
в качестве значения прозрачности контура:
Вы уже наверняка заметили, что наш круг выходит за границы окна, а это не совсем хорошо. Нужно его немного подвинуть, а поможет нам в этом метод move():
1 2 |
// Перемещаем круг для корректного отображения в окне circle.move(15, 15); |
Результат выполнения программы:
Регулярные полигоны
-Welcome to the real world!
Отлично! Сейчас мы рассмотрим, как нарисовать и другие фигуры. Теперь ты готов узнать истину. Она заключается в том, что, на самом деле, ложки не существует, Нео твой круг — это немножко не круг, а многоугольник. Да-да, самый обычный многоугольник с большИм количеством вершин. Всё дело в том, что у конструктора класса CircleShape есть еще и второй параметр (помимо радиуса), который отвечает за количество вершин у создаваемой фигуры, и он по умолчанию равен 30
. Именно при значениях близких к 30, многоугольник становится мало отличимым от круга. В то же время, задавая этот параметр самостоятельно, мы можем получить абсолютно другие геометрические элементы. Не трудно догадаться, что 3 вершины — это треугольник, 4 вершины — квадрат, 5 вершин — пятиугольник, ну а 8 вершин — восьмиугольник (октагон).
Ниже приведен полный код матрицы программы, которая наглядно покажет создание данных фигур:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include <SFML/Graphics.hpp> using namespace sf; // подключаем пространство имен sf int main() { // Объект, который, собственно, является главным окном приложения RenderWindow window(VideoMode(500, 200), "SFML Works!"); // Главный цикл приложения: выполняется, пока открыто окно while (window.isOpen()) { // Обрабатываем очередь событий в цикле Event event; while (window.pollEvent(event)) { // Пользователь нажал на «крестик» и хочет закрыть окно? if (event.type == Event::Closed) window.close(); // тогда закрываем его } // Установка цвета фона window.clear(Color(250, 220, 100, 0)); // Создаем круг радиусом 50 CircleShape circle(50.f); circle.setFillColor(Color(230, 0, 230)); // закрашиваем наш круг circle.setOutlineThickness(15.f); // устанавливаем толщину контура круга circle.setOutlineColor(Color(80,220,50)); // устанавливаем цвет контура circle.move(15, 15); // перемещаем наш круг для корректного отображения в окне window.draw(circle); // отрисовка круга // Создаем треугольник CircleShape triangle(65.f, 3); triangle.setPosition(125, 0); // устанавливаем начальную позицию справа от круга triangle.setFillColor(Color::Blue); // устанавливаем цвет треугольника - синий window.draw(triangle); // отрисовка треугольника // Создаем квадрат CircleShape square(60.f, 4); square.setPosition(250, 0); // устанавливаем начальную позицию справа от треугольника square.setFillColor(Color::Red); // устанавливаем цвет квадрата - красный window.draw(square); // отрисовка квадрата // Создаем октагон CircleShape octagon(60.f, 8); octagon.setPosition(380, 0); // устанавливаем начальную позицию справа от квадрата octagon.setFillColor(Color::Cyan); // устанавливаем цвет октагона - бирюзовый window.draw(octagon); // отрисовка октагона // Отрисовка окна window.display(); } return 0; } |
Результат выполнения программы:
Выпуклые многоугольники
Теперь рассмотрим создание выпуклого многоугольника через координаты его вершин. Для работы с такими фигурами в SFML имеется класс ConvexShape, который содержит методы setPointCount() и setPoint(). Метод setPointCount() устанавливает количество вершин будущего многоугольника, а метод setPoint() устанавливает сами вершины, принимая в качестве параметров порядковый номер (индекс) создаваемой вершины и её координаты:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// Объект, который, собственно, является главным окном приложения RenderWindow window(VideoMode(500, 300), "SFML Works!"); // … - здесь предыдущий код // Отрисовка октагона window.draw(octagon); // Заготовка фигуры многоугольника ConvexShape convex; // Устанавливаем ему 5 вершин convex.setPointCount(5); // Устанавливаем координаты вершин convex.setPoint(0, Vector2f(0.f, 0.f)); convex.setPoint(1, Vector2f(150.f, 10.f)); convex.setPoint(2, Vector2f(120.f, 90.f)); convex.setPoint(3, Vector2f(30.f, 100.f)); convex.setPoint(4, Vector2f(5.f, 50.f)); // Устанавливаем цвет многоугольника - черный convex.setFillColor(Color::Black); // Теперь сдвинем его вниз и чуть-чуть вправо convex.move(1, 150); // Отрисовка многоугольника window.draw(convex); // Отрисовка окна window.display(); // … - здесь весь остальной код |
Результат выполнения программы:
Обратите внимание, координаты вершин задаются в виде контейнера Vector2f(a, b)
. Данный контейнер — это простой шаблонный класс библиотеки SFML.
Примечание: Порядок, в котором вы будете определять точки многоугольника, очень важен. Все они должны быть определены либо по часовой стрелке, либо против часовой стрелки. Если вы будете определять их в несогласованном порядке, то форма многоугольника будет построена неправильно.
Прямоугольники
Для работы с прямоугольниками используется класс RectangleShape, параметрами которого являются геометрические размеры фигуры (ширина и высота). Здесь всё просто:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// … - здесь предыдущий код // Отрисовка многоугольника window.draw(convex); // Создаем прямоугольник размером 70х100 RectangleShape rectangle(Vector2f(70.f, 100.f)); // Перемещаем его в нижний ряд справа от многоугольника rectangle.move(165, 150); // Устанавливаем ему цвет rectangle.setFillColor(Color(175, 180, 240)); // Отрисовка прямоугольника window.draw(rectangle); // Отрисовка окна window.display(); // … - здесь весь остальной код |
Результат выполнения программы:
Линии
Для рисования линий в SFML есть класс, который называется… называется… а никак он не называется, потому что специализированного класса для работы с линиями в SFML нет. Но если так подумать: «А нужен ли он вообще?» Ведь линии по своей сути — это те же прямоугольники, только не такие широкие, поэтому вполне логично, что мы можем использовать уже знакомый нам класс RectangleShape.
Сейчас мы создадим линию с заданной толщиной:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// … - здесь предыдущий код // Отрисовка прямоугольника window.draw(rectangle); // Линия с заданной толщиной RectangleShape line_with_thickness(Vector2f(130.f, 5.f)); // Поворачиваем её на 45 градусов line_with_thickness.rotate(45.f); // Устанавливаем цвет line_with_thickness.setFillColor(Color(15, 180, 140)); // Перемещаем её в нижний ряд справа от прямоугольника line_with_thickness.move(250, 150); // Отрисовка линии window.draw(line_with_thickness); // Отрисовка окна window.display(); // … - здесь весь остальной код |
Результат выполнения программы:
А теперь линия с нулевой толщиной:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// … - здесь предыдущий код // Отрисовка прямоугольника window.draw(rectangle); // Линия с заданной толщиной RectangleShape line_with_thickness(Vector2f(130.f, 5.f)); // Поворачиваем её на 45 градусов line_with_thickness.rotate(45.f); // Устанавливаем цвет line_with_thickness.setFillColor(Color(15, 180, 140)); // Перемещаем линию в нижний ряд справа от прямоугольника line_with_thickness.move(250, 150); // Отрисовка линии window.draw(line_with_thickness); // Линия с нулевой толщиной. Создаём её в качестве массива вершин типа Vertex Vertex line_without_thickness[] = { // Координата первой вершины Vertex(Vector2f(390.f, 240.f)), // Координата второй вершины Vertex(Vector2f(470.f, 150.f)) }; // Устанавливаем цвет линии - черный line_without_thickness->color = Color::Black; // Отрисовка линии window.draw(line_without_thickness, 2, Lines); // Отрисовка окна window.display(); // … - здесь весь остальной код |
Результат выполнения программы:
Сглаживание
Заметили, какие края у черного многоугольника? Как нет?!?! Да его краями можно деревья пилить! Чтобы избавиться от этого дефекта, нужно включить сглаживание, которое сделает нашу картинку намного более приятной глазу. Сразу отмечу, что в SFML нет возможности включить сглаживание (или «антиалиасинг», от англ. «anti-aliasing») для какой-то одной фигуры. Оно действует глобально для всего окна. При этом учитывайте, что возможность использовать сглаживание зависит от характеристик вашей видеокарты и параметров видеодрайвера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
#include <SFML/Graphics.hpp> using namespace sf; // подключаем пространство имен sf int main() { // Устанавливаем 8-й уровень сглаживания ContextSettings settings; settings.antialiasingLevel = 8; // Объект, который, собственно, является главным окном приложения RenderWindow window(VideoMode(500, 300), "SFML Works!", Style::Default, settings); // Главный цикл приложения: выполняется, пока открыто окно while (window.isOpen()) { // Обрабатываем очередь событий в цикле Event event; while (window.pollEvent(event)) { // Пользователь нажал на «крестик» и хочет закрыть окно? if (event.type == Event::Closed) window.close(); // тогда закрываем его } // Установка цвета фона window.clear(Color(250, 220, 100, 0)); // Устанавливаем круг радиусом 50 CircleShape circle(50.f); circle.setFillColor(Color(230, 0, 230)); // закрашиваем наш круг circle.setOutlineThickness(15.f); // устанавливаем толщину контура круга circle.setOutlineColor(Color(80, 220, 50)); // устанавливаем цвет контура circle.move(15, 15); // перемещаем круг для корректного отображения в окне window.draw(circle); // отрисовка круга // Создаем треугольник CircleShape triangle(65.f, 3); triangle.setPosition(125, 0); // устанавливаем начальную позицию справа от круга triangle.setFillColor(Color::Blue); // устанавливаем цвет треугольника - синий window.draw(triangle); // отрисовка треугольника // Создаем квадрат CircleShape square(60.f, 4); square.setPosition(250, 0); // устанавливаем начальную позицию справа от треугольника square.setFillColor(Color::Red); // устанавливаем цвет квадрата - красный window.draw(square); // отрисовка квадрата // Создаем октагон CircleShape octagon(60.f, 8); octagon.setPosition(380, 0); // устанавливаем начальную позицию справа от квадрата octagon.setFillColor(Color::Cyan); // устанавливаем цвет октагона - бирюзовый window.draw(octagon); // отрисовка октагона // Заготовка фигуры многоугольника ConvexShape convex; convex.setPointCount(5); // устанавливаем ему 5 вершин // Устанавливаем координаты вершин convex.setPoint(0, Vector2f(0.f, 0.f)); convex.setPoint(1, Vector2f(150.f, 10.f)); convex.setPoint(2, Vector2f(120.f, 90.f)); convex.setPoint(3, Vector2f(30.f, 100.f)); convex.setPoint(4, Vector2f(5.f, 50.f)); convex.setFillColor(Color::Black); // устанавливаем цвет многоугольника - черный convex.move(1, 150); // теперь сдвинем его вниз и чуть-чуть вправо window.draw(convex); // отрисовка многоугольника // Создаем прямоугольник размером 70х100 RectangleShape rectangle(Vector2f(70.f, 100.f)); rectangle.move(165, 150); // перемещаем его в нижний ряд справа от многоугольника rectangle.setFillColor(Color(175, 180, 240)); // устанавливаем цвет прямоугольника window.draw(rectangle); // отрисовка прямоугольника // Линия с заданной толщиной RectangleShape line_with_thickness(Vector2f(130.f, 5.f)); line_with_thickness.rotate(45.f); // поворачиваем её на 45 градусов line_with_thickness.setFillColor(Color(15, 180, 140)); // устанавливаем цвет линии line_with_thickness.move(250, 150); // перемещаем её в нижний ряд справа от прямоугольника window.draw(line_with_thickness); // отрисовка линии // Линия с нулевой толщиной. Создаем её в качестве массива вершин типа Vertex Vertex line_without_thickness[] = { Vertex(Vector2f(390.f, 240.f)), // координата первой вершины Vertex(Vector2f(470.f, 150.f)) // координата второй вершины }; line_without_thickness->color = Color::Black; // устанавливаем цвет линии - черный window.draw(line_without_thickness, 2, Lines); // отрисовка линии // Отрисовка окна window.display(); } return 0; } |
Результат выполнения программы:
А теперь гифка вам в ленту (смотрите на грани фигур — как было и как стало):
Заключение
-I know Kung Fu!
Вот мы и подошли к концу данной статьи. В ней мы рассмотрели создание базовых геометрических фигур с помощью библиотеки SFML. Узнали, как создаются простые линии и даже немного поигрались со сглаживанием. Надеюсь, что вам понравилось.
Теперь ты понял, насколько глубока кроличья нора, впереди нас ждет еще очень много интересного… KNOCK KNOCK NEO.
GitHub / Создание простых геометрических фигур в C++/SFML — Исходный код
"В SFML не реализована возможность рисования линий".
А как же метод VertexArray? Передаем туда примитив sf::Lines и задаем кол-во вершин 2. Получается VertexArray name(sf::Lines, 2);
Здравствуйте, вопрос не совсем по теме урока, можете подсказать что означает следующая запись "Color::Cyan". Пытался гуглить, но ответа не нашел. "Cyan" это у нас статическая переменная класса "Color", я так понял и через двойное двоеточие мы получаем ее значение? Если можно поясните кто нибудь.
Да, Вы всё верно поняли, это одна из статических константных переменных цвета, определённых в Color.hpp. Можете посмотреть подробно их в репозитории SFML на Github, ссылка
есть на оф.сайте библиотеки
"Для рисования линий в SFML есть класс, который называется… называется… а никак он не называется" -, прошу прощения, но он есть, это sf::Lines) В оф туториале есть отличная статья про примитивные типы sfml : https://www.sfml-dev.org/tutorials/2.5/graphics-vertex-array.php
Здравствуйте, не совсем понятна конструкция из циклов, точнее назначение второго цикла. Если ясно что первый while бесконечно выполняется и отрисовывает заново каждый раз все фигуры (поправьте, если не так), пока окно не закроется, то со вторым циклом не понятно ничего — что за очередь событий в цикле? Прогнал дебагером — он работает вроде рандомное количество раз даже когда никаких event'ов не происходит, почему мы просто не можем сделать проверку на закрытие без цикла?
p.s. появился вопрос про отрисувку окна методом display(). В том же самом отладчике увидел что окно рисуется не после применения метода, а после нового захода на цикл.
Заранее спасибо!
Ответ — события копятся в очереди, а pollEvent забирает следующее событие, и если оно есть, возвращает true.
С помощью внутреннего цикла мы гарантированно обрабатываем ВСЕ накопленные события за один цикл работы программы и не засоряем буфер.
А обязательно свойства фигур запихивать именно в цикл?
Нет, не обязательно. Их можно определить и вне цикла. 🙂
Здравствуйте, спасибо за статью, очень помогла освоиться с этой библиотекой:) Такой вопрос: если мне нужно нарисовать 5 звезд, которые отличаются только местоположением и размером, то я должен каждый раз создавать новый ConvexShape или это можно сделать по другому?
Добрый день.
>>…то я должен каждый раз создавать новый ConvexShape…?
Да. Другого способа я не знаю. 🙂
Я когда создавал подсветку краёв поля (т.е. когда мышь подводишь к краю окна и поле сдвигается), я обошёлся 2 локальными прямоугольниками, что меняли своё местоположение и просто рисовались по очереди. А однажды столкнулся с проблемой не поддержки некоторых видеочипов, из-за которых все текстуры становились белыми. Т.е. создать массив пикселей и вывести было невозможно (только второе), экран был белым. Я сделал так, рисовал градиентные линии по оси х на весь экран ну и повторял по y до конца 🙂 мне нужно было то 2-10 кадров в секунду, а так даже 40 получилось (я знаю, что это извращение)
P.S. я делал 3d движки на этой библиотеке (2 разных типов)
Но вызывать отрисовку для каждого объекта по отдельности накладно.
Есть кое-что получше: поиск + virtual void draw(sf::RenderTarget &target, sf::RenderStates states) const;
Если они не будут отличаться размером то просто цикл в котором создаются одни и те же обьекты но каждый раз растояние допустим на 10 пикселей в право. Если они будут отличаться размером и каждая больше другой на 10 пикселей, то вы сами наверно понимаете что можно тоже сделать цикл, рисующий эти звёзды. Иначе сами.
Интересно. На экране какие-то живые объекты появились — не то, что эти угрюмые цифры и буквы.
Но вопрос. Вот мы нашли эту чудо-библиотеку.
А как понять, какие там существуют объекты и методы? И вообще с чего начинать?
откуда CircleShape, window.draw(rectangle), как мы должны догадаться, что нужно еще и window.display()? Где эта информация находится?
Как мы должны понять, что нужен именно цикл по обработчикам?
"// Обрабатываем очередь событий в цикле"
Как правило, подобную информацию можно найти сайте разработчиков библиотеки.
В частности, документацию по SFML можно найти здесь:
Documentation of SFML 2.5.1
https://www.sfml-dev.org/documentation/2.5.1/annotated.php
А некоторые базовые примеры использования библиотеки — здесь:
https://www.sfml-dev.org/tutorials/2.5/
🙂