Проблема
В вашей игре нужно меню выбора уровня, где пользователь сможет выбрать один из вариантов в сетке.

Решение
Как показано в примере выше, мы создадим прокручиваемую сетку с уровнями, из которой игрок сможет выбирать. Начнем с отдельных ячеек уровней.
1: Ячейка уровня
Настройка узлов:
LevelBox: PanelContainer
Label
MarginContainer
TextureRect
TextureRect
используется для отображения иконки замка.Label
отображает номер уровня. Когда одно из них видно, другое скрывается.
Вы можете стилизовать эти элементы как вам нужно. Вот пример:

Установите для LevelBox
параметр Custom Minimum Size в Инспекторе. В примере используется (110, 110), но размер зависит от макета.
Теперь добавьте скрипт и подключите сигнал gui_input
.
@tool
extends PanelContainer
signal level_selected
@export var locked = true:
set = set_locked
@export var level_num = 1:
set = set_level
@onready var lock = $MarginContainer/Lock
@onready var label = $Label
func set_locked(value):
locked = value
if not is_inside_tree():
await ready
lock.visible = value
label.visible = not value
func set_level(value):
level_num = value
if not is_inside_tree():
await ready
label.text = str(level_num)
func _on_gui_input(event):
if locked:
return
if event is InputEventMouseButton and event.pressed:
level_selected.emit(level_num)
print("Clicked level ", level_num)
Используется @tool
, чтобы можно было изменять свойства в инспекторе и сразу видеть изменения без запуска сцены. Попробуйте переключить свойство Locked
и убедитесь, что иконка замка появляется и исчезает.
Так как в проекте пока нет реальных уровней, можно использовать print()
, чтобы проверить, что нажатия обрабатываются.
2: Сетка
Когда ячейка уровня готова, создайте новую сцену с GridContainer
. Добавьте в нее несколько экземпляров LevelBox
, задав значение Columns
. Вот пример с 6 колонками:


В этом примере параметры Theme Overrides / Constants / H Separation и V Separation установлены в 10.
Сохраните сцену как LevelGrid
. В меню будем использовать несколько таких экземпляров для отображения нужного количества уровней.
3: Экран меню
Проверь свои знания в нашем бесплатном ТЕСТЕ по Godot! Узнай, насколько хорошо ты его знаешь!
Теперь соберем финальное меню.
Вот базовый макет, который нам нужен:


Создадим его с такими узлами:
LevelMenu: MarginContainer
VBoxContainer
Title: Label
HBoxContainer
BackButton: TextureButton
ClipControl: Control
NextButton: TextureButton
Настроим свойства узлов:
- LevelMenu
- Theme Overrides / Constants / Margins: 20
- VBoxContainer
- Theme Overrides / Constants / Separation: 50
- Title
- Оформите шрифт по своему вкусу
- BackButton / NextButton
- Ignore Texture Size: On
- Stretch Mode: Keep Centered
- Layout / Container Sizing / Horizontal / Expand: On
- ClipControl
- Layout / Clip Contents: On
- Layout / Custom Minimum Size: (710, 350) (размер LevelGrid)
Узел ClipControl
содержит сетку. Если содержимое выходит за границы, оно обрезается. Это позволит создать горизонтально прокручиваемый список сеток. Добавьте HBoxContainer
(назовем его GridBox
) внутрь ClipControl
и разместите внутри 3 (или больше) LevelGrid
.
Установите Theme Overrides / Constants / Separation в 0.
Макет должен выглядеть примерно так (временно отключено Clip Contents
, чтобы показать структуру):


С включенной опцией Clip Contents
все три сетки присутствуют, но ClipControl
показывает только одну за раз.
Чтобы прокручивать меню, нужно сдвигать GridBox
на 710 пикселей влево или вправо.
110 (width of each LevelBox)
* 6 (grid columns)
+ 10 (grid spacing) * 5
== 710
Дополнительная информация
Можно было бы использоватьScrollContainer
, но он не подойдет, потому что:
- Нам не нужна плавная прокрутка.
- Не хотим, чтобы появлялась полоса прокрутки.
Добавьте скрипт к LevelMenu
и подключите сигналы pressed
для кнопок.
extends MarginContainer
var num_grids = 1
var current_grid = 1
var grid_width = 710
@onready var gridbox = $VBoxContainer/HBoxContainer/ClipControl/GridBox
func _ready():
# Number all the level boxes and unlock them
# Replace with your game's level/unlocks/etc.
# You can also connect the "level_selected" signals here
num_grids = gridbox.get_child_count()
for grid in gridbox.get_children():
for box in grid.get_children():
var num = box.get_position_in_parent() + 1 + 18 * grid.get_position_in_parent()
box.level_num = num
box.locked = false
func _on_BackButton_pressed():
if current_grid > 1:
current_grid -= 1
gridbox.rect_position.x += grid_width
func _on_NextButton_pressed():
if current_grid < num_grids:
current_grid += 1
gridbox.rect_position.x -= grid_width
Запустите сцену, нажимайте кнопки «Next» и «Back» и убедитесь, что прокрутка работает. При нажатии на ячейки уровней в консоли должно появляться сообщение.