控制器、手柄和摇杆

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

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

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

在本指南中,您将学会:

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

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

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

支持通用导出

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

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

备注

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

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

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

  • 当你有两个轴,如操纵杆或WASD运动,并希望两个轴都表现为单一输入时,使用 Input.get_vector() :

GDScriptC#

  1. # `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
  2. # This handles deadzone in a correct way for most use cases.
  3. # The resulting deadzone will have a circular shape as it generally should.
  4. var velocity = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
  5. # The line below is similar to `get_vector()`, except that it handles
  6. # the deadzone in a less optimal way. The resulting deadzone will have
  7. # a square-ish shape when it should ideally have a circular shape.
  8. var velocity = Vector2(Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
  9. Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")).clamped(1)
  1. // `velocity` will be a Vector2 between `Vector2(-1.0, -1.0)` and `Vector2(1.0, 1.0)`.
  2. // This handles deadzone in a correct way for most use cases.
  3. // The resulting deadzone will have a circular shape as it generally should.
  4. Vector2 velocity = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
  5. // The line below is similar to `get_vector()`, except that it handles
  6. // the deadzone in a less optimal way. The resulting deadzone will have
  7. // a square-ish shape when it should ideally have a circular shape.
  8. Vector2 velocity = new Vector2(Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left"),
  9. Input.GetActionStrength("move_back") - Input.GetActionStrength("move_forward")).Clamped(1);
  • 当你有一个轴可以双向移动时,比如飞行操纵杆上的油门,或者你想单独处理不同的轴时,使用 Input.get_axis() :

GDScriptC#

  1. # `walk` will be a floating-point number between `-1.0` and `1.0`.
  2. var walk = Input.get_axis("move_left", "move_right")
  3. # The line above is a shorter form of:
  4. var walk = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
  1. // `walk` will be a floating-point number between `-1.0` and `1.0`.
  2. float walk = Input.GetAxis("move_left", "move_right");
  3. // The line above is a shorter form of:
  4. float walk = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left");
  • 对于其他类型的模拟输入,例如处理一个触发器或一次处理一个方向,使用 Input.get_action_strength() :

GDScriptC#

  1. # `strength` will be a floating-point number between `0.0` and `1.0`.
  2. var strength = Input.get_action_strength("accelerate")
  1. // `strength` will be a floating-point number between `0.0` and `1.0`.
  2. float strength = Input.GetActionStrength("accelerate");

对于非模拟数字/布尔输入(只有 “按下 “ 或 “未按下 “ 的值),如控制器按钮、鼠标按钮或键盘按键,使用 Input.is_action_pressed() :

GDScript

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

C#

  1. // `jumping` will be a boolean with a value of `true` or `false`.
  2. bool jumping = Input.IsActionPressed("jump");

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

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

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

死区

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

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

Godot 有一个内置的死区系统来解决这个问题。默认值是 0.2,但你可以在“项目设置”的“按键映射”选项卡中根据每个动作增加或减少它。对于 Input.get_vector(),可以指定死区,否则它将从向量中的所有动作计算出平均死区值。

“回声”事件

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

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

故障排除

参见

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

Godot 无法识别我的控制器。

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

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

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

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

Linux/macOSWindows (cmd)Windows (powershell)

  1. export SDL_GAMECONTROLLERCONFIG="your:mapping:here"
  2. ./path/to/godot.x86_64
  1. set SDL_GAMECONTROLLERCONFIG=your:mapping:here
  2. path\to\godot.exe
  1. $env:SDL_GAMECONTROLLERCONFIG="your:mapping:here"
  2. path\to\godot.exe

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

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

Linux

在 Godot 3.3 之前,官方的 Godot 可执行文件在编译时支持 udev,但自编译的可执行文件在编译时不支持 udev,除非在 SCons 命令行中传递 udev=yes。这使得控制器的热插拔支持在自编译的可执行文件中不可用。

HTML5

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

另外,请注意,在 Godot 3.3 及以后的版本中,对控制器的支持得到了很大的改善