Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

控制器、手柄和摇杆

Godot支持数百种控制器模型,这要归功于社区提供的 SDL游戏控制器数据库

控制器支持Windows、macOS、Linux、Android、iOS和HTML5。

请注意,诸如方向盘、方向盘踏板和 HOTAS 等更专业的设备测试较少,可能并不总是按照预期工作。目前尚未实现在这些设备上的力反馈覆盖。如果你有机会使用这些设备,请不要犹豫,在 GitHub 上报告错误。

在本指南中,你将学会:

  • 如何编写你的输入逻辑,从而支持键盘和控制器输入。

  • 控制器的行为如何与键盘/鼠标输入不同。

  • 解决 Godot 中控制器的问题。

支持通用导出

得益于 Godot 的输入动作系统,Godot 可以同时支持键盘和控制器输入,而不需要编写单独的代码路径。你不应该在脚本中对控制器的按键进行硬编码,应该在项目设置中创建输入动作,这些动作引用按键和控制器输入。

输入动作在 使用 InputEvent 页面上有详细解释。

备注

与键盘输入不同,支持鼠标和控制器输入的动作将需要不同的代码路径,例如在第一人称游戏中四处查看,因为这些必须被分开处理。

我应该使用哪个输入单例方法?

有3种方式可以以模拟感知的方式获得输入:

  • 当你有两个轴,如操纵杆或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()Input.is_action_pressed() 是只要输入处于按下的状态就会返回 true,而 Input.is_action_just_pressed() 只会在按下按钮后的一帧内返回 true

在3.4之前的Godot版本,如3.3, Input.get_vector()Input.get_axis() 不可用。只有 Input.get_action_strength()Input.is_action_pressed() 在Godot 3.3中可用。

振动

振动(也叫触觉反馈)可以用来提升游戏手感。比如在赛车游戏中,可以通过振动来体现车辆当前所处的路面,也可以在撞车时进行突然的振动。

请使用 Input 单例的 start_joy_vibration 方法开启游戏手柄的振动。要提前结束振动,请使用 stop_joy_vibration(尤其适用于启动时未指定时长的情况)。

在移动设备上,你还可以使用 vibrate_handheld 来振动设备本身(与游戏手柄的振动是分开的)。在 Android 上,这个功能需要在导出项目前启用 Android 导出预设的 VIBRATE 权限。

备注

振动可能造成某些玩家的不适。请确保在游戏中提供滑块,用来禁用振动或降低振动强度。

键盘/鼠标和控制器输入之间的差异

如果你习惯于处理键盘和鼠标输入,可能会对控制器处理特定情况的方式感到惊讶。

死区

与键盘和鼠标不同,控制器提供带有模拟输入的轴。模拟输入的好处是它们为动作提供了额外的灵活性。不像数字输入只能提供 0.01.0 的强度,模拟输入可以提供 0.01.0 之间的任何强度。缺点是没有死区系统,由于控制器的物理结构,模拟轴的强度永远不会等于 0.0。相反,它将徘徊在一个低值,如 0.062。这种现象被称为漂移,在旧的或有问题的控制器上会更加明显。

让我们把赛车游戏作为一个现实世界的例子。由于有了模拟输入,我们可以将汽车慢慢地转向一个或另一个方向。然而,如果没有死区系统,即使玩家不接触操纵杆,汽车也会自己慢慢转向。这是因为方向轴的强度在我们期望的时候不会等于 0.0。因为我们不希望我们的车在这种情况下自动转向,我们定义了一个“死区”值 0.2,它将忽略所有强度低于 0.2 的输入。一个理想的死区值是足够高的,可以忽略操纵杆漂移引起的输入,但又足够低,不会忽略玩家的实际输入。

Godot 提供了内置的死区系统来解决这个问题。默认值是 0.5,但你可以在“项目设置”的“输入映射”选项卡中针对具体的动作进行调整。Input.get_vector()可以在第五个参数中指定死区。如果没有指定,则会计算向量中的所有动作死区的平均值。

