Меню выбора уровня

Проблема

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

Решение

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

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: Экран меню

Теперь соберем финальное меню.

Вот базовый макет, который нам нужен:

Создадим его с такими узлами:

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» и убедитесь, что прокрутка работает. При нажатии на ячейки уровней в консоли должно появляться сообщение.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

5 × два =

Прокрутить вверх