使用欧拉角在 Godot (GDScript) 中进行 3D 旋转
3D rotation in Godot (GDScript) using Euler angles
我正在尝试根据显示如何在 Unity 中执行此操作的 this 视频为模拟游戏创建 3D 相机。当 t运行将代码转换为 GDScript 时,我 运行 遇到了一个问题:我无法让相机旋转 。
这是我对视频中脚本旋转部分的 GDScript 解释(时间戳:8:50-10:10):
export(float) var rotation_amount = 1
export(Quat) var new_rotation
func _ready():
new_rotation = rotation
func _process(delta):
handle_movement_input(delta)
func handle_movement_input(delta):
if(Input.is_action_pressed("cam_rotate_left")):
new_rotation *= Quat(Vector3.UP * rotation_amount).get_euler()
if(Input.is_action_pressed("cam_rotate_right")):
new_rotation *= Quat(Vector3.UP * -rotation_amount).get_euler()
rotation = rotation.linear_interpolate(new_rotation, delta * movement_time)
类型
您声称 new_rotation
是 Quat
(export(Quat)
)。但是您不仅立即覆盖它(在 _ready
中),您还用 Vector3
覆盖它(rotation
是代表欧拉角的 Vector3
):
func _ready():
new_rotation = rotation
如果你创建了一个类型化的导出变量,Godot 会告诉你这个问题:
export var new_rotation:Quat
是的,GDScript 有类型变量。使用它们。
角度表示法
如果您想使用四元数(如视频中所示),您可以像这样获得旋转 Quat
:
var new_rotation:Quat
func _ready():
new_rotation = transform.basis.get_rotation_quat()
然后你可以通过乘法组合四元数,用slerp
插值,最后用get_euler()
顺便说一句,当您创建一个 Quat
并传递一个 Vector3
时,它被理解为欧拉角。这相当于 Unity 的 Quaternion.Euler
(他们在视频中使用)。此外,调用 to_euler
会将 Quat
转换为欧拉角,这使得使用 Quat
毫无意义,因为它只是从欧拉角创建的(而且根本不是他们在视频中所做的) .
如果您想使用欧拉角,您应该将它们相加而不是相乘。
但是,由于您只关心垂直方向的旋转,我在这里告诉您:使用 float
。把事情简单化。关键你把代码放在哪里。
插值注意事项
Vector3.linear_interpolate
(或Quat.slerp
或lerp
)的权重是一个介于0和1之间的值。如果为0,则得到原始值。如果它是 1,您将获得新值。
添加插值将使运动平滑,但也会给你一个 deceleration/slowing 向下的效果。所以你失去了敏锐的控制。
如果平滑旋转并急剧停止,请将角度乘以增量和一些缩放常数。也就是说,不是这样做:Quat(Vector3.UP * rotation_amount)
,而是这样做:Quat(Vector3.UP * rotation_amount * delta)
。并且不做插值。
你把代码放在哪里?
如果您在 Camera
节点中编写该代码,它将无法工作。因为 rotation
改变了相机的观看方向。而这正是我们不想要的。相反,我们想围绕 space.
中的某个其他点旋转
因此,在场景树中设置:
Pivot:Spatial
└ Camera
您可以将 Pivot
(一个 Spatial
节点)放在地面上,然后让 Camera
从空中观察它。而你把旋转逻辑放在Pivot
中。由于 Pivot
仅负责该旋转,因此您可以使用 float
作为角度。而生活是轻松简单的(用[=33=]插值float
)。
能不能单节点做?是的。我有一个过度设计的解决方案,完全可以做到这一点 elsewhere(包括解释为什么它不是最好的主意)。
我正在尝试根据显示如何在 Unity 中执行此操作的 this 视频为模拟游戏创建 3D 相机。当 t运行将代码转换为 GDScript 时,我 运行 遇到了一个问题:我无法让相机旋转 。 这是我对视频中脚本旋转部分的 GDScript 解释(时间戳:8:50-10:10):
export(float) var rotation_amount = 1
export(Quat) var new_rotation
func _ready():
new_rotation = rotation
func _process(delta):
handle_movement_input(delta)
func handle_movement_input(delta):
if(Input.is_action_pressed("cam_rotate_left")):
new_rotation *= Quat(Vector3.UP * rotation_amount).get_euler()
if(Input.is_action_pressed("cam_rotate_right")):
new_rotation *= Quat(Vector3.UP * -rotation_amount).get_euler()
rotation = rotation.linear_interpolate(new_rotation, delta * movement_time)
类型
您声称 new_rotation
是 Quat
(export(Quat)
)。但是您不仅立即覆盖它(在 _ready
中),您还用 Vector3
覆盖它(rotation
是代表欧拉角的 Vector3
):
func _ready():
new_rotation = rotation
如果你创建了一个类型化的导出变量,Godot 会告诉你这个问题:
export var new_rotation:Quat
是的,GDScript 有类型变量。使用它们。
角度表示法
如果您想使用四元数(如视频中所示),您可以像这样获得旋转 Quat
:
var new_rotation:Quat
func _ready():
new_rotation = transform.basis.get_rotation_quat()
然后你可以通过乘法组合四元数,用slerp
插值,最后用get_euler()
顺便说一句,当您创建一个 Quat
并传递一个 Vector3
时,它被理解为欧拉角。这相当于 Unity 的 Quaternion.Euler
(他们在视频中使用)。此外,调用 to_euler
会将 Quat
转换为欧拉角,这使得使用 Quat
毫无意义,因为它只是从欧拉角创建的(而且根本不是他们在视频中所做的) .
如果您想使用欧拉角,您应该将它们相加而不是相乘。
但是,由于您只关心垂直方向的旋转,我在这里告诉您:使用 float
。把事情简单化。关键你把代码放在哪里。
插值注意事项
Vector3.linear_interpolate
(或Quat.slerp
或lerp
)的权重是一个介于0和1之间的值。如果为0,则得到原始值。如果它是 1,您将获得新值。
添加插值将使运动平滑,但也会给你一个 deceleration/slowing 向下的效果。所以你失去了敏锐的控制。
如果平滑旋转并急剧停止,请将角度乘以增量和一些缩放常数。也就是说,不是这样做:Quat(Vector3.UP * rotation_amount)
,而是这样做:Quat(Vector3.UP * rotation_amount * delta)
。并且不做插值。
你把代码放在哪里?
如果您在 Camera
节点中编写该代码,它将无法工作。因为 rotation
改变了相机的观看方向。而这正是我们不想要的。相反,我们想围绕 space.
因此,在场景树中设置:
Pivot:Spatial
└ Camera
您可以将 Pivot
(一个 Spatial
节点)放在地面上,然后让 Camera
从空中观察它。而你把旋转逻辑放在Pivot
中。由于 Pivot
仅负责该旋转,因此您可以使用 float
作为角度。而生活是轻松简单的(用[=33=]插值float
)。
能不能单节点做?是的。我有一个过度设计的解决方案,完全可以做到这一点 elsewhere(包括解释为什么它不是最好的主意)。