使用 RigidBody
什麼是剛體?
剛體是由物理引擎直接控制,以模擬實際物件行為的物件。為了定義剛體的形狀,必須指派一個或多個 Shape3D 物件。請注意,設定這些形狀的位置會影響剛體的質心。
如何控制剛體
剛體的行為可以透過設定其屬性來調整,例如質量和重量。你需要為剛體新增物理材質,以調整其摩擦力和彈力,並設定其是否具備吸收性或粗糙度。這些屬性可以在屬性檢查器中設定,或是透過程式碼調整。完整屬性列表及其效果,請參考 RigidBody3D 與 PhysicsMaterial 。
依照你的需求,有多種方式可以控制剛體的移動。
如果你只需要設定剛體一次,例如設定其初始位置,可以使用 Node3D 節點所提供的方法,例如 set_global_transform() 或 look_at()。但這些方法不能在每一幀都呼叫,否則物理引擎將無法正確模擬剛體的狀態。舉例來說,假設你想讓一個剛體旋轉並指向另一個物件,一個常見錯誤是每幀都使用 look_at(),這會破壞物理模擬。以下我們會示範正確的實作方式。
無法使用 set_global_transform() 或 look_at() 方法並不代表你就不能完全控制剛體。你可以透過 _integrate_forces() 回呼函式來控制剛體。在這個方法中,你可以加上*力*、施加*衝量*,或設定*速度*,來達到你想要的任何移動效果。
「look at」方法
如上所述,使用 Node3D 的 look_at() 方法不能在每一幀用來追蹤目標。這裡提供一個自訂的 look_follow() 方法,適用於剛體:
extends RigidBody3D
var speed: float = 0.1
func look_follow(state: PhysicsDirectBodyState3D, current_transform: Transform3D, target_position: Vector3) -> void:
var forward_local_axis: Vector3 = Vector3(1, 0, 0)
var forward_dir: Vector3 = (current_transform.basis * forward_local_axis).normalized()
var target_dir: Vector3 = (target_position - current_transform.origin).normalized()
var local_speed: float = clampf(speed, 0, acos(forward_dir.dot(target_dir)))
if forward_dir.dot(target_dir) > 1e-4:
state.angular_velocity = local_speed * forward_dir.cross(target_dir) / state.step
func _integrate_forces(state):
var target_position = $my_target_node3d_node.global_transform.origin
look_follow(state, global_transform, target_position)
using Godot;
public partial class MyRigidBody3D : RigidBody3D
{
private float _speed = 0.1f;
private void LookFollow(PhysicsDirectBodyState3D state, Transform3D currentTransform, Vector3 targetPosition)
{
Vector3 forwardLocalAxis = new Vector3(1, 0, 0);
Vector3 forwardDir = (currentTransform.Basis * forwardLocalAxis).Normalized();
Vector3 targetDir = (targetPosition - currentTransform.Origin).Normalized();
float localSpeed = Mathf.Clamp(_speed, 0.0f, Mathf.Acos(forwardDir.Dot(targetDir)));
if (forwardDir.Dot(targetDir) > 1e-4)
{
state.AngularVelocity = forwardDir.Cross(targetDir) * localSpeed / state.Step;
}
}
public override void _IntegrateForces(PhysicsDirectBodyState3D state)
{
Vector3 targetPosition = GetNode<Node3D>("MyTargetNode3DNode").GlobalTransform.Origin;
LookFollow(state, GlobalTransform, targetPosition);
}
}
這個方法會利用剛體的 angular_velocity 屬性來旋轉剛體。旋轉軸是由當前前方方向與目標方向的向量叉積所決定。clamp 則用來避免旋轉量超過所需的目標方向,因為實際所需的旋轉量是由兩向量的點積再取反餘弦得出。這個方法也可以搭配 axis_lock_angular_* 屬性一起使用。如果需要更精確的控制,可能需要依賴 Quaternion 等方案,如 使用 3D 變換 所討論。