控制器、遊戲手把與搖桿

Godot 內建支援數百種控制器型號。控制器在 Windows、macOS、Linux、Android、iOS 和 HTML5 上皆可使用。

請注意,像方向盤、方向舵踏板及 HOTAS 這類更專業的裝置測試較少,可能無法完全依預期運作。這些裝置的力回饋覆寫功能也尚未實作。如果你有這類裝置,歡迎直接 在 GitHub 上回報問題

在本指南中,你將學到:

  • 如何設計你的輸入邏輯以同時支援鍵盤與控制器輸入。

  • 控制器與鍵盤/滑鼠輸入行為上的差異。

  • 如何在 Godot 中排查控制器相關問題。

支援通用輸入

由於 Godot 的輸入動作系統,你可以同時支援鍵盤與控制器輸入,無需撰寫額外的分支程式碼。你應在「專案設定」中建立 輸入動作 ,這些動作可同時對應指定的鍵盤與控制器按鈕,避免在腳本中硬編碼按鍵或控制器按鈕。

詳細說明請參見 使用 InputEvent 頁面。

備註

與鍵盤輸入不同,若要支援滑鼠與控制器對同一動作(如第一人稱視角的環顧四周),這兩者必須分開處理,因此需要不同的程式邏輯。

我該用哪個 Input 單例的方法?

有三種取得類比輸入的方法:

  • 當有兩個軸(如搖桿或 WASD 移動)且希望兩軸合為一個輸入時,請用 Input.get_vector()

# `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
# This handles deadzone in a correct way for most use cases.
# The resulting deadzone will have a circular shape as it generally should.
var velocity = Input.get_vector("move_left", "move_right", "move_forward", "move_back")

# The line below is similar to `get_vector()`, except that it handles
# the deadzone in a less optimal way. The resulting deadzone will have
# a square-ish shape when it should ideally have a circular shape.
var velocity = Vector2(
        Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
        Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")
).limit_length(1.0)
  • 當你有單一軸可雙向移動(如飛行搖桿的油門),或要個別處理不同軸時,請用 Input.get_axis()

# `walk` will be a floating-point number between `-1.0` and `1.0`.
var walk = Input.get_axis("move_left", "move_right")

# The line above is a shorter form of:
var walk = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
  • 對於其他類型的類比輸入,如扳機鍵或僅單方向輸入,請用 Input.get_action_strength()

# `strength` will be a floating-point number between `0.0` and `1.0`.
var strength = Input.get_action_strength("accelerate")

對於非類比的數位/布林輸入(僅有「按下」或「未按」),例如控制器按鈕、滑鼠按鍵或鍵盤鍵,請用 Input.is_action_pressed()

# `jumping` will be a boolean with a value of `true` or `false`.
var jumping = Input.is_action_pressed("jump")

備註

若你想判斷某輸入是否在上一個影格中『剛剛』被按下,請用 Input.is_action_just_pressed() 取代 Input.is_action_pressed()。後者在持續按下時會一直回傳 true,而 Input.is_action_just_pressed() 僅會在按下後的單一影格回傳 true

振動

振動(也稱為*觸覺回饋*)能增強遊戲體驗。例如,在賽車遊戲中,可以用振動表現車輛行駛路面的不同,或在撞擊時產生強烈震動。

使用 Input 單例的 start_joy_vibration 方法可啟動手把振動。要提前停止(若啟動時未設定時長),則用 stop_joy_vibration

在行動裝置上,可用 vibrate_handheld 讓整台裝置振動(獨立於手把)。Android 匯出前需於預設設定中勾選 VIBRATE 權限。

備註

部分玩家可能會排斥振動。請務必在遊戲中提供調整強度或關閉振動的選項。

鍵盤/滑鼠與控制器輸入的差異

如果你習慣只處理鍵盤與滑鼠輸入,控制器在某些情境下的行為可能會讓你感到驚訝。

死區

與鍵盤和滑鼠不同,控制器的軸是*類比*輸入。類比輸入的好處是能提供 0.01.0 的任意強度,數位輸入則只能有 0.01.0。但缺點是,若沒有死區設計,類比軸的值永遠不會精確等於 0.0,而會維持在如 0.062 這樣的低值,這種現象稱為*漂移*,在老舊或有缺陷的手把上更明顯。

以賽車遊戲為例:有了類比輸入,我們能讓車輛緩慢轉向。但若無死區,車輛即使玩家沒碰搖桿,也會自己慢慢轉彎,因為軸向強度不會真的歸零。因此我們通常設定死區(如 0.2),忽略小於此強度的輸入。理想的死區要高到能排除漂移影響,但又不會太高而誤判玩家的真實操作。

Godot 內建死區系統來解決這個問題。預設值為 0.5,但你可以在「專案設定」的「輸入對應」分頁中,針對每個動作調整死區。對於 Input.get_vector(),可指定第 5 個參數為死區值,若未指定則會取所有動作的平均死區值。

