Перемещайте и вращайте объект вокруг оси и продолжайте с того места, где он остановился - PullRequest
0 голосов
/ 27 января 2020

Я хотел бы переместить объект вокруг другого - как если бы один объект был дочерним по отношению к другому. Это GDscript - Godot Engine 3.2, но логика c должна быть очень похожа на другие игровые движки.

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

Первый GIF демонстрирует зеленый куб, начиная с позиции Vector3 (0, 4, 0) без поворота. Это прекрасно работает.

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

enter image description here

enter image description here

Код ниже не включает фактическое вращение синего куба (точки вращения), а только вычисления, необходимые для перемещения / вращения зеленого куба. Вращение синего куба не проблема. Кроме того, вращение просто ради демонстрации - в реальном сценарии синий куб тоже будет двигаться.

Вращения рассчитываются с использованием кватерниона, но это не является обязательным требованием.

extends Node

var _parent
var _child
var _subject
var _positionOffset: Vector3
var _rotationOffset: Quat

func _ready():
    _parent = get_parent()
    _child = $"/root/Main/Child"

func _input(event):
    if event is InputEventKey:
        if event.scancode == KEY_SPACE:
            if event.is_action_pressed("ui_accept") and  _child != null:
                _subject = _child
                _set_subject_offset(_parent.transform, _child.transform)
            elif event.is_action_released("ui_accept"):
                _subject = null

func _set_subject_offset(pivot: Transform, subject: Transform):
    _positionOffset = (pivot.origin - subject.origin) 
    _rotationOffset = pivot.basis.get_rotation_quat().inverse() * subject.basis.get_rotation_quat()

func _rotate_around_pivot(subject_position: Vector3, pivot: Vector3, subject_rotation: Quat):
    return pivot + (subject_rotation * (subject_position - pivot))

func _physics_process(_delta):
    if _subject == null: return

    var target_position = _parent.transform.origin - _positionOffset
    var target_rotation = _parent.transform.basis.get_rotation_quat() * _rotationOffset
    _subject.transform.origin = _rotate_around_pivot(target_position, _parent.transform.origin, target_rotation) 
    _subject.set_rotation(target_rotation.get_euler())

Я чувствую, что упускаю что-то очевидное.

1 Ответ

1 голос
/ 28 января 2020

Вы можете легко добиться этого, временно поместив объект в точку поворота, сохранив глобальное преобразование:

extends Spatial

onready var obj1: Spatial = $Object1
onready var obj2: Spatial = $Object2

func reparent(obj: Spatial, new_parent: Spatial):
    # Preserve the global transform while reparenting
    var old_trans := obj.global_transform
    obj.get_parent().remove_child(obj)
    new_parent.add_child(obj)
    obj.global_transform = old_trans

func _physics_process(delta: float):
    obj1.rotate_z(delta)

func _input(event: InputEvent):
    if event.is_action_pressed("ui_accept"):
        reparent(obj2, obj1)
    elif event.is_action_released("ui_accept"):
        reparent(obj2, self)

enter image description here

Если переопределение не Это невозможно, вместо этого вы можете перевести родительский элемент RemoteTransform в стержень, и этот pu sh преобразуется в объект, который вы хотите повернуть:

extends Spatial

onready var remote_trans: RemoteTransform = $RemoteTransform

func _process(delta):
    rotate_z(delta)

func attach(n: Node):
    # move the remote so the target maintains its transform
    remote_trans.global_transform = n.global_transform
    remote_trans.remote_path = n.get_path()

func detach():
    remote_trans.remote_path = ""
...