首页 > 解决方案 > 围绕枢轴移动和旋转对象并从停止的位置恢复

问题描述

我想围绕另一个对象移动一个对象 - 就好像一个对象是另一个对象的孩子一样。这是GDscript - Godot Engine 3.2,但逻辑应该与其他游戏引擎非常相似。

每当我按住空格键时,绿色立方体都会跟随蓝色立方体旋转。

第一个 GIF 演示了从 Vector3(0, 4, 0) 位置开始的绿色立方体,没有应用任何旋转。这工作得很好。

在第二个 GIF 中,我反复按住和释放空格键。我希望绿色立方体从它离开的地方继续,但是它“跳”到一个新的位置并从那里继续。

在此处输入图像描述

在此处输入图像描述

下面的代码不包括蓝色立方体(枢轴点)的实际旋转,而只包括移动/旋转绿色立方体所需的计算。蓝色立方体的旋转不是问题。此外,旋转只是为了演示 - 在实际场景中,蓝色立方体也会移动。

旋转是使用四元数计算的,但这不是必需的。

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())

我觉得我错过了一些明显的东西。

标签: mathquaternionsgame-developmenteuler-anglesgodot

解决方案


您可以通过临时将主体设置为轴心点来轻松实现此目的,同时保留全局变换:

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)

在此处输入图像描述

如果重新设置不可行,您可以改为将RemoteTransform设置为枢轴,然后将其转换推送到要旋转的对象:

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 = ""

推荐阅读