Круговое всплывающее меню

Проблема

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

Решение

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

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

Настройка узлов:

В качестве корневого узла используется TextureButton. Это кнопка, с помощью которой меню открывается и закрывается.

Control с кнопками — это контейнер, куда добавляются нужные элементы. Чтобы этот узел не мешал кликам, установите его параметр Mouse/Filter в значение «Ignore».

В примере используется 9 кнопок из рецепта Кнопка кулдауна.

Разбор кода

extends TextureButton
class_name RadialMenuButton

export var radius = 120
export var speed = 0.25

var num
var active = false

Переменные:

  • radius — определяет размер меню (радиус круга).
  • speed — отвечает за скорость анимации (чем меньше значение, тем быстрее).
  • num — хранит количество кнопок.
  • active — флаг, указывающий, открыто ли меню.
func _ready():
    $Buttons.hide()
    num = $Buttons.get_child_count()
    for b in $Buttons.get_children():
        b.position = position

В функции _ready() кнопки меню скрываются, а их позиции устанавливаются на место главной кнопки.

Далее подключаем сигнал pressed к главной кнопке.

func _on_pressed():
    disabled = true
    if active:
        hide_menu()
    else:
        show_menu()

При нажатии меню открывается или закрывается. Кнопка временно отключается, чтобы избежать повторного запуска анимации.

func _on_tween_finished():
    disabled = false
    if not active:
        $Buttons.hide()

Когда анимация завершена, состояние active переключается, а кнопка снова становится активной.

Функция show_menu()

func show_menu():
    $Buttons.show()
    var spacing = TAU / num
    active = true
    var tw = create_tween().set_parallel()
    tw.finished.connect(_on_tween_finished)
    for b in $Buttons.get_children():
        # Subtract PI/2 to align the first button  to the top
        var a = spacing * b.get_position_in_parent() - PI / 2
        var dest = Vector2(radius, 0).rotated(a)
        tw.tween_property(b, "position", dest, speed).from(Vector2.ZERO).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
        tw.tween_property(b, "scale", Vector2.ONE, speed).from(Vector2(0.5, 0.5)).set_trans(Tween.TRANS_LINEAR)

Вычисляется угол между кнопками (чтобы они равномерно располагались). Затем для каждой кнопки определяется конечная позиция (с учетом угла и радиуса). Для плавного эффекта изменяются свойства position и scale.

Функция hide_menu() выполняет обратную задачу — убирает кнопки обратно.

func hide_menu():
    active = false
    var tw = create_tween().set_parallel()
    tw.finished.connect(_on_tween_finished)
    for b in $Buttons.get_children():
        tw.tween_property(b, "position", Vector2.ZERO, speed).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN)
        tw.tween_property(b, "scale", Vector2(0.5, 0.5), speed).set_trans(Tween.TRANS_LINEAR)

Вот итоговый результат:

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

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

восемнадцать − 9 =

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