Godot — движок, в основе которого лежит объектно-ориентированная архитектура. Но ООП в Godot устроено не так, как в классическом программировании на Java или C#. Здесь классы не парят в абстракции — они буквально живут в сцене, а наследование, композиция и сигналы работают вместе, чтобы строить гибкую и выразительную игровую логику. В этой статье мы максимально подробно разберём модель ООП в Godot: что она собой представляет, как она устроена и как использовать её эффективно.
Если вы хотите понимать Godot «как движок мыслит», то без этой статьи не обойтись.
Что такое ООП в контексте Godot
Объектно-ориентированная концепция в Godot опирается на три ключевые идеи:
- Каждая нода — объект.
- Каждый .gd-файл — класс.
- Каждая сцена — дерево объектов.
Поэтому GDScript и сцены Godot образуют целостный объектный мир: объекты наследуют поведение, ноды взаимодействуют друг с другом, а сцены позволяют собирать сложные структуры как из блоков LEGO.
Класс в GDScript = Скрипт + Нода
Класс в GDScript создаётся в момент, когда вы пишете скрипт:
extends Node2D
var speed := 200
func move_forward(delta):
position.x += speed * delta
Этот скрипт:
- является классом;
- расширяет Node2D (родительский класс);
- создаёт компонент поведения для ноды.
Важный момент: один и тот же класс можно использовать либо как компонент ноды, либо как отдельный объект, если создать его вручную через new().
Фундаментальная мысль: класс в Godot — это способ расширить поведение ноды сцены. Он не бывает «пустым», он всегда привязан к какому-то уровню дерева объектов.
class_name — именованные классы
По умолчанию ваш класс анонимный. Но если вы хотите использовать его как полноценный тип — присвойте ему имя:
class_name Enemy
extends CharacterBody2D
var health := 100
Теперь:
- класс появляется в списке типов Godot;
- можно создавать его через Enemy.new();
- его можно использовать как тип в аннотациях (например: var target: Enemy).
Наследование: расширение поведения
Наследование в Godot — прямое и понятное. Вы делаете новый класс на основе существующего:
class_name CharacterBase
extends CharacterBody2D
var speed := 120
var health := 100
func move(direction: Vector2, delta: float):
velocity = direction * speed
move_and_slide()
И дочерний класс:
class_name Enemy
extends CharacterBase
func _process(delta):
move(Vector2.LEFT, delta)
Дочерний класс может:
- добавлять новые свойства;
- переопределять функции;
- использовать всё, что было в родителе.
Когда использовать наследование
Оно уместно, когда:
- классы имеют одинаковую природу (все — персонажи);
- поведение базовое, а различия — в деталях;
- вы хотите избежать дублирования логики.
Например, Player и Enemy оба могут наследоваться от CharacterBase.
Когда не использовать наследование
Наследование в Godot легко приводит к слишком глубоким цепочкам.
Рекомендация: не делайте более 2–3 уровней наследования. Дальше лучше перейти на композицию.
super: вызов родительских методов
Если нужно расширить, а не заменить поведение родителя:
func _ready():
super._ready()
print("Enemy готов к бою")
Это полезно, когда базовый класс уже делает часть работы, а дочернему классу нужно добавить к ней что-то своё.
Композиция: главный принцип Godot
📢 Подписывайтесь на наш Telegram-канал.
Там вы найдете анонсы обучающих статей и видео, готовый код для ваших проектов и увлекательные курсы. Ничего лишнего — только практика, вдохновение и развитие.
В Godot композиция важнее наследования.
Композиция = объект собирается из нескольких самостоятельных компонентов.
Пример:
- HealthComponent.gd — здоровье;
- AttackComponent.gd — атака;
- AIComponent.gd — интеллект;
- LootComponent.gd — выпадение предметов.
В итоге враг может состоять так:
Enemy
├── HealthComponent
├── AttackComponent
├── AIComponent
└── Sprite2D
Плюсы:
- гибко комбинируется;
- легко заменить один компонент;
- легко тестировать;
- нет огромных классов.
Композиция — лучший вариант для больших проектов. Она позволяет создавать разные сущности из общих блоков.
Сигналы: события между объектами
Сигналы — элемент ООП-модели Godot. Это способ разъединить объекты: один сообщает о событии, другие реагируют.
Создание сигнала
signal died
Вызов сигнала
func take_damage(amount):
health -= amount
if health <= 0:
emit_signal("died")
Подключение сигнала
func _ready():
connect("died", self, "_on_died")
func _on_died():
queue_free()
Зачем нужны сигналы
- объект не обязан знать ВСЁ о другом объекте;
- код становится гибче;
- сцены проще поддерживать.
Пример: UI слушает события игрока, но не знает *как* работает игрок.
Взаимодействие нод в сцене
Ноды в Godot взаимодействуют через дерево сцены. Это важная часть ООП-модели движка.
Два стандартных способа
Короткие пути через $
$Sprite2D.flip_h = true
2. Полный путь через get_node()
var enemy = get_node("Enemies/Enemy1")
enemy.take_damage(20)
Используйте onready для безопасного получения ноды:
onready var ui := get_node("../UI")
Передача данных между объектами
Есть пять рабочих способов:
- Передать ссылку → надёжно.
- Использовать сигналы → модульно.
- Использовать автозагрузку → глобально.
- Хранить зависимости в родительской ноде → структурно.
- Инициализировать объект параметрами после создания → гибко.
Пример параметризации:
var bullet = bullet_scene.instantiate()
bullet.damage = 25
bullet.direction = Vector2.RIGHT
add_child(bullet)
Встроенные классы Godot и их роль в ООП
Godot предлагает богатую иерархию классов:
- Node — базовый объект;
- Node2D / Node3D — позиция;
- CharacterBody2D — движение с физикой;
- RigidBody2D — физика высокого уровня;
- Area2D — триггеры;
- Sprite2D / AnimatedSprite2D — визуал;
- Timer — таймеры;
- AI NavigationAgent2D — навигация.
Каждый из этих классов можно расширить через наследование или использовать как компонент при композиции.
Переопределение методов
Переопределяйте методы, когда хотите изменить поведение:
func _process(delta):
super._process(delta)
patrol()
Это позволяет добавлять новую логику, не ломая базовый класс.
Главная философия ООП в Godot
Её можно выразить в одном предложении:
- Ноды = объекты
- Сцены = классы объектов
- Скрипты = поведение
- Сигналы = связь между ними
Это сильно отличается от привычных моделей ООП, но на практике работает намного гибче для разработки игр.
Итоги статьи
Мы глубоко разобрали объектно-ориентированную модель Godot. Теперь вы понимаете:
- как работает система классов GDScript;
- как использовать class_name правильно;
- когда применять наследование, а когда — композицию;
- как строить архитектуру через сигналы;
- как ноды взаимодействуют внутри сцены;
- как передавать данные между объектами;
- как расширять встроенные классы движка.
Умение грамотно применять эти инструменты — шаг от «просто работающего кода» к архитектуре, которую легко поддерживать и развивать.
26.11.2025
0
12
Комментарии
0