Introduction

SDL Joystick is an extension for interacting with joysticks, gamepads, gamepads, etc.

It uses the widely-supported Simple DirectMedia Layer library (SDL), and supports a lot of popular controllers, and cheaper controllers based on them.

The extension icon and a cheat sheet in Gamepad mapping was done by Clickteam user mobichan.

What does SDL Joystick support?

This object likely supports all controllers you'll come across.

Help

SDL Joystick is designed to never crash. So if any crash occurs, please report it immediately to Darkwire Software .

You can reach Phi, the lead Darkwire developer, on the Clickteam Discord .

You can also sponsor the Darkwire Patreon . You'll also gain access to more extensions that Darkwire has not published publicly.

Copyright © 2023 by Darkwire Software, revision 1


Concepts

Introduction ››

Mappings

There are two interfaces in SDL Joystick extension: Joystick, and Gamepad.

Joystick is a raw, 1:1 copy of what the devices are telling the computer. It varies per device, so it makes it very hard to get a consistent gameplay across all controllers.

Gamepad is a translation that simplifies all controllers into behaving like an XBox 360 controller. This mode used to be called Game Controller until SDL v3.

These modes are not used for different types of controllers; they're both used for all types.

Adding more mappings

You can import and customize the Joystick -> Gamepad mapping if you want, using the Add gamepad mapping from file action.

There's a publicly available list available here . Each line of text is a different controller.

Click the "gamecontrollerdb.txt" file, on its filename; then click the Raw button on top right; then right-click on the resulting text file page and press Save As. If you get your browser trying to save a "gamecontrollerdb.txt" file, you've done it successfully. (If you can't work it out, you can download the raw file here ).

To create your own mapping – which you should not need to do, most are supported natively – you can use any of the tools listed here .

You can also use Steam's Big Picture mapping, and read the mapping text line under (Steam install folder)/config/config.vdf. You'll only need the one line, not copying the whole VDF.

Device ID

A device ID is from 0 to 15, inclusive.

Device IDs are kept the same when other devices are disconnected and reconnected; but the same device after a reconnect or reboot may get a different device ID.

Device IDs may have gaps that are filled. For example, if you connect Devices A and B, then disconnect Device A, Device B will stay as device ID 1. If you then connect Device C, it may have ID 0.

You should not use device ID to see if a specific device has reconnected after a disconnect.

Handling controller reconnects

According to a discussion on SDL forums, there is no way to detect a specific controller reconnecting.

SDL combines several joystick interfaces, like DirectInput, raw input, XInput, etc. Most of these don't uniquely identify their controllers across reconnects.

In addition, the users could be using several of the same controller, with the same controller GUID.

It is recommended when a player's controller disconnects, you make them handle the controller re-identifying via UI; for example, "Player 2 was disconnected. When reconnected, Player 2 should press Start to continue", and pass off the problem of identifying which player is using which controller back to the user.

This also solves the problem of players customizing their characters, then swapping controllers, but not wanting to swap their character. They can simply both disconnect and re-identify as their original characters.

Axis dead zone

SDL advice is that you should consider the -8000 to +8000 zone on axes as a "dead zone", where no movement happens and the axis should still be considered centered, due to the over-sensitivity of axes in controllers.

If you don't consider this dead zone, the slight off-centering in a controller will lead the player to be constantly visibly moving, even when the player isn't touching the controls.

You may also want to allow the user to customize the dead zone per controller.

Using the Abs() math function converts negative to positive, but not vice versa, allowing you to simply do:

Compare Two General Values; Absolute value of Game Controller Get Axis greater than eight thousand.

That condition will be enough to decide if the axis is registering a direction, or still centered enough to be considered center.

Jitter

Due to the high sensitivity of axes, you may have to account for tiny variations in the axis value, known as "jitter", by making sure the old value and current value are sufficiently different.

For example, despite the user holding the stick in the exact same position, the value may come back as +/- 500 from where they had it as. A user could hold a stick half-way between center and edge, without visibly moving it at all, and the values will fluctuate.

If holding it exactly halfway is important in your game, use Abs() and check it's within a difference range of halfway, rather than an exact match of old value.

To give a user fine control of their velocity using axis, you should use division and float-based movements. There is a demonstration in the Gamepad frame of the example MFA. Float-based movements are commonly in generic 360 movement examples, though.

Float-based movements are exact enough that tiny variations from jitter will be negligible. If the difference of +/- 500 amounts to barely a quarter pixel speed change per 10 seconds, it won't matter if jitter is present.


Warnings

Introduction ››

Be aware of these things when designing:

  • Some controllers will show as connected, but not change their input in expressions/conditions until the controller is used (e.g. moving a stick, pressing a button).
  • The Xbox controllers will not have rumble if they are connected to a PC under Bluetooth – see Microsoft's note. They should have rumble if they are connected wired, though.
  • PS4/PS5 controllers will not have rumble under Bluetooth, unless you switch their reporting mode to extended reporting.
    This has side effects; see the Enable PS4/PS5 extended reports object property.
  • Joy-Con controllers can show up as either one combined controller, or two separate controllers.
    You can set this behavior in the HIDAPI: Combine Joy-Con controllers object property.
  • You can disable Gamepad events for controllers with no associated player by using the "player object fixed value" actions.
    You should use this behavior when you're not setting up players and their controllers.
  • It is highly recommended you use Gamepad over Joystick. Joystick's data varies considerably per controller.
    You can disable Joystick expressions/conditions by unchecking the Activate Joystick ticking interface object property.
    This will stop the updating of the data used for those, saving processor time, while leaving Gamepad still usable.


Gamepad mapping

Introduction ››

Gamepad interface has its ID numbers mean specific buttons, sticks and the like, regardless of the original ID numbers that the Joystick interface has.

Joystick does not map and so the IDs will vary per controller.

All the joystick's sticks (axis/axes), buttons, hats, etc., are mapped to Gamepad as buttons or axes.

The below axis/button IDs are what applies when Use buttons labels, not position object property is unchecked (the default).

Cheat sheet image. For those using accessibility, it would be better if you looked up the Gamepad button list online.

(This image made by mobichan. It can be opened from its post on Discord here .)

For looking up, the list is included below.

Buttons

Axis

