Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions tutorials/inputs/controller_features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,179 @@ The following example makes the LED blink red three times when the south button
blink_tween.tween_interval(0.2)
blink_tween.set_loops(3)

Motion sensors (gyroscope and accelerometer)
--------------------------------------------

With motion controls, games can track the controller's physical rotation and movement.
This can be used to let the player turn the in-game camera by moving their controller, or shaking their controller to perform a special action.

There are several controller brands that have implemented gyroscope and accelerometer sensors
into their modern controllers, the biggest two being PlayStation and Nintendo.
Note that the Xbox controllers don't have motion sensors inside of them.

To check if a connected controller has motion sensors, use :ref:`Input.has_joy_motion_sensors()<class_Input_method_has_joy_motion_sensors>`.

Motion sensors are disabled by default to avoid draining the controller battery when games don't use those features.
To enable them, call :ref:`Input.set_joy_motion_sensors_enabled()<class_Input_method_set_joy_motion_sensors_enabled>`.

Note that the axes of the values that controller's motion sensors report are always relative to the controller's natural orientation.
Here's an image of the axes mapping for more clarity:

.. image:: img/controller_axes.webp

The controller's gyroscope values show rotation around their respective axes:
- the X value of the gyroscope data shows the rotation around the X axis (roll).
- the Y value of the gyroscope data shows the rotation around the Y axis (yaw).
- the Z value of the gyroscope data shows the rotation around the Z axis (pitch).

The controller's accelerometer will provide values in the following ways, respectively:
- Movement left and right are reported as **+X** and **-X**.
- Movement down and up are reported as **+Y** and **-Y**.
- Movement away from and towards the user are reported as **+Z** and **-Z**.

Gyroscope
~~~~~~~~~

A **gyroscope** is a type of sensor that detects the controller's rotation.
Here are some notable examples of gyroscope use in games:

- In *Helldivers 2*, *Horizon Forbidden West*, *Star Wars: Dark Forces Remaster*, and *Fortnite*,
tilting the controller causes the camera to rotate accordingly ("gyro aiming").
`This video by *Daven On The Moon* <https://www.youtube.com/watch?v=Vlfg9yku2hY>`_ demonstrates and discusses gyro aiming in more detail.
- In *Death Stranding*, BB can be soothed by rotating the controller softly.

The following example rotates an object using a controller's gyroscope sensor.
You can also access this example by taking a look at the
:ref:`Input.start_joy_motion_sensors_calibration()<class_Input_method_start_joy_motion_sensors_calibration>` documentation.

.. code-block::

const GYRO_SENSITIVITY = 10.0

func _ready():
# In this example we only use the first connected joypad (id 0).
if 0 not in Input.get_connected_joypads():
return

if not Input.has_joy_motion_sensors(0):
return

# We must enable the motion sensors before using them.
Input.set_joy_motion_sensors_enabled(0, true)

# (Tell the users here that they need to put their joypads on a flat surface and wait for confirmation.)

# Start the calibration process.
calibrate_motion()

func _process(delta):
# Only move the object if the joypad motion sensors are calibrated.
if Input.is_joy_motion_sensors_calibrated(0):
move_object(delta)

func calibrate_motion():
Input.start_joy_motion_sensors_calibration(0)

# Wait for some time.
await get_tree().create_timer(1.0).timeout

Input.stop_joy_motion_sensors_calibration(0)
# The joypad is now calibrated.

func move_object(delta):
var node: Node3D = ... # Put your object here.

var gyro := Input.get_joy_gyroscope(0)
node.rotation.x -= -gyro.y * GYRO_SENSITIVITY * delta # Use rotation around the Y axis (yaw) here.
node.rotation.y += -gyro.x * GYRO_SENSITIVITY * delta # Use rotation around the X axis (pitch) here.

Note that before using the gyroscope's data, we must first calibrate it by calling :ref:`Input.start_joy_motion_sensors_calibration()<class_Input_method_start_joy_motion_sensors_calibration>`
and :ref:`Input.stop_joy_motion_sensors_calibration()<class_Input_method_stop_joy_motion_sensors_calibration>`.
That's because modern gyroscopes often need calibration. This is like how a weighing scale can need calibration to tell it what "zero" is.
Like a weighing scale, only a correctly calibrated gyroscope will give an accurate reading.
During calibration, the user sets the controller down on a flat surface.
The controller then determines what values its gyroscope reports when it is actually not moving at all (its "bias"),
and uses this information to make its rotation data more accurate.

See `the article on GyroWiki <http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-1:the-gyro-is-a-mouse>`__
for information on how to use gyroscope input as a mouse.

After the controller's gyroscope has been enabled and correctly calibrated,
you can read its reported values by using :ref:`Input.get_joy_gyroscope()<class_Input_method_get_joy_gyroscope>`.

Accelerometer
~~~~~~~~~~~~~

.. warning::

Do not use accelerometer data to find the controller's position in 3D space;
the accelerometers in general are not precise enough for this.

An **accelerometer** is a type of sensor that detects a controller's acceleration in m/s².
For example, it can detect if the player quickly raises their controller, moves it to the side, or shakes it.

The acceleration that an accelerometer detects includes gravity by default.
To get *only* the acceleration imparted by the user, subtract gravity from the detected acceleration:

.. code-block::

Input.get_joy_accelerometer(device) - Input.get_joy_gravity(device)

Due to how accelerometers work physically, after movement in one direction stops
they almost immediately report movement in the opposite direction.
After detecting movement in one direction, you may want to ignore further readings
for a small period of time to avoid detecting this opposite movement.

The following example prints the controller movement when it's being quickly moved by using its accelerometer.
If the sensitivity doesn't feel right for you,
you can tweak the ``THRESHOLD`` constant or you can replace it by using a different value in the code below.

.. code-block::

var detect_accelerometer = true

# Change to make the game detect movement at different thresholds.
# With a lower value, smaller movements will be detected, and with a
# larger value, only big movements will be detected.
const THRESHOLD = 10.0

func _ready():
# In this example, we only use the first connected joypad (ID 0).
if 0 not in Input.get_connected_joypads():
return

if not Input.has_joy_motion_sensors(0):
return

# We must enable the motion sensors before using them.
Input.set_joy_motion_sensors_enabled(0, true)

func _process(delta):
if Input.has_joy_motion_sensors(0):
accelerometer_example()

func accelerometer_example():
if not detect_accelerometer:
return

var acceleration = Input.get_joy_accelerometer(0) - Input.get_joy_gravity(0)
if acceleration.length() > THRESHOLD:
if acceleration.x > THRESHOLD:
print("Moved left")
elif acceleration.x < -THRESHOLD:
print("Moved right")
if acceleration.y < -THRESHOLD:
print("Moved up")
elif acceleration.y > THRESHOLD:
print("Moved down")
if acceleration.z < -THRESHOLD:
print("Moved closer to the player")
elif acceleration.z > THRESHOLD:
print("Moved away from the player")

# After detecting movement in one direction, the accelerometer sensor
# will briefly report movement in the opposite direction, even though the controller only moved once.
# So we need to ignore these reported values for a short amount of time.
detect_accelerometer = false
await get_tree().create_timer(0.5, false).timeout
detect_accelerometer = true
Binary file added tutorials/inputs/img/controller_axes.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading