Перейти к содержанию

SFML: разработка приложений с графическим интерфейсом#

Материалы к занятию#

  1. Презентация
  2. Шрифт для проекта
  3. Картинка с печеньем

SFML. Установка и настройка в VS Studio#

SFML (Simple and Fast Multimedia Library) — это библиотека, предназначенная для разработки мультимедийных приложений, таких как игры и графические интерфейсы, с использованием языка C++. Данная методика описывает процесс установки и настройки SFML в среде разработки Visual Studio.

Для загрузки файлов библиотеки необходимо перейти на официальный сайт SFML и скачать последнюю стабильную версию библиотеки (https://www.sfml-dev.org/download/sfml/2.6.1/) . На момент написания этого пособия последней версией является SFML 2.6.1. Важно выбрать версию, совместимую с Visual Studio, например, Visual C++ 17 (2022) - 64-bit.

https://ratcatcher.ru/media/inf/pr/pr5/S_1.png

После завершения загрузки архив с библиотекой необходимо распаковать в удобное место на вашем компьютере. Рекомендуется создать папку dependencies в корневой директории вашего проекта и организовать её на подкаталоги для 32 и 64-битных версий. Это облегчит управление библиотеками при работе с проектами различных конфигураций.

В Visual Studio создаётся новый проект типа "Пустой проект". Это позволит начать работу с нуля, без предварительных настроек.

В обозревателе решений необходимо выбрать созданный проект и при помощи нажатия ПКМ добавить новый элемент — файл main.cpp, который будет содержать основной код программы (Обозреватель решений → ПКМ → Добавить → Создать элемент).

https://ratcatcher.ru/media/inf/pr/pr5/S_2.png

Важно обратить внимание на разрядность решения, которая должна соответствовать разрядности скачанной версии SFML. Для 64-битных систем указывается x64, а для 32-битных — x86.
Настройка путей к заголовочным файлам и библиотекам в Visual Studio происходит в несколько этапов:

• Открывается меню Проект -> Свойства.

• В разделе C/C++ -> Общие в поле Дополнительные каталоги включаемых файлов указывается путь к папке include библиотеки SFML.

https://ratcatcher.ru/media/inf/pr/pr5/S_3.png

Чтобы обеспечить переносимость проекта между ЭВМ необходимо описать относительные пути. Для этого в файле проекта должна быть создана директория dependencies внутрь которой следует разархивировать файлы SFML-2.6.1. Если процесс выполнен верно, то в строке «дополнительных каталогов включаемых файлов» указывается следующий путь:

                           $(ProjectDir)dependencies\SFML-2.6.1\include

https://ratcatcher.ru/media/inf/pr/pr5/S_4.png

Чтобы компоновщик смог найти библиотеки SFML, нужно указать путь к соответствующей папке:
• В разделе Компоновщик -> Общие в поле Дополнительные каталоги библиотек прописывается путь к папке lib

Если процесс выполнен верно, то в строке «дополнительных каталогов включаемых файлов» указывается следующий путь:

                            $(ProjectDir)dependencies\SFML-2.6.1\lib

https://ratcatcher.ru/media/inf/pr/pr5/S_5.png

Для правильной линковки файлов необходимо вручную добавить библиотеки:
В разделе Компоновщик -> Ввод -> Дополнительные зависимости добавляются файлы библиотек .lib для каждой конфигурации (Debug или Release).

https://ratcatcher.ru/media/inf/pr/pr5/S_6.png

Для конфигурации Debug добавляются следующие файлы:
- sfml-graphics-d.lib
- sfml-window-d.lib
- sfml-audio-d.lib
- sfml-system-d.lib
- sfml-network-d.lib

https://ratcatcher.ru/media/inf/pr/pr5/S_7.png

После настройки Debug версии необходимо также указать файлы для Release. Для этих целей смените конфигурацию в верхнем левом углу окна и повторите процесс аналогично прошлому этапу, изменив конфигурацию следующим образом:
- sfml-graphics.lib
- sfml-window.lib
- sfml-audio.lib
- sfml-system.lib
- sfml-network.lib

https://ratcatcher.ru/media/inf/pr/pr5/S_8.png

Компоновщик -> Дополнительно -> Точка входа (mainCRTStartup)
Кнопка применить!

https://ratcatcher.ru/media/inf/pr/pr5/S_9.png

Копируем все файлы из папки bin библиотеки SFML и вставляем в наш проект.

https://ratcatcher.ru/media/inf/pr/pr5/S_10.png

Проверка подключения#

Если вы правильно настроли среду, то при запуске следующего кода вы увидите окно с черным экраном.

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear();
        window.display();
    }

    return 0;
}

