1. Фундамент архитектуры: отделение игрового цикла (Game Loop) от жизненного цикла Godot
Фундамент архитектуры: отделение игрового цикла (Game Loop) от жизненного цикла Godot
Добро пожаловать в курс «Архитектура игр на Godot: Создание движка внутри движка». Мы начинаем путешествие не просто по изучению инструментов Godot, а по созданию надежной, переносимой и масштабируемой архитектуры.
Многие новички начинают свой путь с создания сцены, добавления спрайта и написания скрипта, который наследуется от Node2D и использует _process(delta) для движения. Это отлично работает для прототипов. Но когда проект разрастается, такой подход превращается в «спагетти-код», где логика игры намертво привязана к визуальному представлению и конкретному движку.
Наша цель — научиться писать игру так, чтобы Godot был лишь «оболочкой», предоставляющей рендеринг, ввод и звук, в то время как «мозг» игры (ваш микродвижок) оставался чистым и независимым.
Проблема «Godot Way» в больших проектах
Godot навязывает удобную, но опасную для архитектуры парадигму: Дерево Сцены (Scene Tree). Когда вы пишете логику внутри методов _process или _physics_process конкретных узлов, вы создаете жесткую связность (coupling).
Почему это плохо для сложной архитектуры:
Node, Vector2, Input)._process в разных узлах может варьироваться, что усложняет создание предсказуемой симуляции (например, для сетевого кода или реплеев).Концепция «Микродвижок»
Идея заключается в создании чистой модели симуляции, которая ничего не знает о том, что она отображается в Godot. Эта модель живет в своем собственном цикле обновления.
В этой архитектуре Godot выполняет роль View (Представления) в паттерне MVC, а ваш микродвижок — это Model (Модель) и Controller (Контроллер).
Игровой цикл (Game Loop): Анатомия времени
Сердце любой игры — это бесконечный цикл. В простейшем виде он выглядит так:
Godot делает это за нас. Однако, чтобы владеть ситуацией, мы должны взять контроль над шагом 2 — Обновлением состояния.
Дельта-время и проблема плавающего шага
Метод _process(delta) вызывается каждый кадр. Время между кадрами (delta) может меняться. Если FPS падает, delta растет. Для простой аркады формула перемещения выглядит так:
Где: * — новая позиция объекта. * — текущая позиция объекта. * — вектор скорости. * — время, прошедшее с прошлого кадра (delta).
Проблема этой формулы в том, что при разных значениях результаты вычислений могут накапливать погрешность, особенно в физических симуляциях. Это убивает детерминизм.
Паттерн «Аккумулятор» (Fixed Time Step)
Чтобы наш микродвижок работал стабильно и предсказуемо, мы должны отвязать время симуляции от времени рендеринга. Мы будем использовать фиксированный шаг времени (например, 60 раз в секунду), независимо от того, сколько кадров выдает монитор (30, 144 или 60).
Алгоритм работы нашего собственного цикла внутри Godot:
_process(delta).delta в переменную-аккумулятор.Математически условие цикла выглядит так:
Где: * — накопленное время (аккумулятор). * — фиксированный шаг времени (например, сек для 60 Гц).
Реализация на GDScript
Давайте создадим структуру, где логика отделена от узлов.
1. Чистая логика (Микродвижок)
Создадим класс, который не наследуется от Node, а, например, от RefCounted (чтобы управлять памятью, но не висеть в дереве сцены). Это будет наш «Мир».
2. Связующее звено (Godot Loop)
Теперь создадим узел, который будет жить в сцене Godot и управлять нашим микродвижком.
``gdscript
game_runner.gd
extends Node
var _simulation: SimulationWorld var _accumulator: float = 0.0 var _fixed_dt: float = 1.0 / 60.0
Ссылка на визуальное представление (например, спрайт)
@onready var _player_sprite: Sprite2D = P_{render}P_{prev}P_{curr}\alpha\frac{accumulator}{time\_step}$ (значение от 0.0 до 1.0).Этот метод позволяет получить «масляную» плавность картинки даже при низкой частоте обновления логики.
Преимущества подхода
практически не использует API Godot (кроме базовых типов вроде Vector2). Его легко перенести или использовать на сервере., при этом интерфейс и анимации меню продолжат работать, так как _process Godot не остановлен.Заключение
Мы заложили первый камень в фундамент: отделили время симуляции от времени рендеринга и вынесли логику из узлов сцены в чистый класс. Теперь Godot для нас — это мощный инструмент ввода-вывода, а не диктатор архитектуры.
В следующей статье мы разберем, как наполнить наш SimulationWorld` сущностями и компонентами, не превращая код в хаос.