「重複」事件

與鍵盤不同,長按手把的按鈕(如方向鍵)**不會**產生固定間隔的重複輸入事件(即「重複」事件),因為作業系統對控制器輸入本就不會傳送這類事件。

若你要讓控制器按鈕也產生重複事件,需用程式自行產生 InputEvent 物件,並定時用 Input.parse_input_event() 解析。你可以配合 Timer 節點達成此效果。

視窗焦點

與鍵盤不同,控制器輸入會被作業系統上**所有**視窗接收到,即使該視窗未獲得焦點。

這對於 第三方分割畫面功能 很有幫助,但也可能有負面影響:玩家在操作其他視窗時,仍可能誤觸並將控制器輸入傳送到正在執行的專案。

若希望在專案視窗未獲焦點時忽略控制器事件,請建立一個名為 Focus自動載入,並用以下腳本來檢查所有輸入:

# Focus.gd
extends Node

var focused := true

func _notification(what: int) -> void:
    match what:
        NOTIFICATION_APPLICATION_FOCUS_OUT:
            focused = false
        NOTIFICATION_APPLICATION_FOCUS_IN:
            focused = true


func input_is_action_pressed(action: StringName) -> bool:
    if focused:
        return Input.is_action_pressed(action)

    return false


func event_is_action_pressed(event: InputEvent, action: StringName) -> bool:
    if focused:
        return event.is_action_pressed(action)

    return false

然後,請用 Focus.input_is_action_pressed(action)``(其中 ``action 為輸入動作名稱)取代 Input.is_action_pressed(action)。同理,請用 Focus.event_is_action_pressed(event, action)``(其中 ``event 是 InputEvent 參照,action 是動作名稱)取代 event.is_action_pressed(action)

防止省電

與鍵盤與滑鼠不同,控制器輸入**不會**阻止作業系統進入睡眠或省電(如螢幕逾時自動關閉)。

因此 Godot 在專案執行時會自動啟用防止省電。如果你發現用手把遊玩時螢幕還是被關閉,請檢查「專案設定」裡 顯示 > 視窗 > 節能 > 保持螢幕開啟 是否已啟用。

在 Linux 上,防止省電機制需引擎能存取 D-Bus。若你在 Flatpak 沙盒下執行專案,請確認 D-Bus 已安裝且可用,因為沙盒限制預設會阻擋其存取。

疑難排解

也參考

你可以在 GitHub 上查看 控制器支援相關已知問題列表

Godot 無法辨識我的控制器。

首先,請確認你的控制器能被其他應用程式辨識。你可以用 Gamepad Tester 網站測試控制器有無被正確辨識。

在 Windows 上,Godot 一次僅支援最多 4 支控制器。這是因為 Godot 採用 XInput API,而此 API 最多只支援 4 支控制器。超過此數量的控制器將會被忽略。

我的控制器按鈕或軸對應錯誤。

首先,若你的控制器有韌體更新工具,請務必更新到最新版本。例如 Xbox One 和 Xbox Series 可用「Xbox 配件」App <https://www.microsoft.com/en-us/p/xbox-accessories/9nblggh30xj3> 進行韌體更新(僅支援 Windows,需有實體或虛擬機器加 USB 支援)。更新完後若以無線連線,請重新配對。

若仍有按鈕對應錯誤,可能是 Godot 所用 SDL 控制器資料庫(或 Godot 控制器資料庫 )的對應檔有誤。此時你需要為你的控制器製作自訂對應。

建立對應有多種方式,其中之一是使用 官方 Joypads 範例 的對應精靈。完成對應後,可在啟動 Godot 前設定 SDL_GAMECONTROLLERCONFIG 環境變數來測試:

export SDL_GAMECONTROLLERCONFIG="your:mapping:here"
./path/to/godot.x86_64

若要在非桌面平台測試對應,或欲隨專案附加額外控制器對應,請在腳本 _ready() 階段儘早呼叫 Input.add_joy_mapping() 以載入自訂對應。

確認自訂對應運作正常後,歡迎到 Godot 控制器資料庫 提交 Pull Request,讓下個 Godot 版本支援你的控制器。

我的控制器在某些平台可用,但在其他平台卻不可用。

Linux

若你用自行編譯的引擎執行檔,請確認已啟用 udev 支援(預設為開啟),不然在 SCons 編譯時加入 udev=no 會導致關閉。若用的是 Linux 發行版自帶的執行檔,也請確認其是否有啟用 udev。

控制器即使沒 udev 支援仍有機會運作,但穩定性較差,因為無法即時偵測熱插拔,只能定時輪詢檢查連接變化。

HTML5

HTML5 平台的控制器支援通常不如「原生」平台穩定,不同瀏覽器的相容性有很大差異。如果玩家的控制器無法使用,可能需要請他們嘗試更換瀏覽器。