Gamepad ties several axis together into simplified sticks.

  • Left stick is handled by Axis 0 ↔, and Axis 1 ↕.
    If axis 0 is less than 0, the left stick is moving left. If it is greater than 0, the left stick is moving right.
    If axis 1 is less than 0, the left stick is moving up. If it is greater than 0, the left stick is moving down.
  • Right stick is handled by Axis 2 ↔, and Axis 3 ↕.
    If axis 2 is less than 0, the right stick is moving left. If it is greater than 0, the right stick is moving right.
    If axis 3 is less than 0, the right stick is moving up. If it is greater than 0, the right stick is moving down.
  • Trigger left is handled by Axis 4 ↑.
    Some controllers' triggers can register several levels of pressure; most can't and will only report maximum pressure level.
    If pressure is greater than 0, the left trigger is being pressed.
  • Trigger right is handled by Axis 5 ↑.
    If pressure is greater than 0, the right trigger is being pressed.


History

Introduction ››

SDL Joystick was written by Clickteam user PkR, with first code version created on April 15th 2020.

He made it open-source under GitHub, PiKeyAr/SDLJoystick repository.

PkR's version used an external SDL library, and initially relied on a Visual Studio redistributable to be installed, on both the Fusion developer's computer, and the users' computers.

Since this was hard to work with, on 20th November 2020, Clickteam user Phi (of Darkwire Software ) recreated the extension, using PkR's code as a template, and implementing it in DarkEdif SDK, which would make it easier to port to other platforms.

He also rebuilt the SDL library so it could be embedded internally in the Fusion extension, as a static library, removing all the external dependencies entirely. Finally, he added range checks so parameters that were absurd (such as device ID -5) could not crash the program by using invalid memory.

There was some conversation trying to merge the codebase, but as DarkEdif was very different to the Fusion SDK PkR had been using, PkR refused to merge it, leaving two versions.

This help file is written by Phi for SDL Joystick build 10, which adds the Gamepad interface, rumble, LED, battery power levels, and more Fusion-friendly interfaces like direction modes.

The Joystick interface design is mostly inherited from PkR's version, whereas Gamepad is Phi's design.

The extension icon and a cheat sheet in Gamepad mapping was done by Clickteam user mobichan.


Set LED color

Actions ›› Joystick ››

If a LED is available on the controller, for example a home button LED, this action will set its color.

You can pass a color using GetRGB(red, blue, green), or pass -1 to turn the LED off.


Start rumble

Actions ›› Joystick ››

This condition starts a rumble of specified intensity, overwriting any existing rumble of that type.

It takes five parameters:

  1. The device ID (0 to 15).
  2. Rumble type
    0 for device rumbling,1 for device's trigger buttons rumbling.
  3. Left side rumble intensity (0 to 65535).
  4. Right side rumble intensity (0 to 65535).
  5. Rumble duration (milliseconds)

To see which types of rumble a device supports, use the Joystick > Device has rumble? condition.

Note that this overwrites an existing rumble; so you can pass intensity 0 to disable rumbling.

If the device does not support rumble, no error will be made.

Note that PlayStation 4 and PlayStation 5 controllers connected by Bluetooth will not have Rumble usable by default. There is a property to change that, but it has side effects.


Set player object fixed value

Actions ›› Joystick ››

This condition sets a controller's associated player object fixed value.

It takes two parameters:

  1. The device ID (0 to 15).
  2. The player object's fixed value, or -1 for none.

The fixed value is not checked to be valid or unique to the controller.

Using this feature is very useful for responding to multiple devices with the same Gamepad events:

Fusion events. Joystick object any gamepad left stick changed; fixed value of player object equals joystick player index minus one. Actions: player, set direction to gamepad direction or angle minus one, minus one, thirty-two.

You can then turn off triggering Gamepad events for controllers with no Player object fixed value, using the Joystick > Set ignore controllers with no player FV action.

This is recommended for improved performance when players are already set up.


Set player index

Actions ›› Joystick ››

This condition sets a controller's associated player index.

It takes two parameters:

  1. The device ID (0 to 15).
  2. The player index (0 to 15), or -1 to reset to none.

Some controllers support displaying the player index via lights on the device. Most will ignore it.

If a controller does not support it, this action will not create an error, but the Joystick > Get player index expression may return -1.


Set ignore controllers with no FV

Actions ›› Joystick ››

This action sets whether to ignore controllers that have no associated player object fixed value.

It takes one parameter: ignore unassociated controllers (1), or trigger events for unassociated controllers (0).

Any other value will result in an error and no change.

If you ignore unassociated controllers, they will not be ticked (so the Gamepad expressions will be frozen) and events will not be triggered for them. This will save on performance.

For best performance, only enable triggering events for unassociated controllers when you're setting up players and their controllers. Once that's done, go back to ignoring.


Set stick/D-Pad direction mode

Actions ›› Gamepad ››

This action sets a stick/D-Pad's direction mode; how many discrete directions they can have.

It takes three parameters:

  1. The gamepad device ID (0 to 15).
  2. The stick(s) to affect: this should be "left" for left stick, "right" for right stick, "dpad" for Direction Pad.
    You can set multiple; for example, "left, dpad".
  3. The direction mode: one of "4-dir", "4-dir diagonal", "8-dir", "16-dir", "32-dir", or "angle".
    This affects how far the stick must change angle before it is considered a direction change.

The default direction mode of new gamepad controllers is decided by the Default direction mode object property, but changed by this action.

Note the Direction Pad can only use 8-dir and angle modes. If 16-dir or 32-dir is specified for D-Pad, it will use 8-dir instead. 4-dir and 4-dir diagonal will also result in 8-dir, including the other 4 directions that you didn't want.

Direction mode is used in calculations for how wide a range is a discrete direction. For example, the right direction is this range in different direction modes:

Image showing the right direction is a 45 degree range in 4 direction mode, and a 22.5 degree range for 8 direction mode.

This direction mode action affects:

  • Triggering the Gamepad > On stick/D-Pad changed condition.
    For example, in 8-dir mode, you must move the stick 45 degrees from one direction for it to trigger a Gamepad > On stick/D-Pad changed event – or move it to center. Whereas in angle mode, you barely need to move the stick at all to change direction.
  • Responding to the Gamepad > Check stick/D-Pad direction condition.
    If you specify a 4-dir, 8-dir, 16-dir direction mode, this condition cannot return true for all the condition's 32 possible directions.
    However, if you specify the angle direction mode, and use this condition, the stick/dpad's current angle is converted to the nearest position in 32-dir for comparing.
  • Setting the default return for Gamepad > Axes and buttons > Get stick dir/angle expression.
    When direction mode parameter in this expression is specified as -1, the direction mode you set in this action is used.

