Controllers, gamepads, and joysticks

Godot supports hundreds of controller models thanks to the community-sourced SDL game controller database.

Controllers are supported on Windows, macOS, Linux, Android, iOS, and HTML5.

Note that more specialized devices such as steering wheels, rudder pedals and HOTAS are less tested and may not always work as expected. Overriding force feedback for those devices is also not implemented yet. If you have access to one of those devices, don't hesitate to report bugs on GitHub.

В этом руководстве вы узнаете:

  • How to write your input logic to support both keyboard and controller inputs.

  • How controllers can behave differently from keyboard/mouse input.

  • Troubleshooting issues with controllers in Godot.

Supporting universal input

Thanks to Godot's input action system, Godot makes it possible to support both keyboard and controller input without having to write separate code paths. Instead of hardcoding keys or controller buttons in your scripts, you should create input actions in the Project Settings which will then refer to specified key and controller inputs.

Input actions are explained in detail on the Использование InputEvent page.

Примечание

Unlike keyboard input, supporting both mouse and controller input for an action (such as looking around in a first-person game) will require different code paths since these have to be handled separately.

Which Input singleton method should I use?

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

  • When you have two axes (such as joystick or WASD movement) and want both axes to behave as a single input, use 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.0)
  • When you have one axis that can go both ways (such as a throttle on a flight stick), or when you want to handle separate axes individually, use 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")
  • For other types of analog input, such as handling a trigger or handling one direction at a time, use Input.get_action_strength():

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

For non-analog digital/boolean input (only "pressed" or "not pressed" values), such as controller buttons, mouse buttons or keyboard keys, use 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.

Differences between keyboard/mouse and controller input

If you're used to handling keyboard and mouse input, you may be surprised by how controllers handle specific situations.

Dead zone

В отличие от клавиатур и мышей, контроллеры предлагают оси с аналоговыми входами. Преимущество аналоговых входов в том, что они обеспечивают дополнительную гибкость действий. В отличие от цифровых входов, которые могут обеспечить силу только 0.0 и 1.0, аналоговый вход может обеспечить любую силу между 0.0 и 1.0. Недостатком является то, что без системы мертвых зон сила аналоговой оси никогда не будет равна 0.0 из-за того, как физически построен контроллер. Вместо этого она будет задерживаться на низком значении, таком как 0.062. Это явление известно как дрейф и может быть более заметно на старых или неисправных контроллерах.

В качестве реального примера возьмем гоночную игру. Благодаря аналоговым входам мы можем медленно направлять автомобиль в ту или иную сторону. Однако без системы мертвых зон автомобиль будет медленно двигаться сам по себе, даже если игрок не прикасается к джойстику. Это происходит потому, что сила оси направления не будет равна 0.0, когда мы этого ожидаем. Поскольку мы не хотим, чтобы в этом случае автомобиль управлялся сам по себе, мы определяем значение "мертвой зоны" 0.2, которое будет игнорировать все входные данные, сила которых меньше 0.2. Идеальное значение мертвой зоны достаточно высоко, чтобы игнорировать ввод, вызванный дрейфом джойстика, но достаточно низко, чтобы не игнорировать фактический ввод от игрока.

Godot features a built-in dead zone system to tackle this problem. The default value is 0.2, but you can increase it or decrease it on a per-action basis in the Project Settings' Input Map tab. For Input.get_vector(), the deadzone can be specified, or otherwise it will calculate the average deadzone value from all of the actions in the vector.

"Echo" events

Unlike keyboard input, holding down a controller button such as a D-pad direction will not generate repeated input events at fixed intervals (also known as "echo" events). This is because the operating system never sends "echo" events for controller input in the first place.

If you want controller buttons to send echo events, you will have to generate InputEvent objects by code and parse them using Input.parse_input_event() at regular intervals. This can be accomplished with the help of a Timer node.

Window focus

Unlike keyboard input, controller inputs can be seen by all windows on the operating system, including unfocused windows.

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_WM_FOCUS_OUT:
            focused = false
        NOTIFICATION_WM_FOCUS_IN:
            focused = true


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

    return false


func event_is_action_pressed(event: InputEvent, action: String) -> bool:
    if focused:
        return Input.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.

Power saving prevention

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.

Power saving prevention is not supported on Linux in Godot 3.x, but it is supported in Godot 4.x. As a workaround, increase the screensaver's timeout period to make it less likely to occur during gameplay.

Устранение неполадок

См.также

You can view a list of known issues with controller support on GitHub.

My controller isn't recognized by Godot.

First, check that your controller is recognized by other applications. You can use the Gamepad Tester website to confirm that your controller is recognized.

My controller has incorrectly mapped buttons or axes.

If buttons are incorrectly mapped, this may be due to an erroneous mapping from the SDL game controller database. You can contribute an updated mapping to be included in the next Godot version by opening a pull request on the linked repository.

There are many ways to create mappings. One option is to use the mapping wizard in the official Joypads demo. Once you have a working mapping for your controller, you can test it by defining the SDL_GAMECONTROLLERCONFIG environment variable before running Godot:

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

To test mappings on non-desktop platforms or to distribute your project with additional controller mappings, you can add them by calling Input.add_joy_mapping() as early as possible in a script's _ready() function.

My controller works on a given platform, but not on another platform.

Linux

Prior to Godot 3.3, official Godot binaries were compiled with udev support but self-compiled binaries were compiled without udev support unless udev=yes was passed on the SCons command line. This made controller hotplugging support unavailable in self-compiled binaries.

HTML5

HTML5 controller support is often less reliable compared to "native" platforms. The quality of controller support tends to vary wildly across browsers. As a result, you may have to instruct your players to use a different browser if they can't get their controller to work.

Also, note that controller support was significantly improved in Godot 3.3 and later.