Кастомные меню в Godot: старт, пауза и экран Game Over
Краткое содержание
- Кастомные меню улучшают навигацию и пользовательский опыт в вашей игре.
- В Godot меню удобно реализовывать через Control-узлы и сцены.
- В статье есть рабочие примеры: стартовое меню, пауза и экран конца игры с кодом на GDScript.

Зачем делать собственные меню
Кастомные меню — это не просто набор кнопок. Это входная дверь в игру: первые секунды контакта с игроком, навигация между состояниями, доступ к настройкам и возможность быстро вернуться в игровой процесс. Хорошо продуманное меню:
- экономит игроку время;
- даёт понятную обратную связь;
- повышает retention и удовлетворённость от UX.
В Godot UI строится из Control-узлов и сцен — это делает меню модульными, легко тестируемыми и локализуемыми.
Настройка проекта и базовая сцена
Начнём с простой 2D‑сцены и базового контроллера игрока. Добавьте KinematicBody2D (или соответствующий узел вашей версии Godot) с CollisionShape2D и Sprite2D.
Код движения игрока (GDScript):
extends KinematicBody2D
const SPEED = 200
const GRAVITY = 500
var velocity = Vector2.ZERO
func _physics_process(delta):
var move_direction = 0
if Input.is_action_pressed("ui_right"):
move_direction += 1
if Input.is_action_pressed("ui_left"):
move_direction -= 1
velocity.x = move_direction * SPEED
velocity.y += GRAVITY * delta
velocity = move_and_slide(velocity, Vector2.UP)Пояснения:
- SPEED — скорость по горизонтали.
- GRAVITY — величина гравитации.
- move_and_slide() обрабатывает скольжение и столкновения.
Важно: в Godot 4 класс для персонажа мог измениться (CharacterBody2D). Если вы используете Godot 4, адаптируйте API соответственно.
Создание стартового меню
Стартовое меню обычно оформляют как отдельную сцену с корнем Control. Это обеспечивает чистоту архитектуры: сцены легко подгружать и заменять.
Шаги:
- Создайте новую сцену с корнем Control.
- Добавьте Label и установите текст, например: “Simple Game” или локализованный вариант.
- Добавьте Button и назовите его “Play Game” (или “Играть”).
- Добавьте второй Button для выхода: “Exit” (или “Выход”).
Пример скрипта для стартового меню:
extends Control
func _ready():
var playButton = $Button
playButton.connect("pressed", self, "_on_PlayButton_pressed")
func _on_PlayButton_pressed():
# Load the game scene
var gameScene = preload("res://GameScene.tscn")
# Transition to the game scene
get_tree().change_scene(gameScene)Если добавляете кнопку выхода, соедините её сигнал с функцией, вызывающей get_tree().quit():
func _ready():
var playButton = $Button
var exitButton = $Button2
playButton.connect("pressed", self, "_on_PlayButton_pressed")
exitButton.connect("pressed", self, "_on_ExitButton_pressed")
func _on_ExitButton_pressed():
# Quit the game
get_tree().quit()Советы по дизайну стартового меню:
- Сделайте главный CTA (Call To Action) крупным и заметным.
- Предусмотрите быстрый доступ к настройкам и языку.
- Для мобильных устройств используйте большие зоны нажатия.
Создание меню паузы
Меню паузы — перекрывающий UI, который обычно добавляется как дочерняя сцена поверх основной игры. Его можно хранить в дереве сцены и показывать/скрывать по необходимости.
Пример логики меню паузы:
extends Control
func _ready():
$btnResume.connect("pressed", self, "_on_resume_button_pressed")
$btnHome.connect("pressed", self, "_on_menu_button_pressed")
$btnExit.connect("pressed", self, "_on_exit_button_pressed")
pause_mode = Node.PAUSE_MODE_PROCESS
get_tree().paused = false
self.hide()
func _input(event):
if event.is_action_pressed("ui_cancel"):
if not self.is_visible_in_tree():
# Pause the game when the pause menu is not visible
self.show()
get_tree().paused = true
else:
# Unpause the game when the pause menu is already visible
self.hide()
get_tree().paused = false
func _on_resume_button_pressed():
# Hide the pause menu and resume the game
self.hide()
get_tree().paused = false
func _on_menu_button_pressed():
# Return to the main menu
get_tree().change_scene("res://StartMenu.tscn")
func _on_exit_button_pressed():
# Quit the game
get_tree().quit()Ключевые моменты:
- pause_mode = Node.PAUSE_MODE_PROCESS позволяет узлу обрабатывать входные события даже при паузе.
- get_tree().paused управляет глобальным состоянием паузы.
- По умолчанию меню скрыто (self.hide()), чтобы игра стартовала не на паузе.
Важно: используйте is_visible_in_tree() вместо is_visible(), если вам важно учитывать видимость с учётом родительских узлов.
Экран окончания игры (Game Over)
Экран “Game Over” обычно реализуется как отдельная сцена Control и показывается в момент, когда игрок теряет жизнь или пересекает границы игрового мира.
Пример сцены GameOver.tscn — скрипт:
extends Control
func _ready():
$Button.connect("pressed", self, "onPlayAgainPressed")
$Button2.connect("pressed", self, "onExitPressed")
func onPlayAgainPressed():
var gameScenePath = "res://GameScene.tscn"
get_tree().change_scene(gameScenePath)
func onExitPressed():
get_tree().quit() # Close the game applicationКак показать экран окончания из основной сцены:
extends KinematicBody2D
func _physics_process(delta):
# Check if the player has crossed the screen boundaries
var screen_size = get_viewport_rect().size
if velocity.y > screen_size.y or velocity.y < 0:
show_game_over_screen()
func show_game_over_screen():
get_tree().change_scene("res://GameOver.tscn")Совет: вместо мгновенной смены сцены можно показывать модальное окно с анимацией и только после подтверждения переходить к сцене.
Дополнительные функции и улучшения
Ниже — набор идей и практических подсказок, которые улучшат ощущение от меню и интерфейса.
Анимированные переходы
Используйте AnimationPlayer или Tween (в зависимости от версии Godot) для плавных переходов: fade‑in, slide‑in, scale. Анимация делает меню более «жизненным» и помогает скрывать моменты загрузки.
Пример: простая анимация появления через AnimationPlayer с изменением modulate.a (альфа-канала) и visible.
Звуковые эффекты
Добавьте AudioStreamPlayer для кликов по кнопкам и звуков при переходах между экранами:
# Предполагается, что в сцене есть AudioStreamPlayer с именем ClickSound
$ClickSound.play()Визуальные эффекты
Шейдеры и частицы подходят для эффектных заставок в меню. Но следите за производительностью — не используйте тяжёлые particle systems в главном меню на слабых устройствах.
Фоновая музыка
Оформите отдельные аудиодорожки для главного меню и игрового процесса. Используйте AudioBus, чтобы управлять громкостью и переключением, соблюдая плавность перехода (fade‑out/in).
Поддержка локализации
Организуйте текстовые ресурсы через экспортированные строки, CSV/Tres файлы или встроенные возможности локализации Godot. Для динамической подмены UI лучше держать все тексты в одном месте (ресурс переводов).
Мини‑методология: как планировать меню
- Определите ключевые состояния меню (старт, пауза, опции, Game Over).
- Сделайте список элементов для каждого состояния (кнопки, заголовки, индикаторы).
- Нарисуйте прототипы (бумага/фрейм‑мейкер).
- Разбейте на сцены: каждая сцена = отдельный UI-модуль.
- Реализуйте базовую логику сигналов и переходов.
- Добавьте анимации и звуки.
- Протестируйте на целевых устройствах и локализуйте.
Чек-листы по ролям
Дизайнер:
- Прототипы экранов и макеты.
- Определённые зоны для кнопок (для разных плотностей экранов).
- Дизайн состояний (hover, pressed, disabled).
Программист:
- Разделение UI на сцены и Control-узлы.
- Реализация сигналов и переходов.
- Обработка паузы и исключений.
- Тесты на мобильных устройствах.
QA:
- Проверить видимость/скрытие меню в разных ситуациях.
- Проверить локализацию длинных строк.
- Проверить взаимодействие клавиатуры/контроллера.
Локализатор:
- Собрать все строки в ресурс переводов.
- Проверить переносы и длины строк.
- Подготовить альтернативы для RTL, если нужно.
Сниппеты и шпаргалка
Переключение видимости меню паузы:
func toggle_pause():
if not $PauseMenu.is_visible_in_tree():
$PauseMenu.show()
get_tree().paused = true
else:
$PauseMenu.hide()
get_tree().paused = falseПодключение звука к кнопке (через сигнал pressed в инспекторе или кодом):
func _on_Button_pressed():
$ClickSound.play()Плавный переход при смене сцен (пример с корутиноподобным ожиданием):
func change_scene_with_fade(path):
$FadeAnimation.play("fade_out")
yield($FadeAnimation, "animation_finished")
get_tree().change_scene(path)Модель мышления и эвристики
- Держите глубину меню минимальной: пользователь должен сделать минимум кликов до старта игры.
- Обратная связь должна быть мгновенной: визуальный отклик и/или звук.
- Не блокируйте основной цикл игры без явного запроса (например, пауза через Esc).
- Локализация должна быть частью дизайна, а не поздней правкой.
Критерии приёмки (тестовые сценарии)
- При нажатии Play Game игра загружается и начинается сцена GameScene.tscn.
- При нажатии Exit приложение закрывается на целевой платформе.
- При нажатии Esc появляется меню паузы и игра останавливается.
- Кнопки в меню реагируют на клики/тачи/клавиатуру/контроллер.
- Тексты корректно отображаются после смены языка.
Альтернативные подходы
- Хранить все меню в одной сцене и переключать контейнеры вместо смены сцен. Это уменьшает накладные расходы на загрузку, но может усложнить структуру.
- Управлять меню через глобальный UI-менеджер (Singleton/Autoload) для единой точки переходов и звука.
- Использовать JSON/Tres‑данные для описания структуры меню и генерировать UI динамически — удобно для редактора уровней и быстрых правок.
Противопоказания и случаи, когда это не подходит
- Если игра очень простая (один экран), сложная система меню будет лишней и только усложнит поддержку.
- Для коротких джем‑игр лучше минимизировать UI и сосредоточиться на геймплее.
Резюме
Кастомные меню в Godot легко реализуются через сцены Control-узлов. Разделяйте стартовое меню, паузу и экран Game Over на отдельные сцены, используйте сигналы для взаимодействия и продумывайте локализацию заранее. Тестируйте меню на целевых устройствах и оптимизируйте анимации и звуковое сопровождение для плавного UX.
Важно: адаптируйте API‑вызовы под вашу версию Godot (в Godot 4 некоторые классы и методы отличаются).
Похожие материалы
Сброс и настройка ленты TikTok For You
Как исправить ошибки eed_ec.dll
Наложение календарей в Outlook: быстро и просто
Переустановить Windows Security в Windows 11
Как отключить голос на Roku