gd: added input addon

This commit is contained in:
2025-05-27 19:20:46 +02:00
parent d8a1604af9
commit c8d8c7ec25
683 changed files with 21608 additions and 2 deletions

View File

@ -0,0 +1,254 @@
@tool
@icon("res://addons/guide/guide_action.svg")
class_name GUIDEAction
extends Resource
enum GUIDEActionValueType {
BOOL = 0,
AXIS_1D = 1,
AXIS_2D = 2,
AXIS_3D = 3
}
enum GUIDEActionState {
TRIGGERED,
ONGOING,
COMPLETED
}
## The name of this action. Required when this action should be used as
## Godot action. Also displayed in the debugger.
@export var name:StringName:
set(value):
if name == value:
return
name = value
emit_changed()
## The action value type.
@export var action_value_type: GUIDEActionValueType = GUIDEActionValueType.BOOL:
set(value):
if action_value_type == value:
return
action_value_type = value
emit_changed()
## If this action triggers, lower-priority actions cannot trigger
## if they share input with this action unless these actions are
## chorded with this action.
@export var block_lower_priority_actions:bool = true:
set(value):
if block_lower_priority_actions == value:
return
block_lower_priority_actions = value
emit_changed()
@export_category("Godot Actions")
## If true, then this action will be emitted into Godot's
## built-in action system. This can be helpful to interact with
## code using this system, like Godot's UI system. Actions
## will be emitted on trigger and completion (e.g. button down
## and button up).
@export var emit_as_godot_actions:bool = false:
set(value):
if emit_as_godot_actions == value:
return
emit_as_godot_actions = value
emit_changed()
@export_category("Action Remapping")
## If true, players can remap this action. To be remappable, make sure
## that a name and the action type are properly set.
@export var is_remappable:bool:
set(value):
if is_remappable == value:
return
is_remappable = value
emit_changed()
## The display name of the action shown to the player.
@export var display_name:String:
set(value):
if display_name == value:
return
display_name = value
emit_changed()
## The display category of the action shown to the player.
@export var display_category:String:
set(value):
if display_category == value:
return
display_category = value
emit_changed()
## Emitted every frame while the action is triggered.
signal triggered()
## Emitted when the action started evaluating.
signal started()
## Emitted every frame while the action is still evaluating.
signal ongoing()
## Emitted when the action finished evaluating.
signal completed()
## Emitted when the action was cancelled.
signal cancelled()
var _last_state:GUIDEActionState = GUIDEActionState.COMPLETED
var _value_bool:bool
## Returns the value of this action as bool.
var value_bool:bool:
get: return _value_bool
## Returns the value of this action as float.
var value_axis_1d:float:
get: return _value.x
var _value_axis_2d:Vector2 = Vector2.ZERO
## Returns the value of this action as Vector2.
var value_axis_2d:Vector2:
get: return _value_axis_2d
var _value:Vector3 = Vector3.ZERO
## Returns the value of this action as Vector3.
var value_axis_3d:Vector3:
get: return _value
var _elapsed_seconds:float
## The amount of seconds elapsed since the action started evaluating.
var elapsed_seconds:float:
get: return _elapsed_seconds
var _elapsed_ratio:float
## The ratio of the elapsed time to the hold time. This is a percentage
## of the hold time that has passed. If the action has no hold time, this will
## be 0 when the action is not triggered and 1 when the action is triggered.
## Otherwise, this will be a value between 0 and 1.
var elapsed_ratio:float:
get: return _elapsed_ratio
var _triggered_seconds:float
## The amount of seconds elapsed since the action triggered.
var triggered_seconds:float:
get: return _triggered_seconds
## This is a hint for how long the input must remain actuated (in seconds) before the action triggers.
## It depends on the mapping in which this action is used. If the mapping has no hold trigger it will be -1.
## In general, you should not access this variable directly, but rather the `elapsed_ratio` property of the action
## which is a percentage of the hold time that has passed.
var _trigger_hold_threshold:float = -1.0
func _triggered(value:Vector3, delta:float) -> void:
_triggered_seconds += delta
_elapsed_ratio = 1.0
_update_value(value)
_last_state = GUIDEActionState.TRIGGERED
triggered.emit()
_emit_godot_action_maybe(true)
func _started(value:Vector3) -> void:
_elapsed_ratio = 0.0
_update_value(value)
_last_state = GUIDEActionState.ONGOING
started.emit()
ongoing.emit()
func _ongoing(value:Vector3, delta:float) -> void:
_elapsed_seconds += delta
if _trigger_hold_threshold > 0:
_elapsed_ratio = _elapsed_seconds / _trigger_hold_threshold
_update_value(value)
var was_triggered:bool = _last_state == GUIDEActionState.TRIGGERED
_last_state = GUIDEActionState.ONGOING
ongoing.emit()
# if the action reverts from triggered to ongoing, this counts as
# releasing the action for the godot action system.
if was_triggered:
_emit_godot_action_maybe(false)
func _cancelled(value:Vector3) -> void:
_elapsed_seconds = 0
_elapsed_ratio = 0
_update_value(value)
_last_state = GUIDEActionState.COMPLETED
cancelled.emit()
completed.emit()
func _completed(value:Vector3) -> void:
_elapsed_seconds = 0
_elapsed_ratio = 0
_triggered_seconds = 0
_update_value(value)
_last_state = GUIDEActionState.COMPLETED
completed.emit()
_emit_godot_action_maybe(false)
func _emit_godot_action_maybe(pressed:bool) -> void:
if not emit_as_godot_actions:
return
if name.is_empty():
push_error("Cannot emit action into Godot's system because name is empty.")
return
var godot_action = InputEventAction.new()
godot_action.action = name
godot_action.strength = _value.x
godot_action.pressed = pressed
Input.parse_input_event(godot_action)
func _update_value(value:Vector3):
match action_value_type:
GUIDEActionValueType.BOOL, GUIDEActionValueType.AXIS_1D:
_value_bool = abs(value.x) > 0
_value_axis_2d = Vector2(abs(value.x), 0)
_value = Vector3(value.x, 0, 0)
GUIDEActionValueType.AXIS_2D:
_value_bool = abs(value.x) > 0
_value_axis_2d = Vector2(value.x, value.y)
_value = Vector3(value.x, value.y, 0)
GUIDEActionValueType.AXIS_3D:
_value_bool = abs(value.x) > 0
_value_axis_2d = Vector2(value.x, value.y)
_value = value
## Returns whether the action is currently triggered. Can be used for a
## polling style input.
func is_triggered() -> bool:
return _last_state == GUIDEActionState.TRIGGERED
## Returns whether the action is currently completed. Can be used for a
## polling style input.
func is_completed() -> bool:
return _last_state == GUIDEActionState.COMPLETED
## Returns whether the action is currently completed. Can be used for a
## polling style input.
func is_ongoing() -> bool:
return _last_state == GUIDEActionState.ONGOING
func _editor_name() -> String:
# Try to give the most user friendly name
if display_name != "":
return display_name
if name != "":
return name
return resource_path.get_file().replace(".tres", "")