Чистая архитектура в играх на 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)
Этот слой связывает бизнес-логику с внешним миром. Например:
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;
}Почему это круто?
- Гибкость: Хотите заменить SFML на SDL? Просто перепишите слой инфраструктуры — остальной код не тронется.
- Тестируемость: Можно проверять логику движения или столкновений без запуска окна.
- Порядок: Каждый слой отвечает за своё, и код становится читаемым.
Полезные советы
Используйте абстрактные классы или интерфейсы, чтобы слои общались через четкие границы.
Не храните состояние игры (например, координаты) в объектах SFML — держите их в сущностях.
Избегайте соблазна смешивать логику с рендерингом — это путь к хаосу.
Чистая архитектура в играх на SFML — это не просто модный подход, а способ сделать ваш проект масштабируемым и удобным для поддержки. Да, на старте придется потратить чуть больше времени на разделение кода, но в долгосрочной перспективе это окупается.
Если у вас есть вопросы или вы хотите разобрать конкретный пример — пишите в комментариях!