Мультимедийная библиотека SFML лечит головную боль не только новичкам в игровой индустрии, но и преподавателям информатики в школе. Основная задача которых состоит в том, чтобы не только научить ребёнка программировать, но и привить любовь к разработке приложений. Используя простые графические объекты и их методы можно за короткое время создать прототип игры или симулятор физико-математических процессов. В этой статье мы рассмотрим разработку аналоговых часов на C++ используя библиотеку SFML.
Инструменты для разработки
Visual Studio c установленным пакетом "Разработка классических приложений на С++".
Подключаемый файл заголовка SFMLWorldTime.
Шрифты и программный код для самостоятельной разработки или репозиторий.
Подготовительная работа
Подключаем к Visual Studio библиотеку SFML и копируем шаблон кода библиотеки SFML в редактор кода.
#include <SFML/Graphics.hpp>
#include"SFMLWorldTime.h"
using namespace sf;
int main()
{
// Создаём графическое окно размером 900х900
RenderWindow window(VideoMode(900, 900), L"Аналоговые часы", Style::Default);
window.setVerticalSyncEnabled(true); // Вертикальная синхронизация
SFMLWorldTime etm(50, 50, 4, Color::Yellow); // Объект электронные часы
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
}
window.clear(Color::Blue); // Очищаем графическое окно и закрашиваем в синий цвет
etm.drawTime(window); // Рисуем электронные часы в графическом окне
window.display();
}
return 0;
}
Добавляем в папку проекта файлы SFMLWorldTime.h и SFMLWorldTime.cpp и папку со шрифтами lib.
Добавляем в проект файлы SFMLWorldTime.h и SFMLWorldTime.cpp.
В код шаблона программы прописываем вызов заголовочного файла SFMLWorldTime.h.
#include"SFMLWorldTime.h"
Создаём объект электронные часы и рисуем их в графическом окне.
SFMLWorldTime etm(50, 50, 4, Color::Yellow);
etm.drawTime(window);
Часовых дел мастер
#define _USE_MATH_DEFINES
#include <SFML/Graphics.hpp>
#include"SFMLWorldTime.h"
#include<math.h>
using namespace sf;
using namespace std;
int main()
{
RenderWindow window(VideoMode(900, 900), L"Аналоговые часы", Style::Default);
window.setVerticalSyncEnabled(true);
SFMLWorldTime etm(50, 50, 4, Color::Yellow);
// Корпус аналоговых часов
CircleShape circleTime(300.f);
circleTime.setOrigin(150, 150);
circleTime.setPosition(303, 303);
circleTime.setFillColor(Color::Blue);
circleTime.setOutlineThickness(10);
circleTime.setOutlineColor(Color::Yellow);
// Риски аналоговых часов объявление переменных
CircleShape PointMin;
PointMin.setFillColor(Color::Yellow);
float radiusNum = 280; // радиус расположения рисок
float radiusPoint;
float CenterClockX = 450;
float CenterClockY = 450;
float xPoint, yPoint;
// Оцифровка циферблата аналоговых часов объявление переменных
Font fontTime;
if (!fontTime.loadFromFile("lib/dockerthree.ttf")) return 777;
Text TimeText;
TimeText.setFont(fontTime);
TimeText.setCharacterSize(30);
TimeText.setFillColor(Color::Yellow);
float numx, numy;
// Рисуем стрелки аналоговых часов
RectangleShape secArrow(Vector2f(2, 280)); //секундная
InitRect(secArrow, 453, 453, 1, 280, Color::Red);
RectangleShape minArrow(Vector2f(8, 260)); //минутная
InitRect(minArrow, 455, 455, 4, 260, Color::Yellow);
RectangleShape hourArrow(Vector2f(12, 180)); //часовая
InitRect(hourArrow, 455, 455, 6, 180, Color::Yellow);
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
}
window.clear(Color::Blue);
etm.drawTime(window); // электронные часы
window.draw(circleTime); // корпус аналоговых часов
// Риски аналоговых часов
for (int a = 0; a < 60; a++) {
if (a % 5 == 0) radiusPoint = 8; else radiusPoint = 4;
xPoint = CenterClockX + radiusNum * cos(-6 * a * (M_PI / 180) + M_PI / 2);
yPoint = CenterClockY - radiusNum * sin(-6 * a * (M_PI / 180) + M_PI / 2);
PointMin.setRadius(radiusPoint);
PointMin.setOrigin(radiusPoint / 2, radiusPoint / 2);
PointMin.setPosition(xPoint, yPoint);
window.draw(PointMin);
}
// Оцифровка циферблата аналоговых часов
for (int i = 1; i <= 12; i++)
{
numx = CenterClockX + (radiusNum - 30) * cos(-30 * i * (M_PI / 180) + M_PI / 2);
numy = CenterClockX - (radiusNum - 30) * sin(-30 * i * (M_PI / 180) + M_PI / 2);
if (i <= 5) TimeText.setPosition(numx - 10, numy - 17);
else TimeText.setPosition(numx - 8, numy - 15);
TimeText.setString(to_string(i));
window.draw(TimeText);
}
secArrow.setRotation(6 * etm.getsec()); // вращение секундной стрелки
minArrow.setRotation(6 * etm.getmin() + etm.getsec() * 0.1); // вращение минутной стрелки
hourArrow.setRotation(30 * etm.gethour() + etm.getmin() * 0.5); // вращение часовой стрелки
window.draw(hourArrow); // часовая стрелка
window.draw(minArrow); // минутная стрелка
window.draw(secArrow); // секундная
window.display();
}
return 0;
}
Рисуем окружность т.е. контур будущих часов
// Блок объявления и установки параметров объектов
CircleShape circleTime(300.f);
circleTime.setOrigin(150, 150);
circleTime.setPosition(303, 303);
circleTime.setFillColor(Color::Blue);
circleTime.setOutlineThickness(10);
circleTime.setOutlineColor(Color::Yellow);
// Блок отрисовки графических объектов
window.draw(circleTime);
Подключаем макрос математических констант
#define _USE_MATH_DEFINES
Подключаем заголовочный файл математических функций
#include<math.h>
Объявляем объекты и переменные для рисования рисок часов.
Создаём объект круга, который и будет отображать окружность в виде рисок.
// Блок объявления и установки параметров объектов
CircleShape PointMin; // объект круга
PointMin.setFillColor(Color::Yellow); // цвет круга жёлтый
Объявим и инициализируем вещественную переменную значением расстояния от центра часов до расположения рисок.
float radiusNum = 280;
Создадим вещественную переменную размера рисок т.е. радиуса объекта круга PointMin
float radiusPoint;
Обозначим переменными координаты центра окружности
float CenterClockX = 450;
float CenterClockY = 450;
Объявим переменные координат рисок
float xPoint, yPoint;
Рисуем риски в графическом окне
for (int a = 0; a < 60; a++)
{
// каждой пятой риски увеличиваем радиус
if (a % 5 == 0) radiusPoint = 8; else radiusPoint = 4;
// вычисляем координаты риски
xPoint = CenterClockX + radiusNum * cos(-6 * a * (M_PI / 180) + M_PI / 2);
yPoint = CenterClockY - radiusNum * sin(-6 * a * (M_PI / 180) + M_PI / 2);
// задаём координаты и параметры риски т.е. объекту круга PointMin
PointMin.setRadius(radiusPoint);
PointMin.setOrigin(radiusPoint / 2, radiusPoint / 2);
PointMin.setPosition(xPoint, yPoint);
// рисуем риску в графическом окне
window.draw(PointMin);
}
В формуле вычисления координат рисок, используем математическую константу числа Пи - M_PI.
Объявим объекты и переменные для отображения циферблата часов
Загрузим шрифт для отображения цифр, создаём объект шрифта fontTime и загружаем в него шрифт dockerthree.ttf
Font fontTime;
if (!fontTime.loadFromFile("lib/dockerthree.ttf")) return 777;
Создаём объект текста и задаём ему параметры: шрифта, размера и цвета.
Text TimeText;
TimeText.setFont(fontTime);
TimeText.setCharacterSize(30);
TimeText.setFillColor(Color::Yellow);
Объявляем переменные координат текстового объекта, для отображения цифр.
float numx, numy;
Рисуем циферблат в графическом окне
for (int i = 1; i <= 12; i++)
{
// вычисляем координаты для текстового объекта с цифрами
numx = CenterClockX + (radiusNum - 30) * cos(-30 * i * (M_PI / 180) + M_PI / 2);
numy = CenterClockX - (radiusNum - 30) * sin(-30 * i * (M_PI / 180) + M_PI / 2);
// Корректируем координаты расположения цифр
if (i <= 5) TimeText.setPosition(numx - 10, numy - 17);
else TimeText.setPosition(numx - 8, numy - 15);
// заносим значение цифры в текстовый объект
TimeText.setString(to_string(i));
// рисуем текстовый объект
window.draw(TimeText);
}
Объявляем объекты для стрелок часов и настраиваем их.
RectangleShape secArrow(Vector2f(2, 280)); // Секундная стрелка
InitRect(secArrow, 453, 453, 1, 280, Color::Red);
RectangleShape minArrow(Vector2f(8, 260)); // Минутная стрелка
InitRect(minArrow, 455, 455, 4, 260, Color::Yellow);
RectangleShape hourArrow(Vector2f(12, 180)); // Часовая стрелка
InitRect(hourArrow, 455, 455, 6, 180, Color::Yellow);
Графическое отображение стрелок базируются на объекте формы прямоугольника RectangleShape. Настраиваем объекты с помощью функции InitRect (), которая находится в заголовочном файле SFMLWorldTime.h.
Рисуем стрелки часов
// Изменение угла положения прямоугольников т.е. стрелок
secArrow.setRotation(6 * etm.getsec()); // Вращение секундной стрелки
minArrow.setRotation(6 * etm.getmin() + etm.getsec() * 0.1); // Вращение минутной стрелки
hourArrow.setRotation(30 * etm.gethour() + etm.getmin() * 0.5); // Вращение часовой стрелки
// Рисование стрелок в графическом окне
window.draw(hourArrow); // часовая стрелка
window.draw(minArrow); // минутная стрелка
window.draw(secArrow); // секундная
Метод setRotation вращает объект вокруг заданной оси, согласно установленному углу поворота в параметрах метода.
Рассчитаем угол поворота стрелок.
Секундная стрелка повернется на угол равный 360 °/60 сек = 6 ° (вся окружность 360 ° по ней распределяем 60 секунд) и умножаем на текущее значение времени секунд.
Минутная стрелка за это время повернется на 360 °/60 мин = 6 ° умноженных на текущее значение времени минут и добавляем 6 °/60сек=0.1 ° умноженный на количество пройденного времени секунд.
Часовая стрелка за это время повернётся на 360°/12 часов = 30 ° умноженных на текущее время часов и добавляем 30 °/60мин=0,5° умноженным на пройденное время минут.
Более подробную инструкцию вы можете получить посмотрев видео "Аналоговые часы SFML C++".
Комментарии (4)
Alex_v99
23.12.2022 14:26А можно дурацкий вопрос?..
С интересом читаю статьи из этого цикла, но до сих пор не понял, чем SFML лучше чем тот же GDI+. Или его преимущества появятся в следующих статьях - в более сложных случаях?
JordanCpp
23.12.2022 18:17SFML это кроссплатформенный продукт, GDI+ нет. Это серьезный недостаток. Когда автор осилит CMake, пользователи с других ос, смогут нормально собирать примеры из статей.
JordanCpp
23.12.2022 22:19Извиняюсь за дезинформацию, ссылка на репозиторий, в конце статьи. При первом чтении пропустил её. Cmake на месте.
wellhause
Хорошая статья, +1