Вариантов отобразить электронные часы на языке программирования С++ очень множество, в данной статье рассмотрим электронные часы для 2D игр написанных используя мультимедийную библиотеку SFML. Для удобства дальнейшего внедрения данного кода в наши проекты создадим класс электронные часы, в котором реализуем возможность менять шрифт, размер и цвет электронных часов.
Для написания кода нам понадобится IDE Visual Studio, С++ проект с настроенной библиотекой SFML, шаблон кода SFML, иконки и шрифты.
Шаблон кода SFML C++
#include <SFML/Graphics.hpp>
using namespace sf;
int main()
{
// Графическое окно размером 900х900 с названием "Часы"
RenderWindow window(VideoMode(900, 900), L"Часы", Style::Default);
// Иконка графического окна
Image icon;
if (!icon.loadFromFile("Image/clock.png")) //Загрузка иконки
{
return 3; // Возвращает 3 если иконка не загрузилась
}
window.setIcon(128, 128, icon.getPixelsPtr()); // Ссылка на иконку
// Рабочий цикл графического окна
while (window.isOpen())
{
// Переменная для хранения события
Event event;
// Цикл обработки событий
while (window.pollEvent(event))
{
// Обработка события нажатия на крестик графического окна
if (event.type == Event::Closed) window.close();
}
// Очистка графического окна, с окраской фона в синий цвет
window.clear(Color::Blue);
// Вывод графики в графическое окно
window.display();
}
return 0;
}
Если Вы с первой часть справились успешно, тогда можем добавить к нашему проекту новый класс назовём его SFMLWorldTime.
Объявление закрытых свойств класса SFMLWorldTime, файл SFMLWorldTime.h
#pragma once
#include <SFML/Graphics.hpp>
class SFMLWorldTime
{
struct tm newtime;
__time64_t long_time;
errno_t err;
int position_x, position_y;
int font_size;
sf::Font time_font;
sf::Color time_color;
sf::Text clock_text;
};
newtime содержит необходимые поля для отображения времени: часы, минуты и секунды. long_time - переменная системного времени. err - сохраняет код ошибки при неверном подключении времени.
position_x, position_y - координаты электронных часов.
font_size - размер шрифта электронных часов.
time_font - хранит шрифт электронных часов. Переменная time_color хранит цвет электронных часов. clock_text - текстовый объект, который рисует элементы электронных часов в графическом окне.
Объявление закрытых методов класса SFMLWorldTime, файл SFMLWorldTime.h
void InitText();
void what_time();
Метод InitText устанавливает настройки объекта clock_text.
Метод what_time записывает текущее системное время в структуру newtime.
Определение закрытых методов класса SFMLWorldTime, файл SFMLWorldTime.cpp
void SFMLWorldTime::InitText()
{
clock_text.setFont(time_font); // тип шрифта
clock_text.setCharacterSize(font_size); // размер шрифта
clock_text.setFillColor(time_color); // цвет шрифта
}
void SFMLWorldTime::what_time()
{
_time64(&long_time); // получаем системное время
// записываем системное время в структуру newtime
err = _localtime64_s(&newtime, &long_time);
if (err) exit(23); // при возникновении ошибки выходим из программы и возвращаем код ошибки 23
}
Объявление конструктора класса SFMLWorldTime, файл SFMLWorldTime.h
public:
SFMLWorldTime(int x = 50, int y = 50, int size = 3, sf::Color mycolor = sf::Color::White, std::string font = "lib/Электро.ttf");
Определение конструктора класса SFMLWorldTime, файл SFMLWorldTime.cpp
SFMLWorldTime::SFMLWorldTime(int x, int y, int size, sf::Color mycolor, std::string font)
{
(size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
position_x = x;
position_y = y;
if (!time_font.loadFromFile(font)) exit(13);
time_color = mycolor;
font_size = size * 10;
InitText();
}
Объявление открытых методов класса SFMLWorldTime, файл SFMLWorldTime.h
void setposition(int x, int y);
void setcolor(sf::Color color);
void setTime_size(int size);
int getsec();
int getmin();
int gethour();
void drawTime(sf::RenderWindow& window);
Методы: setposition, setcolor, setTime_size устанавливаю свойства электронных часов, координаты, цвет и размер.
Методы: getsec, getmin, gethour, возвращаю целочисленное значение системного времени секунды, минуты, часы.
Метод drawTime рисует электронные часы в графическом окне. В параметрах передаётся объект графическое окно.
Определение открытых методов класса SFMLWorldTime, файл SFMLWorldTime.cpp
void SFMLWorldTime::setposition(int x, int y)
{
position_x = x;
position_y = y;
}
void SFMLWorldTime::setcolor(sf::Color color)
{
time_color = color;
InitText();
}
void SFMLWorldTime::setTime_size(int size)
{
(size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
font_size = size * 10;
InitText();
}
int SFMLWorldTime::getsec()
{
what_time(); // обновление свойств структуры newtime
return newtime.tm_sec;
}
int SFMLWorldTime::getmin()
{
what_time();
return newtime.tm_min;
}
int SFMLWorldTime::gethour()
{
what_time();
return newtime.tm_hour;
}
Определение метода drawTime
void SFMLWorldTime::drawTime(sf::RenderWindow& window)
{
what_time();
std::string tmpstr;
if (newtime.tm_hour < 10)
{
tmpstr = "0";
tmpstr.append(std::to_string(newtime.tm_hour));
}
else tmpstr = std::to_string(newtime.tm_hour);
tmpstr.append(":");
if (newtime.tm_min < 10) tmpstr.append("0");
tmpstr.append(std::to_string(newtime.tm_min));
tmpstr.append(":");
if (newtime.tm_sec < 10) tmpstr.append("0");
tmpstr.append(std::to_string(newtime.tm_sec));
clock_text.setPosition(position_x, position_y);
clock_text.setString(tmpstr);
window.draw(clock_text);
}
В методе drawTime проверяется условие для отображения элементов электронных часов, если элемент, например секунды меньше 10, тогда к нему добавляется ноль, который устанавливается перед секундами. Все элементы электронных часов склеиваются в одну строку с помощью метода append() и сохраняются в переменную tmpstr. Функция to_string() переводит целое значение в строковое.
После формирования строки с электронными часами, она выводится в графическое окно с помощью объекта window и метода draw().
Объект электронные часы SFMLWorldTime.
В функции main объявляем тип SFMLWorldTime (электронные часы) и с помощью цикла for выводим в графическое окно 10 электронных часов разных размеров.
#include "SFMLWorldTime.h"
using namespace sf;
int main()
{
RenderWindow window(VideoMode(900, 900), L"Часы", Style::Default);
Image icon;
if (!icon.loadFromFile("Image/clock.png"))
{
return 3;
}
window.setIcon(128, 128, icon.getPixelsPtr());
SFMLWorldTime mytime(50,50,4);
mytime.setcolor(Color::Yellow);
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed) window.close();
}
window.clear(Color::Blue);
for (int i = 10, kof = 0, kof2 = 20, posy = 50, posx = 200; i > 0; i--,
kof += 10, posy += 130 - kof, kof -= 2, posx += kof2)
{
mytime.setTime_size(i);
mytime.setposition(posx, posy);
mytime.drawTime(window);
}
window.display();
}
return 0;
}
Полный код файла SFMLWorldTime.h
#pragma once
#include <SFML/Graphics.hpp>
class SFMLWorldTime
{
struct tm newtime;
__time64_t long_time;
errno_t err;
int position_x, position_y;
int font_size;
sf::Font time_font;
sf::Color time_color;
sf::Text clock_text;
void InitText();
void what_time();
public:
SFMLWorldTime(int x = 50, int y = 50, int size = 3, sf::Color mycolor = sf::Color::White, std::string font = "lib/Электро.ttf");
void setposition(int x, int y);
void setcolor(sf::Color color);
void setTime_size(int size);
int getsec();
int getmin();
int gethour();
void drawTime(sf::RenderWindow& window);
};
Полный код файла SFMLWorldTime.cpp
#include "SFMLWorldTime.h"
SFMLWorldTime::SFMLWorldTime(int x, int y, int size, sf::Color mycolor, std::string font)
{
(size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
position_x = x;
position_y = y;
if (!time_font.loadFromFile(font)) exit(13);
time_color = mycolor;
font_size = size * 10;
InitText();
}
void SFMLWorldTime::drawTime(sf::RenderWindow& window)
{
what_time();
std::string tmpstr;
if (newtime.tm_hour < 10)
{
tmpstr = "0";
tmpstr.append(std::to_string(newtime.tm_hour));
}
else tmpstr = std::to_string(newtime.tm_hour);
tmpstr.append(":");
if (newtime.tm_min < 10) tmpstr.append("0");
tmpstr.append(std::to_string(newtime.tm_min));
tmpstr.append(":");
if (newtime.tm_sec < 10) tmpstr.append("0");
tmpstr.append(std::to_string(newtime.tm_sec));
clock_text.setPosition(position_x, position_y);
clock_text.setString(tmpstr);
window.draw(clock_text);
}
void SFMLWorldTime::InitText()
{
clock_text.setFont(time_font);
clock_text.setCharacterSize(font_size);
clock_text.setFillColor(time_color);
}
void SFMLWorldTime::what_time()
{
_time64(&long_time);
err = _localtime64_s(&newtime, &long_time);
if (err) exit(23);
}
void SFMLWorldTime::setposition(int x, int y)
{
position_x = x;
position_y = y;
}
void SFMLWorldTime::setcolor(sf::Color color)
{
time_color = color;
InitText();
}
void SFMLWorldTime::setTime_size(int size)
{
(size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
font_size = size * 10;
InitText();
}
int SFMLWorldTime::getsec()
{
what_time();
return newtime.tm_sec;
}
int SFMLWorldTime::getmin()
{
what_time();
return newtime.tm_min;
}
int SFMLWorldTime::gethour()
{
what_time();
return newtime.tm_hour;
}
Более подробную инструкцию вы можете получить посмотрев видео "Электронные часы SFML C++".
Комментарии (20)
JordanCpp
12.12.2022 23:44Метод SFMLWorldTime::drawTime тяжело читается. Лучше вынести рисование в отдельный метод с установкой цвета, положения и т. д drawTime(int sec, int minutes, int hours) А в самом drawTime 'е вызывать метод drawDigit(int x, int y, color)
Ещё смущает частое дерганье clock_text.setString и window.draw(clock_text).
В идеале нужно сначала сформировать строку часов минут секунд и потом уже одним вызовом отрисовать.
И делать window.draw(clock_text) один раз в конце цикла событий.
Tujh
14.12.2022 09:55+1Недостаток статьи - используемый стандарт языка на уровне С++03. Многое было бы проще с использованием современного С++.
На вскидку - std::chrono, to_string() вместо ostingstream и т.д.
Tujh
14.12.2022 09:59+1Ещё замечание, никогда не пишите это в заголовочных файлах:
using namespace sf; using namespace std;
это считается очень дурным стилем.
yatanai
14.12.2022 12:03А внутри методов-функций можно? ????
Tujh
14.12.2022 17:43В примере using размещено в самом начале файла, а не в методах.
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rs-using-directive
Да и, опять же, сложные методы, требующие using в теле, я бы тоже перенёс в cpp/cxx файл, а для коротких - четыре-пять лишних символов не большая помеха. И в целом, хорошим тоном, даже для методов, считается вкладывать не всё пространство имён, а только нужную часть.
JordanCpp
14.12.2022 19:54Я бы убрал из класса SFMLWorldTime "struct tm newtime" и перенес работу с датой в класс который бы возвращал время в секундах, минутах и часах. SFMLWorldTime переименовал в SFMLTimePainter. SFMLWorldTime может возвращать время по Now
Вот пример.
https://github.com/JordanCpp/Lt/blob/master/src/Lt/Core/DateTime.cpp
Оборачиваете struct tm в DateTime
Это все на ваше усмотрение. Я понимаю, что это всего лишь пример. Может и не стоит настолько мелко разбивать задачу.
Tujh
14.12.2022 21:55+1Оборачиваете struct tm в DateTime
Но зачем если есть std::chrono?
Потребуется С++20 или внешняя библиотека для предыдущих стандартов
#include <chrono> int main() { using namespace std::chrono; // Get a local time_point with system_clock::duration precision auto now = zoned_time{current_zone(), system_clock::now()}.get_local_time(); // Get a local time_point with days precision auto ld = floor<days>(now); // Convert local days-precision time_point to a local {y, m, d} calendar year_month_day ymd{ld}; // Split time since local midnight into {h, m, s, subseconds} hh_mm_ss hms{now - ld}; // This part not recommended. Stay within the chrono type system. int year{ymd.year()}; int month = unsigned{ymd.month()}; int day = unsigned{ymd.day()}; int hour = hms.hours().count(); int minute = hms.minutes().count(); int second = hms.seconds().count(); }
https://stackoverflow.com/questions/61190884/current-time-and-date-in-c20-days
библиотека для С++11/14/17
Troyy Автор
15.12.2022 11:31Попробую С++20
Troyy Автор
15.12.2022 12:52#include <chrono> #include <iostream> #include <thread> int main() { using namespace std::chrono; while (true) { // получаем локальное время auto now = zoned_time{ current_zone(), system_clock::now() }.get_local_time(); // получаем точность локального времени день auto ld = floor<days>(now); // получаем точность суточное время hh_mm_ss hms{ now - ld }; // форматированный вывод времени в консоль часы, минуты, секунды std::cout << "\n\n\n\t\t\t" << hms.hours().count() << ":" << hms.minutes().count() << ":" << hms.seconds().count(); // задержка 500 милисекунд std::this_thread::sleep_for(500ms); // очистка экрана system("cls"); }
}
JordanCpp
У вас много дублированного кода. Вместо создания и копирования методов отличающихся лишь аргументами. Сделайте один метод и пусть он принимает 4-5 параметра. Это методы SFMLWorldTime.
Плюсом будет сделать клас тайм инкапсулирующий функционал time.h и уже данный класс передавать как зависимость в класс SFMLWorldTime. Это разобьёт ваш монолитный класс на два. Что повысит читабельность и упростит отделение рисования времени от вычисления времени.
Привыкайте сразу писать по возможности понятный и читаемый код.
Обязательно продолжайте.
Troyy Автор
Спасибо большое !