В Godot пользовательский интерфейс (UI) строится из узлов типа Control (и его наследников). Они отличаются от Node2D:
| Node2D | Control | |
|---|---|---|
| Позиционирование | Координаты X, Y | Якоря + отступы |
| Масштабирование | Ручное | Автоматическое (адаптивное) |
| Назначение | Игровые объекты | Интерфейс |
| Рендеринг | В мировом пространстве | В экранном пространстве |
Важно: UI-элементы обычно размещают внутри CanvasLayer, чтобы они отображались поверх игровой сцены и не зависели от камеры.
| Узел | Назначение | Ключевые свойства |
|---|---|---|
Label |
Текст | text, horizontal_alignment |
RichTextLabel |
Форматированный текст | text, bbcode_enabled |
TextureRect |
Изображение | texture, stretch_mode |
Panel |
Фоновая панель | theme |
| Узел | Назначение | Ключевой сигнал |
|---|---|---|
Button |
Кнопка | pressed |
TextureButton |
Кнопка с изображением | pressed |
LineEdit |
Поле ввода (одна строка) | text_submitted |
CheckBox |
Флажок | toggled(bool) |
HSlider |
Горизонтальный ползунок | value_changed(float) |
| Узел | Расположение |
|---|---|
VBoxContainer |
Вертикально (столбец) |
HBoxContainer |
Горизонтально (строка) |
GridContainer |
Сетка |
MarginContainer |
С отступами от краёв |
CenterContainer |
Центрирование содержимого |
PanelContainer |
Фон + контейнер |
Контейнеры автоматически расставляют дочерние элементы. Вам не нужно вручную задавать позиции.
VBoxContainer
├── Label ("Название игры")
├── Button ("Играть")
├── Button ("Об игре")
└── Button ("Выход")
Кнопки автоматически расположатся друг под другом с одинаковым отступом.
Якоря определяют, как элемент привязан к родителю при изменении размера окна.
В верхней панели при выбранном Control-узле есть кнопка Anchor Presets (зелёные иконки):
| Пресет | Поведение |
|---|---|
| Full Rect | Растянут на весь родитель |
| Center | В центре |
| Top Left | В левом верхнем углу |
| Bottom Right | В правом нижнем углу |
| Top Wide | Растянут по всей ширине сверху |
| Left Wide | Растянут по всей высоте слева |
Для мобильных игр обязательно используйте якоря — размер экрана у разных устройств разный.
ButtonТема задаёт единый визуальный стиль для всех UI-элементов: шрифты, цвета, отступы.
func _ready() -> void:
# Тему проще настраивать через Inspector:
# выберите корневой Control → Theme → New Theme → редактируйте
# Тема автоматически применяется ко всем дочерним узлам
# Пример установки цвета шрифта для конкретного узла:
$Label.add_theme_color_override("font_color", Color.RED)
$Label.add_theme_font_size_override("font_size", 24)
AnimationPlayer — мощный инструмент для создания анимаций любых свойств любых узлов.
AnimationPlayer в сценуfade_in)Label → modulate:a для прозрачности)@onready var anim := $AnimationPlayer
func show_title() -> void:
anim.play("fade_in")
func hide_title() -> void:
anim.play("fade_out")
# Анимация завершилась
func _ready() -> void:
anim.animation_finished.connect(_on_animation_finished)
func _on_animation_finished(anim_name: StringName) -> void:
if anim_name == "fade_out":
queue_free() # Удалить узел после исчезновения
position — движениеrotation — вращениеscale — масштабmodulate — цвет и прозрачностьvisible — видимостьTween — альтернатива AnimationPlayer для простых анимаций из кода. Не требует узла в дереве сцены.
func animate_button(button: Button) -> void:
var tween := create_tween()
# Плавно изменить масштаб с текущего до (1.2, 1.2) за 0.2 секунды
tween.tween_property(button, "scale", Vector2(1.2, 1.2), 0.2)
# Затем обратно
tween.tween_property(button, "scale", Vector2(1.0, 1.0), 0.2)
func win_animation() -> void:
var tween := create_tween()
# Последовательно:
tween.tween_property($WinLabel, "modulate:a", 1.0, 0.5) # Появление
tween.tween_property($WinLabel, "scale", Vector2(1.5, 1.5), 0.3) # Увеличение
tween.tween_property($WinLabel, "scale", Vector2(1.0, 1.0), 0.3) # Обратно
tween.tween_interval(1.0) # Пауза 1 секунда
tween.tween_callback(show_next_level_button) # Вызов функции
func entrance_animation() -> void:
var tween := create_tween()
tween.set_parallel(true) # Все последующие — параллельно
tween.tween_property(self, "position:y", 300.0, 0.5)
tween.tween_property(self, "modulate:a", 1.0, 0.5)
var tween := create_tween()
tween.tween_property($Sprite, "position:y", 100.0, 0.5) \
.set_trans(Tween.TRANS_BOUNCE) \
.set_ease(Tween.EASE_OUT)
# TRANS_BOUNCE — тип перехода (с отскоком)
# EASE_OUT — направление (затухание к концу)
# Типы перехода:
# TRANS_LINEAR — равномерно
# TRANS_SINE — плавно (синус)
# TRANS_BOUNCE — с отскоком
# TRANS_ELASTIC — с пружинным эффектом
# TRANS_BACK — с забеганием назад
func go_to_level(level_number: int) -> void:
var path := "res://scenes/levels/level_%d.tscn" % level_number
get_tree().change_scene_to_file(path)
func change_scene_with_fade(scene_path: String) -> void:
# Затемнение
var tween := create_tween()
tween.tween_property($FadeRect, "modulate:a", 1.0, 0.3)
tween.tween_callback(func(): get_tree().change_scene_to_file(scene_path))
$FadeRect — это ColorRect (чёрный прямоугольник на весь экран), размещённый в CanvasLayer.
Для данных, которые должны сохраняться между сценами (текущий уровень, счёт, настройки), используют Autoload.
Настройка:
game_manager.gd)GameManager)# game_manager.gd — Autoload-скрипт
extends Node
var current_level: int = 1
var levels_completed: Array[int] = []
var total_levels: int = 5
func complete_level(level: int) -> void:
if level not in levels_completed:
levels_completed.append(level)
if level < total_levels:
current_level = level + 1
func is_level_completed(level: int) -> bool:
return level in levels_completed
func go_to_level(level: int) -> void:
current_level = level
# Используем единую базовую сцену уровня
get_tree().change_scene_to_file("res://scenes/level_base.tscn")
func go_to_menu() -> void:
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
func go_to_level_select() -> void:
get_tree().change_scene_to_file("res://scenes/level_select.tscn")
Доступ из любого скрипта:
GameManager.complete_level(1)
GameManager.go_to_level(2)
scenes/
main_menu.tscn
level_select.tscn
level_base.tscn ← единая базовая сцена для всех уровней
scripts/
game_manager.gd ← Autoload, хранит current_level
level_base.gd ← подгружает контент уровня по current_level
Вместо отдельных сцен для каждого уровня используем одну level_base.tscn. Какой уровень загрузить, определяется по GameManager.current_level.
extends Control
@export var level_button_scene: PackedScene
func _ready() -> void:
var grid := $GridContainer
for i in range(1, GameManager.total_levels + 1):
var button := Button.new()
button.text = str(i)
button.custom_minimum_size = Vector2(80, 80)
# Отметить пройденные уровни
if GameManager.is_level_completed(i):
button.modulate = Color.GREEN
# Подключить нажатие
# Важно: захватываем i в переменную, иначе все кнопки получат последнее значение!
var level_num := i
button.pressed.connect(func(): GameManager.go_to_level(level_num))
grid.add_child(button)
Для мобильных устройств нужно обрабатывать тач-события вместо мыши.
func _input(event: InputEvent) -> void:
if event is InputEventScreenTouch:
if event.pressed:
print("Палец коснулся: ", event.position)
else:
print("Палец убран: ", event.position)
func _input(event: InputEvent) -> void:
if event is InputEventScreenDrag:
print("Перетаскивание: ", event.position)
print("Скорость: ", event.velocity)
В настройках проекта (Project Settings → Input Devices → Pointing) включите:
Это позволяет использовать один код для мыши и тача — обрабатывайте InputEventMouseButton и InputEventMouseMotion, и они будут работать на обоих платформах.
Частицы создают визуальные эффекты: искры, свечение, дым.
GPUParticles2Damount — количество частиц (20-50)lifetime — время жизни (0.5-2.0 с)direction — направлениеspread — разбросgravity — гравитация частицscale_min/scale_max — размерcolor — цвет# Одноразовый эффект (например, победа)
func play_win_effect() -> void:
$WinParticles.emitting = true
# Частицы остановятся сами после lifetime
| Тема | Ключевое |
|---|---|
| UI-узлы | Button, Label, контейнеры (VBox, HBox, Grid) |
| Якоря | Привязка к краям родителя, адаптивность |
| AnimationPlayer | Анимация любых свойств через редактор |
| Tween | Программные анимации (create_tween()) |
| Переходы | get_tree().change_scene_to_file() |
| Autoload | Глобальные данные между сценами (GameManager) |
| Тач-ввод | Emulate Mouse From Touch — универсальный код |