Using this action, if your player only moves in 8 directions, you can set to 8-dir mode so SDL Joystick only triggers events if the stick changes by 45 degrees (or is moved to/from center).

Whereas, if you want your player moving in 360, you can set to angle mode to cause the player's smallest stick changes to trigger a new Gamepad > On stick/D-Pad changed event.

If you do choose 360 movement, using the Gamepad > Axes and buttons > Get axis intensity expression is highly recommended, as the x/y speed math is done for you, taking into account whether the stick is at the edge of its containing socket, or partway to the edge.

You can also alter the X and Y speed and how precisely it's calculated using the Gamepad > Set axis intensity range, and steps/jitter action.


Set axis deadzone

Actions ›› Gamepad ››

This action sets how far a stick can move off-center while still being considered centered.

It takes three parameters:

  1. The gamepad device ID (0 to 15).
  2. The stick(s) to affect: this should be "left" for left stick, "right" for right stick, or "both" for both.
  3. The deadzone range. Sticks report their range from -32,768 to +32,767.
    You can pass from 1 to 32766.

Sticks normally do not report center as 0 due to their high sensitivity, but will report a range around it.

The range you specify here is considered the range where the stick is still considered to be in the center.

The default range recommended by SDL developers is 8000, so if the stick reports anything between -8000 and +8000 for both X and Y offset, the stick is considered to be in center.

For sticks that don't fluctuate as much (or are even worse), you can use this action to change the deadzone range to give the user finer control over their x and y movement.

This deadzone is taken into account for all Gamepad conditions and expressions, with the exception of the Gamepad > Axes and buttons > Get axis value expression – which you shouldn't be using for left or right stick anyway.


Set axis intensity range, and steps/jitter

Actions ›› Gamepad ››

This action sets how far a stick can move off-center while still being considered centered.

It takes four parameters:

  1. The gamepad device ID (0 to 15).
  2. The stick(s) to affect: this should be "left" for left stick, "right" for right stick, "dpad" for Direction Pad.
    You can set multiple; for example, "left, dpad".
  3. Max intensity – this is a float. Pass a positive number.
    This is mirrored, so the same number as negative is used for other end of the range.
  4. Steps between 0 and max intensity, including the center position (including the deadzone).
    This handles jitter , which is the term for small changes in the sticks due to their high sensitivity.
    You can also specify -1 to not round the intensity into steps.

These parameters are used in the intensity calculations for Gamepad > Axes and buttons > Get axis intensity expression.

For example, if you specify a max intensity of 1.0, with 5 steps, the intensity is calculated with axis's range of -32,768 to +32,767 into a range of:

-1.0, -0.5, 0, +0.5, +1.0

For X axis, at far left the axis intensity expression returns -1.0, midway left is -0.5, center is 0, midway right is 0.5, and far right is 1.0.

The same applies to Y axis; at the top the intensity expression returns -1.0, midway up is -0.5, center is 0, midway down is 0.5, and bottom is 1.0.

With a max intensity of 1.0 and range of 9 steps:

-1.0, -0.75, -0.5, -0.25, 0, +0.25, +0.5, +0.75, +1.0

And with a max intensity of 2.0 and rage of 5 steps:

-2.0, -1.0, 0, +1.0, +2.0

By changing the intensity, you can easily implement a 360 movement and have SDL Joystick calculate the X/Y speeds for your object.


Set sensor enabled

Actions ›› Gamepad ››

This action turns off or on a sensor on a gamepad.

It takes three parameters:

  1. The gamepad device ID (0 to 15).
  2. The sensor to change. This is either:
    "accel" for the accelerometer (which detects controller movement)
    "gyro" for the gyroscope (which detects controller rotation)
  3. Whether to enable or disable the sensor.
    This is 1 to enable, or 0 to disable.

Sensors are disabled by default, for efficiency and saving battery on the controller. If a device is disconnected and reconnected, the sensor will be off again.

If a gamepad does not have the sensor, this action will create an error.

To check if a sensor exists on a controller, use the Gamepad > Does device have accelerometer? or Gamepad > Does device have gyroscope? conditions.


Advanced: Add controller mapping from file

Actions ›› Gamepad ››

This action imports mappings (translations) of Joystick interface to Gamepad interface.

A mappings file will have a device GUID, with a following list of how to map hats, axis, buttons, etc, to Gamepad, which is a Xbox 360 controller's physical layout.

You can access a publicly-available list here , or generate your own mappings. For more detail, read the mappings page.


On error

Conditions ››

This event is triggered by any errors in using SDL Joystick.

Use the Current error expression to read the error.


Controller is connected

Conditions ››

This is true if a joystick with the given device ID is connected.

Note that device IDs are re-used. Read more under device ID .


New device connected

Conditions ›› Joystick ››

This event is triggered when a new device is plugged in.

Joystick events include all devices, even those that do not map to Gamepad.

You can use the Gamepad > Is joystick mapped to gamepad? condition in this event to determine if a gamepad-mapped device was plugged in.

This condition is not part of the Joystick ticking, so it will run even if Joystick interface ticking object property is unchecked.


Device removed

Conditions ›› Joystick ››

This event is triggered when a device is unplugged.

Joystick events include all devices, even those that do not map to Gamepad.

You can use the Gamepad > Is joystick mapped to gamepad? condition in this event to determine if a gamepad-mapped device was removed.

Using the removed device ID in conditions, actions or expressions after this event finishes will result in errors; this event is the last point you can use them for the device.

This condition is not part of the Joystick interface ticking, so it will run even if Joystick interface ticking object property is unchecked.


Device has LED?

Conditions ›› Joystick ››

This event is true when the given device ID (0-15) has a LED to light it up, such as a Home button LED.

This condition is not part of the Joystick interface ticking, so it will work even if Joystick interface ticking object property is unchecked.


Device has rumble?

Conditions ›› Joystick ››

This event is true when the given device ID (0-15) has rumble capacity of a given type.

It takes one parameter, the type of rumble, which is either:

  • 1 for device rumble; or
  • 2 for rumble in the left/right triggers; or
  • 3 for either of those.

The Xbox controllers will not have rumble if they are connected to a PC under Bluetooth – see Microsoft's note. They should have rumble if they are connected wired, though.

