WIP: implement full support for Steam Input
This commit is contained in:
parent
688c56a564
commit
c3167f663b
|
|
@ -0,0 +1,77 @@
|
||||||
|
"In Game Actions"
|
||||||
|
{
|
||||||
|
"actions"
|
||||||
|
{
|
||||||
|
"GameControls"
|
||||||
|
{
|
||||||
|
"title" "#Set_GameControls"
|
||||||
|
"StickPadGyro"
|
||||||
|
{
|
||||||
|
"move"
|
||||||
|
{
|
||||||
|
"title" "#Action_Move"
|
||||||
|
"input_mode" "joystick_move"
|
||||||
|
}
|
||||||
|
"look"
|
||||||
|
{
|
||||||
|
"title" "#Action_Look"
|
||||||
|
"input_mode" "absolute_mouse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"Button"
|
||||||
|
{
|
||||||
|
"interact" "#Action_Interact"
|
||||||
|
"back" "#Action_Back"
|
||||||
|
"crouch" "#Action_Crouch"
|
||||||
|
"zoom" "#Action_Zoom"
|
||||||
|
"skip" "#Action_Skip"
|
||||||
|
"pause" "#Action_Pause"
|
||||||
|
"summarize" "#Action_Summarize"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"MenuControls"
|
||||||
|
{
|
||||||
|
"title" "#Set_MenuControls"
|
||||||
|
"Button"
|
||||||
|
{
|
||||||
|
"menu_up" "#Menu_Up"
|
||||||
|
"menu_down" "#Menu_Down"
|
||||||
|
"menu_left" "#Menu_Left"
|
||||||
|
"menu_right" "#Menu_Right"
|
||||||
|
"accept" "#Menu_Accept"
|
||||||
|
"cancel" "#Menu_Cancel"
|
||||||
|
"focus_next" "#Menu_FocusNext"
|
||||||
|
"focus_previous" "#Menu_FocusPrevious"
|
||||||
|
"scroll_up" "#Menu_ScrollUp"
|
||||||
|
"scroll_down" "#Menu_ScrollDown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"localization"
|
||||||
|
{
|
||||||
|
"english"
|
||||||
|
{
|
||||||
|
"Set_GameControls" "Game Controls"
|
||||||
|
"Set_MenuControls" "Menu Controls"
|
||||||
|
"Action_Move" "Move"
|
||||||
|
"Action_Look" "Look"
|
||||||
|
"Action_Interact" "Pick up"
|
||||||
|
"Action_Back" "Go Back"
|
||||||
|
"Action_Crouch" "Crouch"
|
||||||
|
"Action_Zoom" "Zoom"
|
||||||
|
"Action_Skip" "Skip"
|
||||||
|
"Action_Pause" "Pause Game"
|
||||||
|
"Action_Summarize" "Summarize Content"
|
||||||
|
"Menu_Up" "Menu Up"
|
||||||
|
"Menu_Down" "Menu Down"
|
||||||
|
"Menu_Left" "Menu Left"
|
||||||
|
"Menu_Right" "Menu Right"
|
||||||
|
"Menu_Accept" "Accept"
|
||||||
|
"Menu_Canel" "Cancel"
|
||||||
|
"Menu_FocusNext" "Focus Next"
|
||||||
|
"Menu_FocusPrevious" "Focus Previous"
|
||||||
|
"Menu_ScrollUp" "Scroll Up"
|
||||||
|
"Menu_ScrollDown" "Scroll Down"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
class_name SteamInputGlypthDisplay extends TextureRect
|
||||||
|
|
||||||
|
@export var action_name: StringName
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
SteamInputManager.glyphs_loaded.connect(_update_glyph)
|
||||||
|
texture = PlaceholderTexture2D.new()
|
||||||
|
texture.size = Vector2(64, 64)
|
||||||
|
|
||||||
|
func _update_glyph() -> void:
|
||||||
|
texture = SteamInputManager.get_glyph(action_name)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cgfyo6u8wygjr
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal glyphs_loaded
|
||||||
|
|
||||||
|
# True means analog action!
|
||||||
|
const game_control_actios := {
|
||||||
|
"move": true,
|
||||||
|
"look": true,
|
||||||
|
"interact": false,
|
||||||
|
"back": false,
|
||||||
|
"crouch": false,
|
||||||
|
"zoom": false,
|
||||||
|
"skip": false,
|
||||||
|
"pause": false,
|
||||||
|
"summarize": false
|
||||||
|
}
|
||||||
|
|
||||||
|
const menu_control_actions := {
|
||||||
|
"menu_up": false,
|
||||||
|
"menu_down": false,
|
||||||
|
"menu_left": false,
|
||||||
|
"menu_right": false,
|
||||||
|
"accept": false,
|
||||||
|
"cancel": false,
|
||||||
|
"focus_next": false,
|
||||||
|
"focus_previous": false,
|
||||||
|
"scroll_up": false,
|
||||||
|
"scroll_down": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
var action_set_ids: Dictionary[StringName, int] = {"game_control_actios": -1, "menu_control_actions": -1}
|
||||||
|
|
||||||
|
var controller_id := -1
|
||||||
|
var got_handles := false
|
||||||
|
var actions := {}
|
||||||
|
var analog_actions: PackedStringArray = []
|
||||||
|
var action_states: Dictionary[StringName, SteamActionState] = {}
|
||||||
|
|
||||||
|
var current_action_set: int:
|
||||||
|
set(value):
|
||||||
|
if Steam.isSteamRunning():
|
||||||
|
Steam.activateActionSet(controller_id, value)
|
||||||
|
current_action_set = value
|
||||||
|
|
||||||
|
var action_glyphs: Dictionary[StringName, Texture2D]= {}
|
||||||
|
var cooldown: float = 0.0
|
||||||
|
var gdt_cooldown: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func init() -> void:
|
||||||
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||||
|
Steam.input_device_connected.connect(_on_controller_connect)
|
||||||
|
Steam.input_device_disconnected.connect(_on_controller_disconnect)
|
||||||
|
Steam.runFrame()
|
||||||
|
State.game_context_updated.connect(_on_set_change)
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
Steam.runFrame()
|
||||||
|
if cooldown > 0:
|
||||||
|
cooldown -= delta
|
||||||
|
if gdt_cooldown > 0:
|
||||||
|
gdt_cooldown -= delta
|
||||||
|
|
||||||
|
if controller_id != -1 and State.last_input_source == State.InputSource.KEYBOARD_MOUSE:
|
||||||
|
for action in actions.keys():
|
||||||
|
if Steam.getDigitalActionData(controller_id, actions[action]).state:
|
||||||
|
State.last_input_source = State.InputSource.JOYPAD
|
||||||
|
break
|
||||||
|
|
||||||
|
func kill():
|
||||||
|
State.game_context_updated.disconnect(_on_set_change)
|
||||||
|
|
||||||
|
|
||||||
|
func si_to_godot(event_action: StringName):
|
||||||
|
if State.last_input_source == State.InputSource.JOYPAD and gdt_cooldown <= 0:
|
||||||
|
gdt_cooldown = 0.01
|
||||||
|
var event := InputEventAction.new()
|
||||||
|
event.action = event_action
|
||||||
|
event.pressed = true
|
||||||
|
Input.parse_input_event(event)
|
||||||
|
|
||||||
|
var event_release := InputEventAction.new()
|
||||||
|
event_release.action = event_action
|
||||||
|
event_release.pressed = false
|
||||||
|
Input.parse_input_event(event_release)
|
||||||
|
|
||||||
|
|
||||||
|
func get_handles() -> void:
|
||||||
|
if controller_id != -1:
|
||||||
|
for key in action_set_ids.keys():
|
||||||
|
action_set_ids[key] = Steam.getActionSetHandle(key)
|
||||||
|
action_set_ids[key] = Steam.getActionSetHandle(key)
|
||||||
|
get_action_handles()
|
||||||
|
got_handles = true
|
||||||
|
|
||||||
|
|
||||||
|
func get_action_handles() -> void:
|
||||||
|
for action_set_key in action_set_ids.keys():
|
||||||
|
for action in get(action_set_key).keys():
|
||||||
|
if game_control_actios[action]:
|
||||||
|
actions[action] = Steam.getAnalogActionHandle(action)
|
||||||
|
analog_actions.append(action)
|
||||||
|
else:
|
||||||
|
actions[action] = Steam.getDigitalActionHandle(action)
|
||||||
|
|
||||||
|
func get_action_strength(action: StringName, device: int = controller_id, exact_match: bool = false) -> float:
|
||||||
|
if device != -1:
|
||||||
|
if got_handles:
|
||||||
|
return Steam.getAnalogActionData(device, actions[action]).x
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
return Input.get_action_strength(action, exact_match)
|
||||||
|
|
||||||
|
|
||||||
|
func get_axis(base_name: StringName, device: int = controller_id) -> Vector2:
|
||||||
|
if device != -1:
|
||||||
|
if not got_handles: return Vector2.ZERO
|
||||||
|
|
||||||
|
var action_data = Steam.getAnalogActionData(device, actions[base_name])
|
||||||
|
return Vector2(action_data.x, -action_data.y).normalized()
|
||||||
|
return Vector2(Input.get_axis("%s_left" % base_name, "%s_right"), Input.get_axis("%s_positive", "%s_negative")).normalized()
|
||||||
|
|
||||||
|
|
||||||
|
func is_action_pressed(action: StringName, device: int = controller_id, exact_match: bool = false) -> bool:
|
||||||
|
if device != -1:
|
||||||
|
if not got_handles: return false
|
||||||
|
|
||||||
|
var current_frame = Engine.get_process_frames()
|
||||||
|
if State.last_input_source == State.InputSource.JOYPAD:
|
||||||
|
var currently_held = Steam.getDigitalActionData(device, actions[action]).state
|
||||||
|
set_action_state(action, currently_held, current_frame)
|
||||||
|
return currently_held
|
||||||
|
|
||||||
|
return Input.is_action_pressed(action, exact_match)
|
||||||
|
|
||||||
|
func is_action_just_pressed(action: StringName, device: int = controller_id, exact_match: bool = false) -> bool:
|
||||||
|
if device >= 0:
|
||||||
|
if not got_handles:
|
||||||
|
return false
|
||||||
|
|
||||||
|
if cooldown <= 0:
|
||||||
|
var current_frame = Engine.get_process_frames()
|
||||||
|
if State.last_input_source == State.InputSource.JOYPAD:
|
||||||
|
var currently_held = Steam.getDigitalActionData(device, actions[action]).state
|
||||||
|
var action_state = set_action_state(action, currently_held, current_frame)
|
||||||
|
if currently_held:
|
||||||
|
cooldown = 0.1
|
||||||
|
return currently_held and action_state.press_frame == current_frame
|
||||||
|
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
|
return Input.is_action_just_pressed(action, exact_match)
|
||||||
|
|
||||||
|
func is_action_just_released(action: StringName, device: int = controller_id, 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(action, currently_held, current_frame)
|
||||||
|
return not currently_held and action_state.release_frame == current_frame
|
||||||
|
|
||||||
|
return Input.is_action_just_released(action, exact_match)
|
||||||
|
|
||||||
|
func get_action_state(action: String) -> SteamActionState:
|
||||||
|
if not action_states.has(action):
|
||||||
|
action_states[action] = SteamActionState.new()
|
||||||
|
return action_states[action]
|
||||||
|
|
||||||
|
|
||||||
|
func set_action_state(action: StringName, currently_held: bool, current_frame: int) -> SteamActionState:
|
||||||
|
var previous_action_state = get_action_state(action)
|
||||||
|
|
||||||
|
if currently_held and not previous_action_state.held:
|
||||||
|
action_states[action].pressed = true
|
||||||
|
action_states[action].frame_pressed = current_frame
|
||||||
|
|
||||||
|
elif not currently_held and previous_action_state.pressed:
|
||||||
|
action_states[action].pressed = false
|
||||||
|
action_states[action].frame_released = current_frame
|
||||||
|
|
||||||
|
return action_states[action]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func _on_controller_connect(input_handle) -> void:
|
||||||
|
controller_id = input_handle
|
||||||
|
get_handles()
|
||||||
|
# a timer for a delayed reload of glyphs/icons as sometimes, Steam loads the wrong ones
|
||||||
|
|
||||||
|
call_deferred("_delayed_reload", [10])
|
||||||
|
|
||||||
|
Steam.run_callbacks()
|
||||||
|
Steam.runFrame()
|
||||||
|
|
||||||
|
_preload_glyphs()
|
||||||
|
set_glyphs()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_controller_disconnect(_input_handle) -> void:
|
||||||
|
actions.clear()
|
||||||
|
action_states.clear()
|
||||||
|
action_glyphs.clear()
|
||||||
|
got_handles = false
|
||||||
|
# to pause the game if controller is disconnected while in game
|
||||||
|
get_node("/root/Game").on_controller_disconnect()
|
||||||
|
controller_id = -1
|
||||||
|
set_glyphs()
|
||||||
|
State.last_input_source = State.InputSource.KEYBOARD_MOUSE
|
||||||
|
|
||||||
|
|
||||||
|
func _on_set_change(new_context: State.GameContext) -> void:
|
||||||
|
current_action_set = action_set_ids["game_control_actios"] if new_context == State.GameContext.WALK else action_set_ids["menu_control_actions"]
|
||||||
|
|
||||||
|
|
||||||
|
func _preload_glyphs() -> void:
|
||||||
|
var action_sets := action_set_ids.values()
|
||||||
|
|
||||||
|
for set_id in action_sets:
|
||||||
|
Steam.activateActionSet(controller_id, set_id)
|
||||||
|
Steam.runFrame()
|
||||||
|
|
||||||
|
for action_name in actions.keys():
|
||||||
|
var origins := []
|
||||||
|
|
||||||
|
if analog_actions.has(action_name):
|
||||||
|
origins = Steam.getAnalogActionOrigins(controller_id, set_id, actions[action_name])
|
||||||
|
else:
|
||||||
|
origins = Steam.getDigitalActionOrigins(controller_id, set_id, actions[action_name])
|
||||||
|
|
||||||
|
if origins.size() == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
var style := Steam.INPUT_GLYPH_STYLE_DARK | Steam.INPUT_GLYPH_STYLE_SOLID_ABXY
|
||||||
|
var path = Steam.getGlyphPNGForActionOrigin(origins[0], Steam.INPUT_GLYPH_SIZE_LARGE, style)
|
||||||
|
var img = Image.new()
|
||||||
|
if img.load(path) == OK:
|
||||||
|
var tex :Texture2D = ImageTexture.create_from_image(img)
|
||||||
|
action_glyphs[action_name] = tex
|
||||||
|
glyphs_loaded.emit()
|
||||||
|
|
||||||
|
|
||||||
|
func get_glyph(action_name: StringName) -> Texture2D:
|
||||||
|
if action_glyphs.has(action_name):
|
||||||
|
return action_glyphs[action_name]
|
||||||
|
else:
|
||||||
|
var placeholder = PlaceholderTexture2D.new()
|
||||||
|
placeholder.size = Vector2(64, 64)
|
||||||
|
return placeholder
|
||||||
|
|
||||||
|
|
||||||
|
func _delayed_reload(delay: float):
|
||||||
|
await get_tree().create_timer(delay).timeout
|
||||||
|
var current_set: int = current_action_set
|
||||||
|
_preload_glyphs()
|
||||||
|
set_glyphs()
|
||||||
|
current_action_set = current_set
|
||||||
|
Steam.runFrame()
|
||||||
|
|
||||||
|
func set_glyphs():
|
||||||
|
pass
|
||||||
|
|
||||||
|
##TODO: update controller state in state!!!
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://10u2ea21g7sy
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
class_name SteamActionState extends Object
|
||||||
|
|
||||||
|
var pressed: bool
|
||||||
|
var frame_pressed: int
|
||||||
|
var frame_released: int
|
||||||
|
|
||||||
|
func _init(is_pressed: bool = false, first_frame_pressed: int = -1, first_frame_released: int = -1):
|
||||||
|
self.pressed = is_pressed
|
||||||
|
self.frame_pressed = first_frame_pressed
|
||||||
|
self.frame_released = first_frame_released
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://ba1kym4lim7lq
|
||||||
Loading…
Reference in New Issue