https://ratcatcher.ru/media/inf/pr/pr5/S_11.png

Создание окна#

sf::RenderWindow — это класс, который представляет собой окно, используемое для отображения графических объектов. Окно предоставляет графический контекст для отрисовки 2D объектов, таких как спрайты, текстуры, тексты и прочие элементы.

  sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");

Здесь:

sf::VideoMode(200, 200) задает размеры окна: ширина — 200 пикселей, высота — 200 пикселей.

"SFML works!" — строка, которая будет отображаться в заголовке окна.

Примечание: sf::RenderWindow автоматически создает контекст OpenGL для отрисовки, что делает его удобным для создания графических приложений.

Основной цикл программы#

В графических приложениях обычно используется главный цикл, который продолжает выполняться, пока окно активно. Этот цикл управляет обновлением экрана, обработкой событий и логикой приложения. Рассмотрим его структуру:

while (window.isOpen())
{
    // Обработка событий
    sf::Event event;
    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
            window.close();
    }

    // Очистка окна
    window.clear();

    // Обновление содержимого окна
    window.display();
}

Ключевым элементом графических приложений является обработка событий (нажатие клавиш, изменение размера окна, движение мыши и т.д.). В SFML это реализовано через класс sf::Event и метод window.pollEvent(event).

sf::Event event — объект, который хранит информацию о произошедшем событии.
window.pollEvent(event) — метод, который проверяет очередь событий окна и извлекает их. Если есть новое событие, метод возвращает true, и событие обрабатывается. Если событий нет, возвращает false.

sf::Event event;
while (window.pollEvent(event))
{
    if (event.type == sf::Event::Closed)
        window.close();
}

Если тип события sf::Event::Closed, это значит, что пользователь нажал на кнопку закрытия окна. В ответ на это окно закрывается с помощью метода window.close(), что завершает главный цикл программы.

Замечание: Все события, включая изменения фокуса окна, нажатия клавиш, перемещение мыши и т.д., помещаются в очередь событий. Метод pollEvent позволяет извлекать и обрабатывать эти события по очереди.

Обновление и отображение содержимого окна#

В каждом кадре важно сначала очистить окно, затем нарисовать новый кадр и обновить содержимое экрана. Эти действия выполняются следующими методами:

window.clear() — очищает окно, удаляя все, что было отрисовано на предыдущем шаге.

window.display() — отображает все, что было нарисовано с момента последнего вызова clear(). Это ключевой метод для обновления изображения на экране.

Практика#

Скаченные файлы (cookie.png и arial.ttf) из начала поместите в папку с проектом

Первым шагом в создании игры на SFML является инициализация окна. В данном случае окно создается с размерами 800x600 пикселей и заголовком "Cookie Clicker":

    sf::RenderWindow window(sf::VideoMode(800, 600), "Cookie Clicker");

Задается окно, в котором будет происходить вся отрисовка и взаимодействие с пользователем. Это основной контейнер для всех графических объектов в игре.

    sf::RenderWindow window(sf::VideoMode(800, 600), "Cookie Clicker");

Чтобы взаимодействовать с игроком, необходимо отобразить графический объект — печенье. Это реализовано через загрузку текстуры и создание спрайта.

    // Загрузка текстуры печеньки
    sf::Texture cookieTexture;
    if (!cookieTexture.loadFromFile("cookie.png")) {
        return -1;
    }

    // Спрайт для печеньки
    sf::Sprite cookieSprite;
    cookieSprite.setTexture(cookieTexture);
    cookieSprite.setPosition(300, 200); // Позиция печеньки

Текстура — это изображение, которое будет отображаться на экране. Спрайт — это графический объект, который визуализирует текстуру. В данном примере спрайт печеньки размещается на позиции (300, 200).

