Импортируйте проект lesson_05_ui_animation/project/ в Godot
В проекте:
scenes/main_menu.tscn — шаблон главного меню (скелет)scenes/level_select.tscn — шаблон экрана выбора уровняscenes/level_base.tscn — шаблон уровня с HUDscripts/game_manager.gd — глобальный менеджер (Autoload)Настройте Autoload:
res://scripts/game_manager.gd, Name: GameManagerЦель: создать экран главного меню с кнопками и анимацией появления.
Откройте scenes/main_menu.tscn. Структура уже создана:
MainMenu (Control)
├── CenterContainer
│ └── VBoxContainer
│ ├── TitleLabel ("Название игры")
│ ├── PlayButton ("Играть")
│ ├── AboutButton ("Об игре")
│ └── QuitButton ("Выход")
└── AboutPanel (PanelContainer, скрыт)
└── VBoxContainer
├── AboutLabel ("Это учебная игра...")
└── CloseButton ("Закрыть")
Откройте scripts/main_menu.gd и реализуйте:
extends Control
func _ready() -> void:
# TODO 1: Подключите сигнал pressed каждой кнопки:
# $CenterContainer/VBoxContainer/PlayButton.pressed.connect(_on_play)
# $CenterContainer/VBoxContainer/AboutButton.pressed.connect(_on_about)
# $CenterContainer/VBoxContainer/QuitButton.pressed.connect(_on_quit)
# $AboutPanel/VBoxContainer/CloseButton.pressed.connect(_on_about) # закрытие панели
# TODO 2: Анимация появления — заголовок плавно появляется
# var title := $CenterContainer/VBoxContainer/TitleLabel
# title.modulate.a = 0.0
# var tween := create_tween()
# tween.tween_property(title, "modulate:a", 1.0, 0.8)
pass
func _on_play() -> void:
# TODO 3: Перейти на экран выбора уровня
# get_tree().change_scene_to_file("res://scenes/level_select.tscn")
pass
func _on_about() -> void:
# TODO 4: Показать/скрыть панель "Об игре"
# AboutPanel уже есть в сцене (PanelContainer, скрыт по умолчанию)
# $AboutPanel.visible = not $AboutPanel.visible
pass
func _on_quit() -> void:
# TODO 5: Выйти из игры
# get_tree().quit()
pass
Цель: создать сетку кнопок уровней с индикацией прохождения.
Откройте scenes/level_select.tscn. Структура:
LevelSelect (Control)
├── TopBar (HBoxContainer)
│ ├── BackButton ("← Назад")
│ └── TitleLabel ("Выбор уровня")
└── CenterContainer
└── GridContainer (columns = 3)
Откройте scripts/level_select.gd и реализуйте:
extends Control
func _ready() -> void:
# Кнопка "Назад"
$TopBar/BackButton.pressed.connect(func(): GameManager.go_to_menu())
# Генерация кнопок уровней (цикл уже написан)
var grid := $CenterContainer/GridContainer
for i in range(1, GameManager.total_levels + 1):
var button := Button.new()
button.text = str(i)
button.custom_minimum_size = Vector2(100, 100)
# TODO 1: Если уровень пройден — покрасить кнопку в зелёный
# if GameManager.is_level_completed(i):
# button.modulate = Color.GREEN
# TODO 2: Подключить нажатие — переход к уровню
# var level_num := i # Важно: захватить значение в переменную!
# button.pressed.connect(func(): GameManager.go_to_level(level_num))
grid.add_child(button)
Примечание: Цикл создания кнопок уже написан. Вам нужно раскомментировать TODO 1 и TODO 2.
GridContainer:
columns = 3Цель: создать интерфейс уровня — номер, подсказка, кнопки управления.
Откройте scenes/level_base.tscn. Структура:
LevelBase (Node2D)
├── GameField (Node2D) ← игровое поле (здесь будут объекты)
└── UILayer (CanvasLayer)
├── TopBar (HBoxContainer)
│ ├── BackButton ("← Назад")
│ ├── LevelLabel ("Уровень X")
│ └── HintLabel ("Подсказка")
├── BottomBar (HBoxContainer)
│ ├── RunButton ("▶ Запуск")
│ └── ResetButton ("↺ Сброс")
└── WinPanel (PanelContainer) ← скрыта по умолчанию
└── VBoxContainer
├── WinLabel ("Уровень пройден!")
├── NextButton ("Следующий")
└── MenuButton ("К выбору уровней")
Откройте scripts/level_base.gd и реализуйте:
extends Node2D
## Номер уровня (читается из GameManager)
var level_number: int = 1
@export var hint_text: String = "Разместите объекты на поле"
func _ready() -> void:
# Номер уровня берём из глобального менеджера
level_number = GameManager.current_level
$UILayer/TopBar/LevelLabel.text = "Уровень %d" % level_number
$UILayer/TopBar/HintLabel.text = hint_text
$UILayer/WinPanel.visible = false
# TODO 1: Подключите кнопки
# $UILayer/TopBar/BackButton.pressed.connect(_on_back)
# $UILayer/BottomBar/RunButton.pressed.connect(_on_run)
# $UILayer/BottomBar/ResetButton.pressed.connect(_on_reset)
# $UILayer/WinPanel/VBoxContainer/NextButton.pressed.connect(_on_next_level)
# $UILayer/WinPanel/VBoxContainer/MenuButton.pressed.connect(_on_to_menu)
pass
func _on_back() -> void:
GameManager.go_to_level_select()
func _on_run() -> void:
# TODO 2: Запустить симуляцию (логика зависит от кейса)
# check_win_condition()
print("Запуск симуляции!")
func _on_reset() -> void:
# TODO 3: Сбросить уровень
# get_tree().reload_current_scene()
pass
## Проверить условие победы (переопределяется в конкретных уровнях)
func check_win_condition() -> void:
var win := false # Замените на реальную проверку
if win:
show_win_screen()
func show_win_screen() -> void:
# TODO 4: Показать панель победы с анимацией
# $UILayer/WinPanel.visible = true
# var tween := create_tween()
# $UILayer/WinPanel.modulate.a = 0.0
# tween.tween_property($UILayer/WinPanel, "modulate:a", 1.0, 0.5)
# GameManager.complete_level(level_number)
pass
func _on_next_level() -> void:
GameManager.go_to_level(level_number + 1)
func _on_to_menu() -> void:
GameManager.go_to_level_select()
Цель: создать эффект при прохождении уровня.
В level_base.tscn добавьте GPUParticles2D как дочерний к UILayer:
WinParticlesОбновите show_win_screen():
func show_win_screen() -> void:
$UILayer/WinPanel.visible = true
$UILayer/WinPanel.modulate.a = 0.0
# Запустить частицы
$UILayer/WinParticles.emitting = true
# Анимация появления надписи
var tween := create_tween()
tween.tween_property($UILayer/WinPanel, "modulate:a", 1.0, 0.5)
# Анимация увеличения текста "Уровень пройден!"
var label := $UILayer/WinPanel/VBoxContainer/WinLabel
label.scale = Vector2(0.5, 0.5)
label.pivot_offset = label.size / 2
tween.tween_property(label, "scale", Vector2(1.0, 1.0), 0.3) \
.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
GameManager.complete_level(level_number)
Цель: проверить полный цикл навигации.
Установите main_menu.tscn как главную сцену:
main_menu.tscnПроверьте полный цикл:
Главное меню → Выбор уровня → Уровень 1 → (победа) → Уровень 2 → Назад → Меню
Убедитесь, что GameManager сохраняет данные между сценами:
Добавьте эффект при наведении/нажатии на кнопку:
# Подключите в _ready() для каждой кнопки:
func _setup_button_animation(button: Button) -> void:
button.mouse_entered.connect(func():
var tween := create_tween()
tween.tween_property(button, "scale", Vector2(1.1, 1.1), 0.1)
)
button.mouse_exited.connect(func():
var tween := create_tween()
tween.tween_property(button, "scale", Vector2(1.0, 1.0), 0.1)
)
button.pivot_offset = button.size / 2