Add Steam controller input manager and integrate with Steam API

This commit is contained in:
2025-05-03 05:23:17 +02:00
parent 476403f89c
commit 218ef9fc19
5 changed files with 207 additions and 5 deletions

View File

@@ -0,0 +1,169 @@
extends Node
## A globally accessible manager for device-specific actions using SteamInput for any controller
## and standard Godot Input for the keyboard.
##
## All methods in this class that have a "device" parameter can accept -1
## which means the keyboard device.
# The actions defined in the Steam .vdf file are listed here
# with true or false indicating if input is analog or digital.
# False is digital (buttons), true is analog (joysticks, triggers, etc).
var action_names := {
"move": true,
"jump": false,
"left": false,
"right": false,
"down": false,
"up": false,
"interact": false,
"pause": false,
}
var got_handles := false
var game_action_set
var current_action_set
var actions := {}
# Store the state of each action and the frame it entered that state.
var action_states := {}
func init() -> void:
Steam.input_device_connected.connect(_on_steam_input_device_connected)
Steam.input_device_disconnected.connect(_on_steam_input_device_disconnected)
func _on_steam_input_device_connected(input_handle: int) -> void:
if not got_handles:
get_handles()
Steam.activateActionSet(input_handle, current_action_set)
print("Device connected %s" % str(input_handle))
func _on_steam_input_device_disconnected(input_handle: int) -> void:
print("Device disconnected %s" % str(input_handle))
func get_handles() -> void:
got_handles = true
game_action_set = Steam.getActionSetHandle("GameControls")
current_action_set = game_action_set
get_action_handles(action_names)
func get_action_handles(action_names: Dictionary) -> void:
for action in action_names.keys():
if action_names[action]:
actions[action] = Steam.getAnalogActionHandle(action)
else:
actions[action] = Steam.getDigitalActionHandle(action)
func get_controllers() -> Array[int]:
var controllers: Array[int] = [-1]
var steam_controllers = Steam.getConnectedControllers()
if steam_controllers:
controllers.append_array(steam_controllers)
return controllers
func get_action_strength(device: int, action: StringName, exact_match: bool = false) -> float:
if device >= 0:
if not got_handles: return 0
var action_data = Steam.getAnalogActionData(device, actions[action])
return action_data.x
return Input.get_action_strength(action, exact_match)
func get_axis(device: int, negative_action: StringName, positive_action: StringName) -> float:
if device >= 0:
if not got_handles: return 0
var negative = Steam.getAnalogActionData(device, actions[negative_action])
var positive = Steam.getAnalogActionData(device, actions[positive_action])
return positive.x - negative.x
return Input.get_axis(negative_action, positive_action)
func get_vector(device: int, negative_x: StringName, positive_x: StringName, negative_y: StringName, positive_y: StringName, deadzone: float = -1.0) -> Vector2:
if device >= 0:
if not got_handles: return Vector2.ZERO
var negative_x_val = Steam.getAnalogActionData(device, actions[negative_x])
var positive_x_val = Steam.getAnalogActionData(device, actions[positive_x])
var negative_y_val = Steam.getAnalogActionData(device, actions[negative_y])
var positive_y_val = Steam.getAnalogActionData(device, actions[positive_y])
# Steam's y axis is inverted compared to Godot
return Vector2(positive_x_val - negative_x_val, -(positive_y_val - negative_y_val)).normalized()
return Input.get_vector(negative_x, positive_x, negative_y, positive_y, deadzone)
func get_move_input(device: int) -> Vector2:
if device >= 0:
if not got_handles: return Vector2.ZERO
# Get the analog stick movement
var action_data = Steam.getAnalogActionData(device, actions["Move"])
return Vector2(action_data.x, -action_data.y).normalized()
return Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down")).normalized()
func get_action_state(device: int, action: String) -> Dictionary:
# Get the current action, but create the defaults along the way if they don't exist.
if not action_states.get(device):
action_states[device] = {}
if not action_states[device].get(action):
action_states[device][action] = { "held": false, "press_frame": -1, "release_frame": -1 }
return action_states[device][action]
func set_action_state(device: int, action: StringName, currently_held: bool, current_frame: int) -> Dictionary:
# Get the state of the action last frame
var previous_action_state := get_action_state(device, action)
# If we're pressing the action now and we weren't pressing it last frame,
# track that we pressed the action this frame.
if currently_held and not previous_action_state.held:
action_states[device][action].held = true
action_states[device][action].press_frame = current_frame
# If we're not pressing it this frame but we were pressing it last frame,
# track that we released the action this frame.
elif not currently_held and previous_action_state.held:
action_states[device][action].held = false
action_states[device][action].release_frame = current_frame
# Return the current state of the action
return action_states[device][action]
func is_action_pressed(device: int, action: StringName, exact_match: bool = false) -> bool:
if device >= 0:
if not got_handles: return false
var current_frame := Engine.get_process_frames()
var currently_held = Steam.getDigitalActionData(device, actions[action]).state
set_action_state(device, action, currently_held, current_frame)
return currently_held
# If keyboard, use normal Godot input system.
return Input.is_action_pressed(action, exact_match)
func is_action_just_pressed(device: int, action: StringName, exact_match: bool = false) -> bool:
if device >= 0:
if not got_handles: return false
var current_frame := Engine.get_process_frames()
var currently_held = Steam.getDigitalActionData(device, actions[action]).state
var action_state := set_action_state(device, action, currently_held, current_frame)
return currently_held and action_state.press_frame == current_frame
# If keyboard, use normal Godot input system.
return Input.is_action_just_pressed(action, exact_match)
func is_action_just_released(device: int, action: StringName, exact_match: bool = false) -> bool:
if device >= 0:
if not got_handles: return false
var current_frame := Engine.get_process_frames()
var currently_held = Steam.getDigitalActionData(device, actions[action]).state
var action_state := set_action_state(device, action, currently_held, current_frame)
return not currently_held and action_state.release_frame == current_frame
# If keyboard, use normal Godot input system.
return Input.is_action_just_released(action, exact_match)