Also, the PlayStation 4 and PlayStation 5 controllers connected by Bluetooth will not have Rumble enabled by default. There is a property to change that, but it has side effects.

This condition is not part of the Joystick interface ticking, so it will work even if Joystick interface ticking object property is unchecked.


Button is pressed

Conditions ›› Joystick ››

This condition is true the first event loop that a button starts being pressed, but false from the next event loop and further, even if the button is still held down. The condition can be true again for that button after the button is lifted.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The button number (0 to 31).

This condition will be invalid if Joystick interface ticking object property is unchecked.


Button is held down

Conditions ›› Joystick ››

This condition is true on the first event loop a button is pressed down, and remains true until the button is lifted.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The button number (0 to 31).

This condition will be invalid if Joystick interface ticking object property is unchecked.


Button is released

Conditions ›› Joystick ››

This condition is true on the first event loop a button is no longer pressed down, and is false from the next event loop after.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The button number (0 to 31).

This condition will be invalid if Joystick interface ticking object property is unchecked.


Any button pressed

Conditions ›› Joystick ››

This condition is true the first event loop that any button starts being pressed, but false from the next event loop and further, even if the original button is still held down. The condition can be true again for another button being pressed, or for the original button if it is lifted then pressed again.

This condition takes one parameter: the device ID (0 to 15).

This condition will be invalid if Joystick interface ticking object property is unchecked.


Any button held down

Conditions ›› Joystick ››

This condition is true on the first event loop any button is pressed down, and remains true until all buttons are lifted.

This condition takes one parameter: the device ID (0 to 15).

This condition will be invalid if Joystick interface ticking object property is unchecked.


Any button released

Conditions ›› Joystick ››

This condition is true on the first event loop any button are no longer pressed down, and is false from the next event loop after.

This condition takes one parameter: the device ID (0 to 15).

This condition will be invalid if Joystick interface ticking object property is unchecked.


Is joystick mapped to gamepad?

Conditions ›› Gamepad ››

This condition is true if the device is mapped to gamepad interface.

If true, the controller can use Gamepad conditions, actions, and expressions.

If false, the controller will make an error when used with Gamepad conditions, actions, or expressions; and it won't trigger any of the Gamepad triggered conditions (sometimes called immediate conditions).

You can still use the Joystick conditions/actions/expressions for non-Gamepad, but note controllers will report controls very differently; left stick may be axis 0, axis 2, etc.

If you do use Joystick axis/hats/buttons, make sure you leave the Joystick ticking interface activated object property enabled.


Does device have accelerometer?

Conditions ›› Gamepad ››

This condition is true if the given device ID has an accelerometer (a sensor circuit that detects movement).

If false, the device does not have one.

This condition will be true even if the accelerometer sensor is currently disabled, which is the default to save battery; use the Joystick > Enable sensor action to enable the sensor.

To read the device movement, use the Gamepad > Accelerometer sensor expressions .


Does device have gyroscope?

Conditions ›› Gamepad ››

This condition is true if the given device ID has an gyroscope (a sensor circuit that detects rotation).

If false, the device does not have one.

This condition will be true even if the gyroscope sensor is currently disabled, which is the default to save battery; use the Joystick > Enable sensor action to enable the sensor.

To read the device rotation, use the Gamepad > Gyroscope sensor expressions .


Button started being pressed

Conditions ›› Gamepad ›› Button ››

This condition is triggered the first event loop that a button starts being pressed. The button must be released before this condition can occur for that button again.

This condition takes one parameter: the gamepad button number (0 to 20).

It will trigger for all Gamepad controllers, and automatically select the correct controller ID, which you can read using the Current triggered event > Device ID expression.

You use the Joystick > Get player FV expression to select the right player object for the triggering event.

This condition will not trigger for gamepads without a player FV, saving processing time, if you enable this behavior with Joystick > Ignore controllers with no FV action.

If multiple buttons start being pressed in the same event loop, the lowest button ID being pressed triggers this event, then the next lowest one, etc.

If multiple controllers have buttons start being pressed in the same event loop, the lowest controller ID is first to start triggering its button pressed events, then the next lowest controller ID, etc.


(for repeating button, see expressions)

Conditions ›› Gamepad ›› Button ››

To make the equivalent of "Repeat while button is held down", use Compare Two General Values and the Gamepad > Buttons and axis > Get button state expression.

For example:

Non-triggered conditions are less efficient than repeating ones, and don't pre-select the controller, so you shouldn't use this method anyway, as a condition or expression.

It's better to enable a behavior when the button starts being pressed by using a Gamepad > Button > Button started being pressed event, and then reverse it when the button is released using a Gamepad > Button > Button released event.


Button released

Conditions ›› Gamepad ›› Button ››

This condition is triggered on the first event loop a button is no longer pressed down.

This condition takes one parameter: the gamepad button number (0 to 20).

It will trigger and automatically select the correct controller ID, which you can read using the Current triggered event > Device ID expression.

You use the Joystick > Get player FV expression to select the right player object for the triggering event.

This condition will not trigger for gamepads without a player FV, saving processing time, if you enable this behavior with Joystick > Ignore controllers with no FV action.

If multiple buttons are released in the same event loop, the lowest button ID being released triggers this event, then the next lowest one, etc.

If multiple controllers release buttons in the same event loop, the lowest controller ID is first to start triggering its button released events, then the next lowest controller ID, etc.


Any gamepad, any button started being pressed

Conditions ›› Gamepad ›› Button ››

This condition is true the first event loop that any button starts being pressed. It will not run again for that button until it is released and pressed again.

The gamepad ID triggering this event is available in the Current triggered event > Device ID expression.

The button ID is available in the Current triggered event > Gamepad button ID expression.

You use the Joystick > Get player FV expression to select the right player object for the triggering event.

This condition will not trigger for gamepads without a player FV, saving processing time, if you enable this behavior with Joystick > Ignore controllers with no FV action.

If multiple buttons start being pressed in the same event loop, the lowest button ID being pressed triggers this event, then the next lowest one, etc.

If multiple controllers have buttons start being pressed in the same event loop, the lowest controller ID is first to start triggering its button pressed events, then the next lowest controller ID, etc.


Check stick/D-Pad direction (8/16/32-dir)

Conditions ›› Gamepad ›› Axis ››

This condition checks the passed stick or direction pad (D-Pad) direction.

