Проблема
Вы хотите захватывать и перемещать твердые тела с помощью мыши.
Решение
Работа с твердыми телами может быть непростой. Движением таких тел управляет физический движок Godot, и вмешательство в этот процесс часто приводит к неожиданным результатам. Ключевой момент — использование свойства mode
тела. Это применимо как в 2D, так и в 3D.
Настройка тела
Начнем с создания объекта твердого тела, добавив Sprite2D
и CollisionShape2D
. При необходимости можно добавить PhysicsMaterial
, чтобы задать свойства отскока и трения.
Мы будем использовать свойство freeze
твердого тела, чтобы временно вывести его из-под контроля физического движка во время перетаскивания. Поскольку мы хотим, чтобы тело оставалось подвижным, нужно установить Freeze Mode
в значение Kinematic
вместо стандартного Static
.
Поместите тело в группу с именем pickable
. Это позволит использовать несколько экземпляров перетаскиваемых объектов в основной сцене. Присоедините скрипт к телу и подключите его сигнал _input_event
.
extends RigidBody2D
signal clicked
var held = false
func _on_input_event(viewport, event, shape_idx):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
print("clicked")
clicked.emit(self)
Мы будем отправлять сигнал при обнаружении клика мышью, включая ссылку на тело. Поскольку тел может быть много, основная сцена будет управлять тем, можно ли перетаскивать тело или если уже есть тело в состоянии удержания.
Если тело перетаскивается, мы обновляем его позицию, чтобы оно следовало за курсором мыши.
Проверь свои знания в нашем бесплатном ТЕСТЕ по Godot! Узнай, насколько хорошо ты его знаешь!
func _physics_process(delta):
if held:
global_transform.origin = get_global_mouse_position()
Наконец, вот две функции, которые вызываются при поднятии и отпускании тела. Установка freeze
в true
исключает тело из обработки физическим движком. Обратите внимание, что другие объекты все еще могут сталкиваться с ним. Если это не нужно, можно отключить collision_layer
и/или collision_mask
. Не забудьте включить их обратно при отпускании.
func pickup():
if held:
return
freeze = true
held = true
func drop(impulse=Vector2.ZERO):
if held:
freeze = false
apply_central_impulse(impulse)
held = false
В функции отпускания, после возврата freeze
в false
, тело снова переходит под контроль физического движка. Передавая необязательное значение импульса, мы можем добавить возможность «бросать» объект при отпускании.
Основная сцена
Создайте основную сцену с несколькими статичными препятствиями или TileMap
и добавьте несколько экземпляров перетаскиваемого тела.
Вот скрипт для основной сцены. Начнем с подключения сигнала clicked
для всех перетаскиваемых тел в сцене.
extends Node2D
var held_object = null
func _ready():
for node in get_tree().get_nodes_in_group("pickable"):
node.clicked.connect(_on_pickable_clicked)
Затем идет функция, к которой подключается сигнал. Эта функция устанавливает held_object
, чтобы отслеживать текущее перетаскиваемое тело, и вызывает метод pickup()
тела.
func _on_pickable_clicked(object):
if !held_object:
object.pickup()
held_object = object
Наконец, при отпускании мыши во время перетаскивания выполняем обратные действия.
func _unhandled_input(event):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if held_object and !event.pressed:
held_object.drop(Input.get_last_mouse_velocity())
held_object = null
Обратите внимание на использование get_last_mouse_velocity()
для передачи импульса объекту — будьте осторожны! Вы можете случайно запустить тела на высокой скорости, особенно если у них небольшая масса. Рекомендуется масштабировать это значение до разумного и ограничить его с помощью clamp()
. Экспериментируйте, чтобы найти подходящие значения.