Add Steam controller input manager and integrate with Steam API
This commit is contained in:
169
autoloads/steam_controller_input.gd
Normal file
169
autoloads/steam_controller_input.gd
Normal 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)
|
@@ -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
|
||||||
|
@@ -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]
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
|
Reference in New Issue
Block a user