“回声”事件

与键盘输入不同,按住一个控制器按钮,如十字方向键,不会产生固定间隔的重复输入事件(也被称为“回声”事件)。这是因为操作系统首先不会为控制器输入发送“回声”事件。

如果你想让控制器按钮发送回声事件,你将不得不通过代码生成 InputEvent 对象,并使用 Input.parse_input_event() 定期解析它们。这可以在 Timer 节点的帮助下完成。

窗口焦点

与键盘输入不同,控制器的输入可以被操作系统中的所有窗口看到,包括未持有焦点的窗口。

While this is useful for third-party split screen functionality, it can also have adverse effects. Players may accidentally send controller inputs to the running project while interacting with another window.

If you wish to ignore events when the project window isn't focused, you will need to create an autoload called Focus with the following script and use it to check all your inputs:

# 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

Then, instead of using Input.is_action_pressed(action), use Focus.input_is_action_pressed(action) where action is the name of the input action. Also, instead of using event.is_action_pressed(action), use Focus.event_is_action_pressed(event, action) where event is an InputEvent reference and action is the name of the input action.

防止省电模式

Unlike keyboard and mouse input, controller inputs do not inhibit sleep and power saving measures (such as turning off the screen after a certain amount of time has passed).

To combat this, Godot enables power saving prevention by default when a project is running. If you notice the system is turning off its display when playing with a gamepad, check the value of Display > Window > Energy Saving > Keep Screen On in the Project Settings.

On Linux, power saving prevention requires the engine to be able to use D-Bus. Check whether D-Bus is installed and reachable if running the project within a Flatpak, as sandboxing restrictions may make this impossible by default.

故障排除

参见

你可以在 GitHub 上查看控制器支持的已知问题列表

Godot 无法识别我的控制器。

首先,检查你的控制器是否被其他应用程序识别。你可以使用 Gamepad Tester 网站来确认你的控制器被识别。

On Windows Godot only supports up to 4 controllers at a time. This is because Godot uses the XInput API, which is limited to supporting 4 controllers at once. Additional controllers above this limit are ignored by Godot.

我的控制器的按钮或轴映射不正确。

First, if your controller provides some kind of firmware update utility, make sure to run it to get the latest fixes from the manufacturer. For instance, Xbox One and Xbox Series controllers can have their firmware updated using the Xbox Accessories app. (This application only runs on Windows, so you have to use a Windows machine or a Windows virtual machine with USB support to update the controller's firmware.) After updating the controller's firmware, unpair the controller and pair it again with your PC if you are using the controller in wireless mode.

如果按钮存在映射错误,可能是由于来自 SDL 游戏控制器数据库的错误的映射。你可以在链接的仓库中提交拉取请求,为下一个 Godot 版本提供映射更新。

有很多方法来创建映射。一个选择是使用 官方Joypads演示 中的映射向导。一旦你有了控制器可工作的映射,你可以在运行Godot之前通过定义 SDL_GAMECONTROLLERCONFIG 环境变量来测试它:

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

要在非桌面平台上测试映射,或者用额外的控制器映射来分发你的项目,你可以通过调用 Input.add_joy_mapping() 尽早在脚本的 _ready() 函数中添加它们。

我的控制器在特定的平台上工作,但在另一个平台上却不能。

Linux

If you're using a self-compiled engine binary, make sure it was compiled with udev support. This is enabled by default, but it is possible to disable udev support by specifying udev=no on the SCons command line. If you're using an engine binary supplied by a Linux distribution, double-check whether it was compiled with udev support.

Controllers can still work without udev support, but it is less reliable as regular polling must be used to check for controllers being connected or disconnected during gameplay (hotplugging).

HTML5

与 "本地" 平台相比,HTML5 控制器的支持通常不太可靠。各个浏览器对控制器的支持质量往往相差甚远。因此,如果玩家无法使用他们的控制器,你可能不得不指示他们使用不同的浏览器。