围绕枢轴移动和旋转对象并从它停止的地方继续

Move and rotate object around pivot and resume from where it stopped

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

只要我按住空格键,绿色立方体就会跟随蓝色立方体旋转。

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

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

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

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

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

我觉得我遗漏了一些明显的东西。

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

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

repost, Thank You path9263, answered Jun 4, 2019 by path9263 (134 points)

func rotate_around(obj:Spatial, point:Vector3, axis:Vector3, phi:float):
    # https://godotengine.org/qa/45609/how-do-you-rotate-spatial-node-around-axis-given-point-space?show=45970#a45970
    obj.global_translate(-point)
    obj.transform = obj.transform.rotated(axis, phi)
    obj.global_translate(point)

以下功能不起作用

func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
    # #4: busted, no rotation
    # 
    # Preserve the global transform while reparenting
    var pivot_spatial = Spatial.new()
    pivot_spatial.translation = pivot_point
    var parent_orig = transform_me.get_parent()
    var transform_orig = transform_me.global_transform
    parent_orig.remove_child(transform_me)
    pivot_spatial.add_child(transform_me)
    transform_me.global_transform = transform_orig
    
    pivot_spatial.rotate(axis, phi)
    
    var transform_rotated = transform_me.global_transform
    pivot_spatial.remove_child(transform_me)
    parent_orig.add_child(transform_me)
    transform_me.global_transform = transform_orig
func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
    # busted, no rotate
    # 
    var remote_trans : RemoteTransform = RemoteTransform.new()
    remote_trans.global_transform = transform_me.global_transform
    remote_trans.remote_path = transform_me.get_path()
    remote_trans.rotate(axis, phi)
    remote_trans.remote_path = ""

func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
    # busted, local rotate
    # https://godotforums.org/discussion/comment/43335/#Comment_43335
    var gto_orig = transform_me.global_transform.origin
    transform_me.global_transform.origin = pivot_point
    transform_me.rotate(axis, phi)
    transform_me.global_transform.origin = gto_orig

func rotate_around_point(transform_me:Spatial, pivot_point:Vector3, axis:Vector3, phi:float):
    # busted, no rotate
    # https://godotengine.org/qa/34248/rotate-around-a-fixed-point-in-3d-space?show=38928#a38928
    var start_position : Vector3 = transform_me.translation
    var pivot_radius : Vector3 = start_position - pivot_point
    var pivot_transform : Transform = Transform(transform.basis, pivot_point)
    var transform = pivot_transform.rotated(axis, phi).translated(pivot_radius)
    transform.xform(transform_me)