sf::Sprite - класс, описывающий спрайт, спрайт - это некий графический объект, которым мы можем управлять, менять его текстуру и другий свойства
sf::Texture` - это класс текстуры, по сути просто картинка, которую мы загрузили и можем натянуть куда-нибудь

Для отображения счёта нужно инициализировать текст. Это делается с помощью шрифта и объекта sf::Text.

    // Шрифт для отображения счёта
    sf::Font font;
    if (!font.loadFromFile("arial.ttf")) {
        return -1;
    }

    // Текст для отображения счёта
    sf::Text scoreText;
    scoreText.setFont(font);
    scoreText.setCharacterSize(30);
    scoreText.setFillColor(sf::Color::White);
    scoreText.setPosition(10, 10);

Используем шрифт для текста, задаем его размер и цвет. Текст будет отображаться в левом верхнем углу окна.

Игровая логика начинается с инициализации переменной счёта, которая будет увеличиваться при каждом нажатии на печенье.

    int score = 0;  // Начальный счёт
    scoreText.setString("Score: " + std::to_string(score));

Используем текст для отображения начального значения счёта, который изначально равен 0.

Как и в любой игре, необходим основной цикл, который будет управлять всей логикой игры. В SFML такой цикл продолжается до тех пор, пока окно открыто.

    while (window.isOpen()) {
    // Обработка событий
        sf::Event event;
        while (window.pollEvent(event)) {
            ...
        }

        // Очистка и отрисовка объектов
        window.clear();
        window.draw(cookieSprite);
        window.draw(scoreText);
        window.display();
}

Цикл выполняет следующие шаги:

  • Обработка событий.
  • Очистка экрана для подготовки к новому кадру.
  • Отрисовка объектов (печенья и счёта).
  • Отображение новых данных на экране.

Одним из важнейших элементов любой игры является обработка событий, таких как нажатие кнопок или клики мышью. В данном примере используются следующие типы событий:
- Закрытие окна.
- Клики мышью.

    if (event.type == sf::Event::Closed) {
    window.close();
    }

    if (event.type == sf::Event::MouseButtonPressed) {
        if (event.mouseButton.button == sf::Mouse::Left) {
            sf::Vector2i mousePos = sf::Mouse::getPosition(window);

            // Проверка, попал ли клик на печеньку
            if (cookieSprite.getGlobalBounds().contains(mousePos.x, mousePos.y)) {
                score += 1;  // Увеличение счёта
                scoreText.setString("Score: " + std::to_string(score));  // Обновление текста
            }
        }
    }

Если нажата кнопка закрытия окна, программа завершает выполнение.

При клике левой кнопкой мыши проверяется, находится ли курсор в пределах границ спрайта печеньки. Если да — увеличивается счёт на единицу, и текст обновляется.

Итоговый проект#

#include <SFML/Graphics.hpp>
#include <vector>
#include <cstdlib> // Для rand()
#include <ctime>   // Для time()

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

    // Загрузка текстуры печеньки
    sf::Texture cookieTexture;
    if (!cookieTexture.loadFromFile("cookie.png")) {
        return -1;
    }

    // Спрайт для печеньки
    sf::Sprite cookieSprite;
    cookieSprite.setTexture(cookieTexture);
    cookieSprite.setPosition(300, 200); // Позиция печеньки

    // Шрифт для отображения счёта
    sf::Font font;
    if (!font.loadFromFile("arial.ttf")) {
        return -1;
    }

    // Текст для отображения счёта
    sf::Text scoreText;
    scoreText.setFont(font);
    scoreText.setCharacterSize(30);
    scoreText.setFillColor(sf::Color::White);
    scoreText.setPosition(10, 10);

    int score = 0;  // Начальный счёт
    scoreText.setString("Score: " + std::to_string(score));

    // Основной цикл игры
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }

            // Проверка клика мышью
            if (event.type == sf::Event::MouseButtonPressed) {
                if (event.mouseButton.button == sf::Mouse::Left) {
                    // Получение позиции клика
                    sf::Vector2i mousePos = sf::Mouse::getPosition(window);

                    // Проверка, попал ли клик на печеньку
                    if (cookieSprite.getGlobalBounds().contains(mousePos.x, mousePos.y)) {
                        score += 1;;  // Увеличение счёта
                        scoreText.setString("Score: " + std::to_string(score));  // Обновление текста
                    }
                }
            }
        }

        // Очистка экрана
        window.clear();

        // Отрисовка печеньки и текста счёта
        window.draw(cookieSprite);
        window.draw(scoreText);

        // Отображение нового кадра
        window.display();
    }

    return 0;
}

https://ratcatcher.ru/media/inf/pr/pr5/S_12.png)