It takes three parameters:

  1. The gamepad device ID to check (0-15).
  2. The stick(s) to check. You can pass "L" for left stick, "R" for right stick, "D" for direction pad.
    This is case sensitive.
    You can also pass multiple sticks, in which case the logic below applies.
  3. The direction(s) to check, or select none to check if the stick is centered.
    If you use the expression mode, specify 0-31 for direction, or -1 for centered.

Note that the direction uses the direction mode you set for the stick, but converts the current stick(s) direction to 32-dir mode.

Only 32-dir and angle direction mode can trigger all the directions. For example, it is impossible for the D-Pad to return 5 o'clock direction. Similarly, if you set the left stick to use 8-dir direction mode, then in this condition check if left stick is pointing at 5 o'clock, the same impossibility will apply and the condition will never be true.

This condition will not trigger for gamepads without a player FV, saving processing time, if you enable this behavior with Joystick > Ignore controllers with no FV action.

Multiple stick behavior

You can pass multiple sticks to check any of them match with the others inactive.

For example, you can pass "LD" for checking left stick, then D-Pad.

If you pass multiple sticks, the first to match is selected, and will be returned in Current triggered event > Gamepad direction stick expression.

However, if one stick matches but the other sticks you are checking are not centered, then this condition will return false.

So if you check "LD" is facing right, and left stick is facing right, but D-Pad down is also being pressed, then this condition will return false.

Only one passed stick must be facing the direction, even all the passed sticks matching will not trigger this event. This design is to ensure the user doesn't use two controls for the same thing and end up making strange mixes of controls.

If you prefer a strange merger, contact Phi so he can try and work out what ungodly formulas you want and whether it should be possible to go up 200%.

If you prefer to check if all are facing together, then simply use multiple of this condition.

If you pass no direction (centered sticks), and multiple sticks, all passed sticks must be centered for this condition to be true.


(For stick as angle, see expressions)

Conditions ›› Gamepad ›› Axis ››

To get the stick's direction as an angle, rather than a 32-dir check, you can use the Gamepad > Axes and buttons > Get stick dir/angle expression. Simply pass 360 as the last parameter to get an angle.


On stick/D-Pad changed

Conditions ›› Gamepad ›› Axis ››

This condition is triggered whenever any gamepad's sticks change direction.

It takes one parameter, the stick(s) to check. This can be "L" for left stick, "R" for right stick, or "D" for D-Pad. This is case-sensitive.

You can also check for any of multiple sticks, e.g. "LD". If any match, this condition will be true.

How much angle the stick must change to trigger a direction changed event is based on what you set the direction mode to; either using the default direction mode property, or by overriding that default with the Gamepad > Set stick/D-Pad direction mode action.

Using this condition is very useful for responding to multiple devices with the same Gamepad events.

You can read the new direction with the Gamepad > Get stick dir/angle expression.

You may also want to use the Is current stick/D-Pad in center? condition.

You use the Joystick > Get player FV expression to select the right player object for the triggering event.

This condition will not trigger for gamepads without a player FV, saving processing time, if you enable this behavior with Joystick > Ignore controllers with no FV action.

Note sticks' direction change is always done in the order "left stick, right stick, D-Pad", regardless of whether you specify "LD" or "DL", order of events, or whether the user changed the direction for both in the same event tick.

This is because the sticks' directions are updated, then change-checked, in the same L, R, D order, every event loop.


Is current stick/D-Pad in center

Conditions ›› Gamepad ›› Axis ››

Returns true if the current device's current stick is in the center (i.e. not moved).

This condition is only usable with the Gamepad > Axis > On stick/D-Pad changed event.

It is negatable, so you can check if not-center as well, by right-clicking it and pressing Negate.

If you want to check the stick/D-Pad being centered outside of that triggered event, consider using Gamepad > Axis > Check stick/D-Pad direction with no direction selected.

Alternatively, you can check if Gamepad > Axes and buttons > Get stick dir/angle is -1.


Current error

Expressions ››

During an On error event, will return the error text.

Outside of these events, will return blank.


Device ID

Expressions ›› Current triggered event ››

Returns the current device ID that is triggering Joystick > New device connected or Joystick > Device removed events, or any of the Gamepad triggered events.


Gamepad button ID

Expressions ›› Current triggered event ››

Returns the current gamepad button ID that is triggering the Gamepad's button events.

It is most useful with the Gamepad > Any gamepad, any button started being pressed condition.


Gamepad direction stick

Expressions ›› Current triggered event ››

Returns the current gamepad stick hat is triggering the Gamepad's button events.

0 is left stick, 1 is right stick, 2 is D-Pad.

If no stick is available, -1 is returned.

It is most useful with the Gamepad > On stick/D-Pad changed condition.


Get axis value

Expressions ›› Joystick ›› Current input ››

Returns the state of an axis; either X or Y position of a stick, or X or Y position of Directional Pad, or how hard a right/left trigger was pressed.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The axis number (0 to 7).
    What each axis index means on different device models may vary considerably, e.g. index 0 could be left stick on one device, and right stick on another.
    Consider using the Gamepad expressions instead for a standardized interpretation.

This expression can return from -32768 to +32767, depending on how much the stick was moved, or how hard the D-Pad/trigger was pressed.

You should consider the axis dead zone , and account for jitter.

If the device ID or axis ID is invalid, the return will be 0.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get hat value

Expressions ›› Joystick ›› Current input ››

Returns the directions of a Directional Pad being pressed.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The hat number (0 to 7).
    What each hat index means on different device models may vary considerably. In some models, the Directional Pad is considered buttons.
    Consider using the Gamepad expressions instead for a standardized interpretation.

This expression returns a bitmask, of 0-2 directions at once. This includes the values:

1 = Up

2 = Right

4 = Down

8 = Left

This expression ranges from 0, indicating hat at center, to 12, for down and left pressed simultaneously.

You can test for 1 direction in any combination, using Fusion's and operator. For example, to test if user is holding left:

Joystick_GetHat("SDL Joystick object", 0, 0) and 8

This expression will return 8 if the user is pressing left, or left-up, or left-down; it will return 0 if they're not pressing left at all.

You can check for two directions at once, by adding them together. For example, 8 (left) + 1 (up) is 8 + 1 == 9.

Joystick_GetHat("SDL Joystick object", 0, 0) == 9

If the device ID or axis ID is invalid, the expression will return 0.

