Мультимедийная библиотека SFML C++
March 15

Чистая архитектура в играх на SFML С++: как разложить код по полочкам

Привет, разработчики! Сегодня поговорим о том, как применить чистую архитектуру (Clean Architecture) в разработке игр с использованием библиотеки SFML. Если вы хотите, чтобы ваш код был модульным, тестируемым и не привязанным к конкретной библиотеке, этот подход для вас. Давайте разберем, как разделить код на слои и сделать вашу игру структурированной и гибкой.

Что такое чистая архитектура?

Чистая архитектура — это концепция, предложенная Робертом Мартином (дядей Бобом), которая помогает организовать код так, чтобы он был:

  • Независимым от фреймворков (в нашем случае — SFML).
  • Тестируемым без запуска всей игры.
  • Четко разделенным по зонам ответственности.

Идея в том, чтобы игровая логика не знала о том, как рисуются спрайты или как обрабатываются нажатия клавиш. Вместо этого мы разбиваем код на слои, где каждый слой делает свою работу.

Слои чистой архитектуры в игре

Представьте игру как торт: каждый слой — это отдельный уровень со своей задачей. Вот как это может выглядеть в контексте SFML:

Структура проекта

1. Сущности (Entities)

Это сердце игры — основные объекты, такие как игрок, враги или пули. Они содержат данные (позицию, здоровье, скорость) и базовую логику, но не знают ничего о SFML.

Пример:

class Player {
public:
    float x, y;
    float speed;
    int health;

    void move(float dx, float dy) {
        x += dx * speed;
        y += dy * speed;
    }
};

2. Бизнес-логика (Use Cases / Interactors)

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

Пример:

class MovePlayerUseCase {
public:
    void execute(Player& player, float dx, float dy) {
        player.move(dx, dy);
    }
};

3. Интерфейсы (Controllers / Presenters)

Этот слой связывает бизнес-логику с внешним миром. Например:

  • InputController — превращает нажатия клавиш в команды для игры.
  • Renderer — отвечает за отрисовку.

Пример обработки ввода:

class InputController {
public:
    void handleInput(sf::RenderWindow& window, 
    MovePlayerUseCase& moveUseCase, Player& player) {
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) {
            moveUseCase.execute(player, 0, -1);
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
            moveUseCase.execute(player, 0, 1);
        }
    }
};

4. Инфраструктура (Frameworks & Drivers)

Тут живет код, который зависит от SFML: рендеринг, обработка событий, создание окон. Этот слой изолирован от остальной логики.

Пример рендеринга:

class SfmlRenderer {
public:
    void render(sf::RenderWindow& window, const Player& player) {
        sf::CircleShape shape(20.f);
        shape.setPosition(player.x, player.y);
        shape.setFillColor(sf::Color::Green);
        window.draw(shape);
    }
};

Собираем игру: главный цикл

Теперь соединим всё в главном цикле игры.

Вот пример:

#include <SFML/Graphics.hpp>
#include "Entities/Player.hpp"
#include "UseCases/MovePlayerUseCase.hpp"
#include "Interfaces/InputController.hpp"
#include "Infrastructure/SfmlRenderer.hpp"

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 600), "Clean SFML Game");

    Player player{400.f, 300.f, 5.f, 100}; // Начальная позиция и параметры
    MovePlayerUseCase moveUseCase;
    InputController inputController;
    SfmlRenderer renderer;

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

        // Ввод
        inputController.handleInput(window, moveUseCase, player);

        // Рендеринг
        window.clear();
        renderer.render(window, player);
        window.display();
    }

    return 0;
}

Почему это круто?

  1. Гибкость: Хотите заменить SFML на SDL? Просто перепишите слой инфраструктуры — остальной код не тронется.
  2. Тестируемость: Можно проверять логику движения или столкновений без запуска окна.
  3. Порядок: Каждый слой отвечает за своё, и код становится читаемым.

Полезные советы

Используйте абстрактные классы или интерфейсы, чтобы слои общались через четкие границы.

Не храните состояние игры (например, координаты) в объектах SFML — держите их в сущностях.

Избегайте соблазна смешивать логику с рендерингом — это путь к хаосу.

Чистая архитектура в играх на SFML — это не просто модный подход, а способ сделать ваш проект масштабируемым и удобным для поддержки. Да, на старте придется потратить чуть больше времени на разделение кода, но в долгосрочной перспективе это окупается.

Если у вас есть вопросы или вы хотите разобрать конкретный пример — пишите в комментариях!

А как вы организуете код в своих играх? Делитесь опытом!

Телеграмм канал - Программирование игр С++