View File

@@ -6,5 +6,4 @@
[node name="Background Music" type="AudioStreamPlayer" parent="."] [node name="Background Music" type="AudioStreamPlayer" parent="."]
stream = ExtResource("1_fuc3i") stream = ExtResource("1_fuc3i")
autoplay = true
parameters/looping = true parameters/looping = true

View File

@@ -30,6 +30,12 @@ GameManager="*res://objects/game_manager.tscn"
PhantomCameraManager="*res://addons/phantom_camera/scripts/managers/phantom_camera_manager.gd" PhantomCameraManager="*res://addons/phantom_camera/scripts/managers/phantom_camera_manager.gd"
GUIDE="*res://addons/guide/guide.gd" GUIDE="*res://addons/guide/guide.gd"
AudioController="*res://objects/audio_controller.tscn" AudioController="*res://objects/audio_controller.tscn"
SteamIntegrationNode="*res://objects/steam_integration.tscn"
SteamControllerInput="*res://autoloads/steam_controller_input.gd"
[debug]
file_logging/enable_file_logging=true
[display] [display]
@@ -77,6 +83,11 @@ jump={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
] ]
} }
up={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
down={ down={
"deadzone": 0.5, "deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
@@ -111,6 +122,7 @@ show_marketplace={
[physics] [physics]
2d/physics_engine="GodotPhysics2D" 2d/physics_engine="GodotPhysics2D"
common/physics_interpolation=true
[rendering] [rendering]

View File

@@ -534,6 +534,7 @@ metadata/_edit_group_ = true
[node name="Camera2D" type="Camera2D" parent="."] [node name="Camera2D" type="Camera2D" parent="."]
physics_interpolation_mode = 1 physics_interpolation_mode = 1
position = Vector2(903, -118) position = Vector2(903, -118)
process_callback = 0
limit_left = 0 limit_left = 0
limit_top = -896 limit_top = -896
limit_right = 4128 limit_right = 4128

View File

@@ -1,13 +1,34 @@
class_name SteamIntegration class_name SteamIntegration
extends Node extends Node
var app_id: String = "3575090" var app_id: String = "3575090"
var is_on_steam_deck: bool = false
var is_online: bool = false
var has_bought_game: bool = false
func _init() -> void: func _init() -> void:
OS.set_environment("STEAM_APP_ID", app_id) OS.set_environment("SteamAppId", app_id)
OS.set_environment("STEAM_GAME_ID", app_id) OS.set_environment("SteamGameId", app_id)
func _ready() -> void: func _ready() -> void:
pass Steam.steamInit()
Steam.enableDeviceCallbacks()
SteamControllerInput.init()
var is_running := Steam.isSteamRunning()
if !is_running:
print("Steam is not running.")
return
print("Steam is running.")
is_on_steam_deck = Steam.isSteamRunningOnSteamDeck()
is_online = Steam.loggedOn()
has_bought_game = Steam.isSubscribed()
if not has_bought_game:
print("You have not bought the game.")
get_tree().quit(69)