If the hat is not being touched and stays at center, the expression will return 0.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get ball X delta

Expressions ›› Joystick ›› Current input ››

[Trackball support was removed in SDL 3. This expression will no longer work.]

Returns the X difference that a trackball has been moved, since the last event loop.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The ball number (0 to 4).
    What each ball index means on different device models may vary considerably.
    Consider using the Gamepad expressions instead for a standardized interpretation.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get ball Y delta

Expressions ›› Joystick ›› Current input ››

[Trackball support was removed in SDL 3. This expression will no longer work.]

Returns the Y difference that a trackball has been moved, since the last event loop.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The ball number (0 to 4).
    What each ball index means on different device models may vary considerably.
    Consider using the Gamepad expressions instead for a standardized interpretation.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get button state

Expressions ›› Joystick ›› Current input ››

Returns the state of a button; either 0 for not pressed, or 1 for pressed.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The button number (0 to 31).
    What each button index means on different device models may vary considerably, e.g. index 0 could be button A on one device, and button X on another.

Joysticks can report triggers as buttons, or they may register several pressure levels for triggers, and so report them as axis instead.

Consider using the Gamepad expressions instead for a standardized interpretation.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get raw buttons (HEX)

Expressions ›› Joystick ›› Current input ››

Returns the state of all joystick buttons as one bitfield integer.

This condition takes one parameter: the device ID (0 to 15).

The code for this starts with 0, then loops through every button in the joystick, and if that button is on, it bitshifts 1 to the left by the button index.

For example, button index 0 will cause 1 to be added, button index 1 will cause 2 to be added.

Only buttons that are currently being held down will be added, though.

The resulting number is then converted to hexadecimal text.

What each button index means on different device models may vary considerably, e.g. index 0 could be button A on one device, and button X on another.

Joysticks can report triggers as buttons, or they may register several pressure levels for triggers, and so report them as axis instead.

Consider using the Gamepad expressions instead for a standardized interpretation.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get held button index

Expressions ›› Joystick ›› Current input ››

Returns the nth held button's ID.

This condition takes two parameters:

  1. The device ID (0 to 15).
  2. The held button index (0 to 31). This is not the button ID.

All buttons have ID from 0-31. When a button is held, it is added to SDL Joystick's internal held button list, and takes up one of the held button indexes passed to the second parameter of this expression.

For example, if you hold button ID 1, 5, 6, holding them in that order, they will have held button index 0, 1, 2; and calling this expression with the device ID and 0, 1, 2, will return 1, 5, 6, respectively.

The held button ID is removed from the list when it is no longer held.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get last button pressed

Expressions ›› Joystick ›› Current input ››

Returns the last button ID that was not being pressed, and started being pressed.

This condition takes one parameter: the device ID (0 to 15).

This expression will keep the same return value until another button is pressed.

This expression can return the same as the Joystick > Get last button released expression, if the button is released and then pressed again.

Although unlikely, multiple buttons can be pressed simultaneously in the same Fusion event loop; this expression will return the highest button ID that was pressed in that scenario. The other IDs are lost to the void.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Get last button released

Expressions ›› Joystick ›› Current input ››

Returns the last button ID that used to be held down, but was released.

This condition takes one parameter: the device ID (0 to 15).

This expression will keep the same return value until another button is released.

This expression can return the same as the Joystick > Get last button pressed expression, if the button is released and then pressed again.

Although unlikely, multiple buttons can be released simultaneously in the same Fusion event tick; this expression return the highest button ID that was released in that scenario. The other IDs are lost to the void.

This expression will be invalid if the joystick ticking API is deactivated by the object property .


Number of devices

Expressions ›› Joystick ›› Metadata ››

This returns the number of devices connected to the system. It ranges from 0 to 16.

SDL library might be programmed to allow more simultaneous devices, but the SDL Joystick Fusion extension only supports up to 16. If for some reason more is needed, contact the developers.


Get device name

Expressions ›› Joystick ›› Metadata ››

Returns the device name, using the given device ID (0 to 15).

Example: "Nintendo Switch Pro Controller".

You can use this to uniquely identify a controller type. However, it won't uniquely identify a controller device, as users could easily have two of the same controller.

See handling controller reconnects under Concepts.


Get device GUID

Expressions ›› Joystick ›› Metadata ››

Returns the device GUID, using the given device ID (0 to 15).

Example: "030000007e0500000920000010026800".

You can use this to uniquely identify a controller type.

However, it won't uniquely identify a controller device, as users could easily have two of the same controller, so they will have the same GUID. Knock-off variants may have a different GUID to their originals.

Using different drivers may also change the joystick GUID.

See handling controller reconnects under Concepts.


Get device type

Expressions ›› Joystick ›› Metadata ››

This returns the controller's device type, using the given device ID (0 to 15).

Joystick interface supports a lot more devices than Gamepad, but each device reports input differently, making it difficult to code for.

The return text possibilities are: "unknown", "gamepad", "wheel", "arcade stick", "flight stick", "dance pad", "guitar", "drum kit", "arcade pad", and "throttle".


Get power level (as text)

Expressions ›› Joystick ›› Metadata ››

This returns the controller's power level, using the given device ID (0 to 15).

The return texts are:

  • "unknown"
    SDL can't read power level from controller. Controller may not support applications reading its power level.
  • "wired"
    Controller is plugged in wired, not on battery.
  • "empty/nearly empty"
    Controller has 0-5% battery. (this is one return text, not two)
  • "low"
    Controller has 6-20% battery.
  • "medium"
    Controller has 21-70% battery.
  • "full"
    Controller has 71-100% battery.


Number of axes

Expressions ›› Joystick ›› Metadata ››

Returns the number of axes (the plural of axis) that are supported by the joystick.

This condition takes one parameter: the device ID (0 to 15).

The return value is from 0 to 8.

If the device ID is invalid, 0 will be returned.


Number of buttons

Expressions ›› Joystick ›› Metadata ››

Returns the number of buttons that are supported by the joystick.

This condition takes one parameter: the device ID (0 to 15).

The return value is from 0 to 8.

If the device ID is invalid, 0 will be returned.


Number of hats

Expressions ›› Joystick ›› Metadata ››

Returns the number of hats that are supported by the joystick.

This condition takes one parameter: the device ID (0 to 15).

The return value is from 0 to 4.

If the device ID is invalid, 0 will be returned.


Number of balls

Expressions ›› Joystick ›› Metadata ››

[Trackball support was removed in SDL 3. This expression will no longer work.]

Returns the number of balls that are supported by the joystick.

This condition takes one parameter: the device ID (0 to 15).

The return value is from 0 to 4.

If the device ID is invalid, 0 will be returned.


Get player index

Expressions ›› Joystick ››

Returns the player index (0+), or -1 if it is not assigned, or not supported.

It takes one parameter: the controller's device ID (0-15, or -1 for current device ).

Player index is not supported by all controllers, and not displayed by all controllers even if supported. Consider using the player fixed value instead to uniquely identify players.

When the app ends, the controller's player number is reset to -1.


Get player FV

Expressions ›› Joystick ››

Returns the controller's player object fixed value, which you set using Joystick > Set player object fixed value action.

It takes one parameter: the controller's device ID (0-15, or -1 for current device ).

If no player fixed value is set for that controller, 0 is returned.

Using this expression is very useful for responding to multiple devices with the same Gamepad events:

Fusion events. Joystick object any gamepad left stick changed; fixed value of player object equals joystick player index minus one. Actions: player, set direction to gamepad direction or angle minus one, minus one, thirty-two.

Note this expression does not check the fixed value is valid.


Get axis value

Expressions ›› Gamepad ›› Axes and buttons ››

Returns the state of an axis; either X or Y offset of a stick, or how hard a right/left trigger was pressed.

This condition takes two parameters:

  1. The device ID (0 to 15, or -1 for current device ).
  2. The gamepad axis ID (0 to 5).

This expression can return from -32768 to +32767, depending on how much the stick was moved, or how hard the trigger was pressed.

Due to this high sensitivity, you may have to account for deadzone and jitter. For more info, see Concepts .

You should prefer using the Get axis intensity and Get stick dir/angle expressions for calculating movement, as deadzone and jitter are already compensated for in those. This expression is mostly useful for the trigger axes, 4 and 5.

If the device ID or axis ID is invalid, the return will be 0.


Get axis intensity

Expressions ›› Gamepad ›› Axes and buttons ››

Returns the intensity of a stick, how far it was moved in the passed axis, with compensation for jitter , deadzone and how many steps in-between both ranges of the stick/trigger.

This expression takes three parameters:

  1. The device ID (0 to 15, or -1 for current device ).
  2. Which stick to check?
    0 = Left stick, 1 = right stick, 2 = D-Pad, or -1 = current stick .
  3. Axis of stick to calculate intensity by.
    0 = X intensity, 1 = Y intensity, 2 = both intensities merged.

If both intensities is specified, Pythagoras theorem is used to calculate the intensity using X and Y intensity together. This will be greater than max intensity.

For example, moving up-right with intensity of 1.0 will return 1.414; note that both intensities is always positive.

(Without Pythagoras, a player could move faster diagonally, than they could by going horizontally and then vertically.)

This expression usually returns from -1.0 to 1.0, depending on how much the stick was moved.

However, you can adjust the intensity range using the Gamepad > Set axis intensity range, and steps/jitter action.

Jitter and deadzone are already compensated for in this event; both of which are adjustable with the same action. This means that if the stick is slightly off center, but in deadzone, this expression will return 0 for X and Y intensity.

This expression can be paired with reading a stick's direction, to see how far the user pushed the stick in that direction, allowing the player a fine-tuned control over their speed.

It can also be used to easily do a Fusion 360 movement via the X and Y intensity being the X and Y speed. To see how the calculation works, see the intensity range action.

If the device ID or axis ID is invalid, the return will be 0.0.


Get stick dir/angle

Expressions ›› Gamepad ›› Axes and buttons ››

Returns the direction or angle of a stick, or the D-Pad.

This expression takes three parameters:

  1. The device ID (0 to 15, or -1 for current device ).
  2. Which stick to check?
    0 = Left stick, 1 = right stick, 2 = D-Pad, or -1 = current stick .
  3. Direction mode to use.
    You can pass 4 for 4-dir, -4 for 4-dir diagonal, 8 for 8-dir, 16 for 16-dir, 32 for 32-dir, 360 for angles, or -1 for the direction mode the controller was set to.
    Controllers' direction modes start as whatever the default direction mode property is, and can be changed with the Gamepad > Set stick/D-Pad direction mode action.
    D-Pad will only return 8-dir or angle expression; if you try to read a different mode, it'll return 8-dir.

Note that this expression returns a float, so the 360 direction mode has a wider range than 360, as it can return partial degrees. It's simply faster to parse a number 360 than text "angle".

This expression will stay consistent during an event loop, as the position of sticks/D-Pad is read before triggered events and later, non-triggered events, are run; reading this expression does not re-query the stick.


Get button state

Expressions ›› Gamepad ›› Axes and buttons ››

Returns the state of a button; either 0 for not pressed, or 1 for pressed.

This condition takes two parameters:

  1. The device ID (0 to 15, or -1 for current device ).
  2. The gamepad button ID (0 to 20).

If the button ID is valid but the button is not present on the gamepad, no error will be created; this expression will simply return 0.


Accelerometer sensor

Expressions ›› Gamepad ››

The accelerometer shows the physical movement of the device. For detecting rotation, use the gyroscope.

These expressions return the current acceleration in m/s², not the current speed.

So if the object started being moved in the same direction, this expression will initially be high (or low), then go back to zero as it is no longer accelerating, but having a constant speed.

This measurement includes the force of gravity, so a device at rest will have an value of around 9.80665 away from the center of the earth, assuming you're on the standard Earth. Gravity will also vary slightly based on altitude and distance from the planet's equator.

For phones held in portrait mode, and game controllers held in front of you, the axes are defined as follows:

-X … +X: left … right

-Y … +Y: bottom … top

-Z … +Z: farther … closer

The values and axes definitions are not affected by changing the app's screen orientation, from landscape to portrait or vice versa.


Gyroscope sensor

Expressions ›› Gamepad ››

The gyroscope detects when the device is being rotated. For the device being moved around, use the accelerometer.

The gyroscope expressions return the current rate of rotation in radians per second.

The max radians in a full rotation is Pi x 2, or 6.28319…, and this can be negative -6.28319… as well.

For phones held in portrait mode and game controllers held in front of you, the axes are defined as follows:

-X … +X: left … right

-Y … +Y: bottom … top

-Z … +Z: farther … closer

X axis rotation is pitch; Y axis rotation is yaw; Z axis rotation is roll.

The rotation is positive in the counter-clockwise direction. That is, an observer looking from a positive location on one of the axes would see positive rotation on that axis when it appeared to be rotating counter-clockwise.

The values are not affected by the app's screen orientation changing from landscape to portrait or vice versa. Only physical device rotation matters.

The values display changes in physical rotation, not the current rotation angle, nor screen orientation.


Get gamepad type

Expressions ›› Gamepad ›› Metadata ››

This returns the given gamepad's basic gamepad type.

SDL noticing these types, or a controller type not being in this list, does not affect the joystick -> gamepad mapping , which remains mapped to XBox 360 controller.

Remember, if a controller is not supported, you can add it with Gamepad > (Advanced) Add controller mapping from file action.

The return text possibilities are:

"Unknown", "Virtual",

"XBox 360", "XBox One", "PlayStation 3", "PlayStation 4", "PlayStation 5",

"Nintendo Switch Pro", "Nintendo Switch Joycon (Left)", "Nintendo Switch Joycon (Right)", "Nintendo Switch Joycon (Pair)",

"Amazon Luna", "Google Stadia", and "NVIDIA Shield".

Joy-cons are either considered separate joystick/gamepad devices, or a single one, based on the object property HIDAPI: Combine Joy-Con pairs .


Get mapping text

Expressions ›› Gamepad ›› Metadata ››

This returns the mapping text for the given gamepad device ID .

The mapping decides how to interpret a device's input and map it to a XBox 360 controller's physical layout.


Properties

SDL Joystick has 11 properties.

Activate joystick interface ticking

Default: Enabled.

The Joystick interface reports exactly how the controllers report: some will say axis 0 is the left stick, some will say it is the right stick, and so on.

It is not recommended you use the Joystick interface due to this confusion.

If you choose not to use it, you can save performance by unchecking this.

This setting will enable or disable most of the Joystick ticking interface; so it will stop updating axis and button conditions/expressions.

Device plug/unplug, and joystick metadata, such as number of axes or number of buttons, will still be usable.

Use button labels, not position

Default: Disabled – use position.

Gamepad interface simplifies all controllers into the Xbox Controller physical layout, so if you design your game for Xbox, you will have designed it for Nintendo Switch, PS4 controller, and so on.

This setting switches Gamepad into reporting buttons based on their label, rather than their position. For example:

(Y) (X)

(X) (B) (Y) (A)

(A) (B)

XBox 360 Nintendo

Switch

By default, mapping by physical position , the Switch's X button will trigger On Y pressed, as it is where the Y key is on the XBox controller's layout.

But if this property is enabled, the Switch's X button will trigger On X pressed.

  • If you map by position, you won't know the button labels, which may confuse you as a developer, but you will know the physical layout by using an XBox controller or the image in Gamepad mapping as a guide.
  • If you map by label, you will have a layout that differs per controller, which will may be a layout that's confusing to use for your users.
    For example, mapping by label, if you expect users to move up with Y button, on a Switch they'll have to press the left button to move up instead.

Enable Steam controllers

Default: Enabled.

Whether SDL should search for Steam controllers when it hunts for devices, or ignore them.

HIDAPI: Enable Bluetooth PS4/PS5 ext. reports

Default: Disabled.

PlayStation 4 and 5 controllers have two reporting modes: extended and basic. Usually, basic reporting is used, which does not allow Rumble in Bluetooth, although it does allow it if the controller is plugged in via USB.

If extended reporting is used, any joystick-using app that uses DirectInput to access controllers will not be able to use the PS4/PS5 controller.

The controller won't switch back to basic reporting until it is powered off and back on again.

Enable this property if you want to use rumble on Bluetooth PS4/PS5 controllers (and don't care about bullying older apps).

HIDAPI: Combine Joy-Con pairs

Default: Enabled.

If enabled, the two halves of a Joy-Con controller will be considered one controller. If disabled, they will be reported as two separate controllers.

HIDAPI: Use Joy-Con in vertical mode

Default: Disabled.

If enabled, Joy-Cons are expected to be held vertically. If disabled, they're expected to be held horizontally.

HIDAPI: Set player LEDs

Default: Enabled

If enabled, for controllers that have player LEDs, like the Nintendo Switch, this sets them to the player number used.

HIDAPI: Change home button light

Default: Leave as is

If set to turn on, the home button's backlight is turned on when the application sees a controller with a Home button, like the Nintendo Switch.

If set to turn off, the home button's backlight is turned off.

If set to leave as is, the home button's backlight is left however it initially was.

Enable ROG Chakram mouse as joystick

Default: Disabled

If set to enabled, Rog Chakram mice (which feature a joystick on the side), are reported as joysticks. If disabled, they are not recognised by SDL.

Android/iOS: Read accelerometer as joystick

Default: Disabled.

If set to enabled, the Android/iOS device's built-in accelerometer, used for changing screen orientation, for compass, etc., is added as a joystick.

If disabled, this does not occur and only controllers connected to the Android/iOS device (via Bluetooth or wires) can be used.

Default direction mode

Default: 32-dir.

This controls the default stick/direction pad direction mode. This is used to divide joystick movements into discrete directions. For a full explanation

Modes include 4-dir, 4-dir (diagonal), 8-dir, 16-dir, 32-dir, and angle.

For example, in different modes, the right direction will include this range on the stick:

Image showing the right direction is a 45 degree range in 4 direction mode, and a 22.5 degree range for 8 direction mode.

(Center is not included in the direction count, so 8-dir really has 9 positions.)

The 32-dir mode means 360 degrees divided into 32 directions, so you get 11.25 degrees for each of the 32 directions. Which means you can move a joystick just shy of 11.25 degrees before SDL Joystick will trigger a stick changed direction event. (Or move it to/from center position)

The 8-dir mode means 360 degrees divided into 8 directions, so you get 45 degrees for each. So you can move the joystick 45 degrees before you move to a new direction.

The 4-dir mode is left, up, down, right; the 4-dir diagonal is top-left, top-right, bottom-left, bottom-right.

Note the angle mode, while it will likely trigger direction changes each Fusion tick, will still not prevent you using the Gamepad > Check stick/D-Pad direction condition in an expected way. The angle is rounded to the nearest 32-dir.

This direction mode object property is used for new controllers, but can be changed by the Gamepad > Set stick/D-Pad direction mode action.