Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

帶有彈簧臂的第三人稱攝影機

前言

在 3D 遊戲中,常見的第三人稱攝影機會跟隨並繞著玩家角色或載具等物件旋轉。

在 Godot 中,可以將 Camera3D 設為某個節點的子節點來達成這個效果。不過,如果只這樣做,攝影機會穿透場景幾何並導致畫面消失。

這時就可以使用 SpringArm3D 節點來解決這個問題。

什麼是彈簧臂?

彈簧臂有兩個主要組件會影響其行為。

彈簧臂的「長度」是用來檢查碰撞時,從其全域位置往外偵測的距離:

../../_images/spring_arm_position_length.webp

彈簧臂的「形狀」則是用來檢查碰撞的碰撞體。彈簧臂會將這個形狀從其原點往長度方向掃描偵測。

../../_images/spring_arm_shape.webp

彈簧臂會嘗試將它的所有子節點維持在長度的末端。如果碰撞形狀偵測到碰撞,子節點就會被放置在碰撞點上或附近:

../../_images/spring_arm_children.webp

彈簧臂搭配攝影機

當攝影機作為彈簧臂的子節點時,會使用一個代表攝影機的金字塔形狀作為碰撞形狀。

這個金字塔代表攝影機的**近裁剪面**:

../../_images/spring_arm_camera_shape.webp

備註

如果彈簧臂已設定特定的碰撞形狀,就會**永遠**使用該形狀。

攝影機的形狀只有在攝影機是彈簧臂的**直接子節點**時才會被使用。

如果沒有指定任何形狀且攝影機也不是直接子節點,彈簧臂會退回使用射線檢測(Ray Cast),這對攝影機碰撞而言不精確,並不推薦使用。

每個物理處理循環,彈簧臂都會進行一次移動碰撞檢查以偵測是否有碰撞發生:

../../_images/spring_arm_camera_motion_cast.webp

當碰撞形狀撞到物件時,攝影機會被放置在碰撞點或其附近:

../../_images/spring_arm_camera_collision.webp

設定彈簧臂與攝影機

我們來在平台遊戲範例中加上彈簧臂攝影機。

備註

你可以從 GitHub資源庫 下載 Platformer 3D 範例專案。

通常在製作第三人稱攝影機時,會將三個節點設為玩家(或其他要跟隨物件)的子節點:

  • Node3D (攝影機的「旋轉樞紐點」)

    • SpringArm3D

      • Camera3D

打開 player/player.tscn 場景。將這三個節點設為玩家的子節點,並給它們設定獨特的名稱,好讓我們在腳本中能正確取得。記得要刪除原本的攝影機節點!

../../_images/spring_arm_editor_setup.webp

把旋轉樞紐點在 Y 軸上往上移動 2,避免攝影機位於地面上:

../../_images/spring_arm_pivot_setup.webp

將彈簧臂長度設為 3,讓攝影機位於角色後方:

../../_images/spring_arm_length_setup.webp

備註

彈簧臂的 Shape (形狀)欄位保持為 <empty> ,這樣就會使用攝影機的金字塔形狀作為碰撞體。

你也可以嘗試其他形狀——例如球體,因為它能更平滑地沿著邊緣滑動,是常見的選擇。

player/player.gd 檔案開頭,透過獨特的名稱取得攝影機與樞紐點節點:

player/player.gd
# Comment out this existing camera line.
# @onready var _camera := $Target/Camera3D as Camera3D

@onready var _camera := %Camera3D as Camera3D
@onready var _camera_pivot := %CameraPivot as Node3D

新增 _unhandled_input 函式來檢查攝影機移動輸入,並根據輸入旋轉樞紐點:

player/player.gd
@export_range(0.0, 1.0) var mouse_sensitivity = 0.01
@export var tilt_limit = deg_to_rad(75)


func _unhandled_input(event: InputEvent) -> void:
    # Mouselook implemented using `screen_relative` for resolution-independent sensitivity.
    if event is InputEventMouseMotion:
        _camera_pivot.rotation.x -= event.screen_relative.y * mouse_sensitivity
        # Prevent the camera from rotating too far up or down.
        _camera_pivot.rotation.x = clampf(_camera_pivot.rotation.x, -tilt_limit, tilt_limit)
        _camera_pivot.rotation.y += -event.screen_relative.x * mouse_sensitivity

旋轉樞紐點時,彈簧臂也會跟著旋轉,進而改變攝影機的位置。執行遊戲後,你可以看到滑鼠移動會讓攝影機繞著角色旋轉。如果攝影機碰到牆壁,也會正確產生碰撞效果。