控制器、游戏板和操纵杆

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

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

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

在本指南中, 您将学会:

  • 如何编写你的输入逻辑以支持键盘和控制器输入.

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

  • 解决Godot中控制器的问题

支持通用导出

由于Godot的输入动作系统,Godot使得支持键盘和控制器输入成为可能,而不需要编写单独的代码路径。你应该在项目设置中创建 输入动作 ,然后引用指定的按键和控制器输入,而不是在你的脚本中硬编码按键或控制器按钮。

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

注解

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

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

There are 3 ways to get input in an analog-aware way:

  • 当你有两个轴,如操纵杆或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")).clamped(1)
// `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.
Vector2 velocity = Input.GetVector("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.
Vector2 velocity = new Vector2(Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left"),
            Input.GetActionStrength("move_back") - Input.GetActionStrength("move_forward")).Clamped(1);
  • 当你有一个轴可以双向移动时,比如飞行操纵杆上的油门,或者你想单独处理不同的轴时,使用 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")
// `walk` will be a floating-point number between `-1.0` and `1.0`.
float walk = Input.GetAxis("move_left", "move_right");

// The line above is a shorter form of:
float walk = Input.GetActionStrength("move_right") - Input.GetActionStrength("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")
// `strength` will be a floating-point number between `0.0` and `1.0`.
float strength = Input.GetActionStrength("accelerate");

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

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

In Godot versions before 3.4, such as 3.3, Input.get_vector() and Input.get_axis() aren't available. Only Input.get_action_strength() and Input.is_action_pressed() are available in 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() ,可以指定死区,否则它将从向量中的所有动作计算出平均死区值。

"Echo" 事件

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

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

故障排除

参见

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

Godot无法识别我的控制器。

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

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

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

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

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

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

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

macOS

控制器目前只支持基于x86的Mac电脑。这意味着控制器不能在采用ARM处理器的Mac上工作,如苹果M1。

Linux

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

HTML5

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

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