Проблема
Вам нужно радиальное меню — круг кнопок, который появляется для выбора нужного варианта.
Решение
Круговое всплывающее меню используются во многих играх для быстрого доступа к различным действиям. Например, при нажатии на 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
Переменные:
Проверь свои знания в нашем бесплатном ТЕСТЕ по Godot! Узнай, насколько хорошо ты его знаешь!
- 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)
Вот итоговый результат:
