gd: added input addon
This commit is contained in:
49
addons/guide/inputs/guide_input.gd
Normal file
49
addons/guide/inputs/guide_input.gd
Normal file
@ -0,0 +1,49 @@
|
||||
@tool
|
||||
@icon("res://addons/guide/inputs/guide_input.svg")
|
||||
## A class representing some actuated input.
|
||||
class_name GUIDEInput
|
||||
extends Resource
|
||||
|
||||
## The current valueo f this input. Depending on the input type only parts of the
|
||||
## returned vector may be relevant.
|
||||
var _value:Vector3 = Vector3.ZERO
|
||||
|
||||
## The current input state. This will be set by GUIDE when the input is used.
|
||||
var _state:GUIDEInputState = null
|
||||
|
||||
## Whether this input needs a reset per frame. _input is only called when
|
||||
## there is input happening, but some GUIDE inputs may need to be reset
|
||||
## in the absence of input.
|
||||
func _needs_reset() -> bool:
|
||||
return false
|
||||
|
||||
## Resets the input value to the default value. Is called once per frame if
|
||||
## _needs_reset returns true.
|
||||
func _reset() -> void:
|
||||
_value = Vector3.ZERO
|
||||
|
||||
## Returns whether this input is the same input as the other input.
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return false
|
||||
|
||||
## Called when the input is started to be used by GUIDE. Can be used to perform
|
||||
## initializations. The state object can be used to subscribe to input events
|
||||
## and to get the current input state.
|
||||
func _begin_usage() -> void :
|
||||
pass
|
||||
|
||||
## Called, when the input is no longer used by GUIDE. Can be used to perform
|
||||
## cleanup.
|
||||
func _end_usage() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return ""
|
||||
|
||||
func _editor_description() -> String:
|
||||
return ""
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return -1
|
1
addons/guide/inputs/guide_input.gd.uid
Normal file
1
addons/guide/inputs/guide_input.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://ccvqqvfooyvn0
|
12
addons/guide/inputs/guide_input.svg
Normal file
12
addons/guide/inputs/guide_input.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.07241,0,0,1.07396,-3.11767,-2.34767)">
|
||||
<path d="M17.827,2.164C26.061,2.164 32.747,8.85 32.747,17.084C32.747,25.319 26.061,32.004 17.827,32.004C9.592,32.004 2.907,25.319 2.907,17.084C2.907,8.85 9.592,2.164 17.827,2.164ZM17.827,4.857C11.08,4.857 5.604,10.337 5.604,17.084C5.604,23.831 11.08,29.311 17.827,29.311C24.574,29.311 30.05,23.831 30.05,17.084C30.05,10.337 24.574,4.857 17.827,4.857Z" style="fill:rgb(253,150,0);"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0.687353,-2.69876)">
|
||||
<g transform="matrix(24,0,0,24,11.6286,27.2968)">
|
||||
<rect x="0.105" y="-0.717" width="0.097" height="0.717" style="fill:rgb(253,150,0);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
38
addons/guide/inputs/guide_input.svg.import
Normal file
38
addons/guide/inputs/guide_input.svg.import
Normal file
@ -0,0 +1,38 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://oku7f5t0ox3r"
|
||||
path="res://.godot/imported/guide_input.svg-d7e8ae255db039e6a02cccc3f844cc0e.ctex"
|
||||
metadata={
|
||||
"has_editor_variant": true,
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/guide/inputs/guide_input.svg"
|
||||
dest_files=["res://.godot/imported/guide_input.svg-d7e8ae255db039e6a02cccc3f844cc0e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=0.5
|
||||
editor/scale_with_editor_scale=true
|
||||
editor/convert_colors_with_editor_theme=false
|
59
addons/guide/inputs/guide_input_action.gd
Normal file
59
addons/guide/inputs/guide_input_action.gd
Normal file
@ -0,0 +1,59 @@
|
||||
## An input that mirrors the action's value while the action is triggered.
|
||||
@tool
|
||||
class_name GUIDEInputAction
|
||||
extends GUIDEInput
|
||||
|
||||
## The action that this input should mirror. This is live tracked, so any change in
|
||||
## the action will update the input.
|
||||
@export var action:GUIDEAction:
|
||||
set(value):
|
||||
if value == action:
|
||||
return
|
||||
action = value
|
||||
emit_changed()
|
||||
|
||||
func _begin_usage():
|
||||
if is_instance_valid(action):
|
||||
action.triggered.connect(_on)
|
||||
action.completed.connect(_off)
|
||||
action.ongoing.connect(_off)
|
||||
if action.is_triggered():
|
||||
_on()
|
||||
return
|
||||
# not triggered or no action.
|
||||
_off()
|
||||
|
||||
|
||||
func _end_usage():
|
||||
if is_instance_valid(action):
|
||||
action.triggered.disconnect(_on)
|
||||
action.completed.disconnect(_off)
|
||||
action.ongoing.disconnect(_off)
|
||||
|
||||
|
||||
func _on() -> void:
|
||||
# on is only called when the action is actually existing, so this is
|
||||
# always not-null here
|
||||
_value = action.value_axis_3d
|
||||
|
||||
func _off() -> void:
|
||||
_value = Vector3.ZERO
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return other is GUIDEInputAction and other.action == action
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputAction: " + str(action) + ")"
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Action"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "An input that mirrors the action's value while the action is triggered."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_3D
|
1
addons/guide/inputs/guide_input_action.gd.uid
Normal file
1
addons/guide/inputs/guide_input_action.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://mc0mxjvhanrx
|
150
addons/guide/inputs/guide_input_any.gd
Normal file
150
addons/guide/inputs/guide_input_any.gd
Normal file
@ -0,0 +1,150 @@
|
||||
## Input that triggers if any input from the given device class
|
||||
## is given.
|
||||
@tool
|
||||
class_name GUIDEInputAny
|
||||
extends GUIDEInput
|
||||
|
||||
|
||||
## Should input from mouse buttons be considered? Deprecated, use
|
||||
## mouse_buttons instead.
|
||||
## @deprecated
|
||||
var mouse:bool:
|
||||
get: return mouse_buttons
|
||||
set(value): mouse_buttons = value
|
||||
|
||||
## Should input from joy buttons be considered. Deprecated, use
|
||||
## joy_buttons instead.
|
||||
## @deprecated
|
||||
var joy:bool:
|
||||
get: return joy_buttons
|
||||
set(value): joy_buttons = value
|
||||
|
||||
## Should input from mouse buttons be considered?
|
||||
@export var mouse_buttons:bool = false
|
||||
|
||||
## Should input from mouse movement be considered?
|
||||
@export var mouse_movement:bool = false
|
||||
|
||||
## Minimum movement distance of the mouse before it is considered
|
||||
## moving.
|
||||
@export var minimum_mouse_movement_distance:float = 1.0
|
||||
|
||||
## Should input from gamepad/joystick buttons be considered?
|
||||
@export var joy_buttons:bool = false
|
||||
|
||||
## Should input from gamepad/joystick axes be considered?
|
||||
@export var joy_axes:bool = false
|
||||
|
||||
## Minimum strength of a single joy axis actuation before it is considered
|
||||
## as actuated.
|
||||
@export var minimum_joy_axis_actuation_strength:float = 0.2
|
||||
|
||||
## Should input from the keyboard be considered?
|
||||
@export var keyboard:bool = false
|
||||
|
||||
## Should input from touch be considered?
|
||||
@export var touch:bool = false
|
||||
|
||||
|
||||
func _needs_reset() -> bool:
|
||||
# Needs reset because we cannot detect the absence of input.
|
||||
return true
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to relevant input events
|
||||
if mouse_movement:
|
||||
_state.mouse_position_changed.connect(_refresh)
|
||||
if mouse_buttons:
|
||||
_state.mouse_button_state_changed.connect(_refresh)
|
||||
if keyboard:
|
||||
_state.keyboard_state_changed.connect(_refresh)
|
||||
if joy_buttons:
|
||||
_state.joy_button_state_changed.connect(_refresh)
|
||||
if joy_axes:
|
||||
_state.joy_axis_state_changed.connect(_refresh)
|
||||
if touch:
|
||||
_state.touch_state_changed.connect(_refresh)
|
||||
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from input events
|
||||
if mouse_movement:
|
||||
_state.mouse_position_changed.disconnect(_refresh)
|
||||
if mouse_buttons:
|
||||
_state.mouse_button_state_changed.disconnect(_refresh)
|
||||
if keyboard:
|
||||
_state.keyboard_state_changed.disconnect(_refresh)
|
||||
if joy_buttons:
|
||||
_state.joy_button_state_changed.disconnect(_refresh)
|
||||
if joy_axes:
|
||||
_state.joy_axis_state_changed.disconnect(_refresh)
|
||||
if touch:
|
||||
_state.touch_state_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh() -> void:
|
||||
# if the input was already actuated this frame, remain
|
||||
# actuated, even if more input events come in. Input will
|
||||
# reset at the end of the frame.
|
||||
if not _value.is_zero_approx():
|
||||
return
|
||||
|
||||
if keyboard and _state.is_any_key_pressed():
|
||||
_value = Vector3.RIGHT
|
||||
return
|
||||
|
||||
if mouse_buttons and _state.is_any_mouse_button_pressed():
|
||||
_value = Vector3.RIGHT
|
||||
return
|
||||
|
||||
if mouse_movement and _state.get_mouse_delta_since_last_frame().length() >= minimum_mouse_movement_distance:
|
||||
_value = Vector3.RIGHT
|
||||
return
|
||||
|
||||
if joy_buttons and _state.is_any_joy_button_pressed():
|
||||
_value = Vector3.RIGHT
|
||||
return
|
||||
|
||||
if joy_axes and _state.is_any_joy_axis_actuated(minimum_joy_axis_actuation_strength):
|
||||
_value = Vector3.RIGHT
|
||||
return
|
||||
|
||||
if touch and _state.is_any_finger_down():
|
||||
_value = Vector3.RIGHT
|
||||
return
|
||||
|
||||
_value = Vector3.ZERO
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return other is GUIDEInputAny and \
|
||||
other.mouse == mouse and \
|
||||
other.joy == joy and \
|
||||
other.keyboard == keyboard
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Any Input"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Input that triggers if any input from the given device class is given."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.BOOL
|
||||
|
||||
# support for legacy properties
|
||||
func _get_property_list():
|
||||
return [
|
||||
{
|
||||
"name": "mouse",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_NO_EDITOR
|
||||
},
|
||||
{
|
||||
"name": "joy",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_NO_EDITOR
|
||||
}
|
||||
]
|
||||
|
1
addons/guide/inputs/guide_input_any.gd.uid
Normal file
1
addons/guide/inputs/guide_input_any.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://w3fbpe7r01n8
|
40
addons/guide/inputs/guide_input_joy_axis_1d.gd
Normal file
40
addons/guide/inputs/guide_input_joy_axis_1d.gd
Normal file
@ -0,0 +1,40 @@
|
||||
## Input from a single joy axis.
|
||||
@tool
|
||||
class_name GUIDEInputJoyAxis1D
|
||||
extends GUIDEInputJoyBase
|
||||
|
||||
## The joy axis to sample
|
||||
@export var axis:JoyAxis = JOY_AXIS_LEFT_X:
|
||||
set(value):
|
||||
if value == axis:
|
||||
return
|
||||
axis = value
|
||||
emit_changed()
|
||||
|
||||
func _begin_usage() -> void:
|
||||
_state.joy_axis_state_changed.connect(_refresh)
|
||||
|
||||
func _end_usage() -> void:
|
||||
_state.joy_axis_state_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh() -> void:
|
||||
_value.x = _state.get_joy_axis_value(joy_index, axis)
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return other is GUIDEInputJoyAxis1D and \
|
||||
other.axis == axis and \
|
||||
other.joy_index == joy_index
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputJoyAxis1D: axis=" + str(axis) + ", joy_index=" + str(joy_index) + ")"
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Joy Axis 1D"
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "The input from a single joy axis."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_1D
|
1
addons/guide/inputs/guide_input_joy_axis_1d.gd.uid
Normal file
1
addons/guide/inputs/guide_input_joy_axis_1d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bbhoxsiqwo07l
|
53
addons/guide/inputs/guide_input_joy_axis_2d.gd
Normal file
53
addons/guide/inputs/guide_input_joy_axis_2d.gd
Normal file
@ -0,0 +1,53 @@
|
||||
## Input from two joy axes.
|
||||
class_name GUIDEInputJoyAxis2D
|
||||
extends GUIDEInputJoyBase
|
||||
|
||||
## The joy axis to sample for x input.
|
||||
@export var x:JoyAxis = JOY_AXIS_LEFT_X:
|
||||
set(value):
|
||||
if value == x:
|
||||
return
|
||||
x = value
|
||||
emit_changed()
|
||||
|
||||
|
||||
## The joy axis to sample for y input.
|
||||
@export var y:JoyAxis = JOY_AXIS_LEFT_Y:
|
||||
set(value):
|
||||
if value == y:
|
||||
return
|
||||
y = value
|
||||
emit_changed()
|
||||
|
||||
func _begin_usage() -> void:
|
||||
_state.joy_axis_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
_state.joy_axis_state_changed.disconnect(_refresh)
|
||||
|
||||
|
||||
func _refresh():
|
||||
_value.x = _state.get_joy_axis_value(joy_index, x)
|
||||
_value.y = _state.get_joy_axis_value(joy_index, y)
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return other is GUIDEInputJoyAxis2D and \
|
||||
other.x == x and \
|
||||
other.y == y and \
|
||||
other.joy_index == joy_index
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputJoyAxis2D: x=" + str(x) + ", y=" + str(y) + ", joy_index=" + str(joy_index) + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Joy Axis 2D"
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "The input from two Joy axes. Usually from a stick."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_2D
|
1
addons/guide/inputs/guide_input_joy_axis_2d.gd.uid
Normal file
1
addons/guide/inputs/guide_input_joy_axis_2d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://doauobik3xyea
|
13
addons/guide/inputs/guide_input_joy_base.gd
Normal file
13
addons/guide/inputs/guide_input_joy_base.gd
Normal file
@ -0,0 +1,13 @@
|
||||
## Base class for joystick inputs.
|
||||
@tool
|
||||
class_name GUIDEInputJoyBase
|
||||
extends GUIDEInput
|
||||
|
||||
## The index of the connected joy pad to check. If -1 checks all joypads.
|
||||
@export var joy_index:int = -1:
|
||||
set(value):
|
||||
if value == joy_index:
|
||||
return
|
||||
joy_index = value
|
||||
emit_changed()
|
||||
|
1
addons/guide/inputs/guide_input_joy_base.gd.uid
Normal file
1
addons/guide/inputs/guide_input_joy_base.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cnqnbdsw3lw12
|
41
addons/guide/inputs/guide_input_joy_button.gd
Normal file
41
addons/guide/inputs/guide_input_joy_button.gd
Normal file
@ -0,0 +1,41 @@
|
||||
@tool
|
||||
class_name GUIDEInputJoyButton
|
||||
extends GUIDEInputJoyBase
|
||||
|
||||
@export var button:JoyButton = JOY_BUTTON_A:
|
||||
set(value):
|
||||
if value == button:
|
||||
return
|
||||
button = value
|
||||
emit_changed()
|
||||
|
||||
func _begin_usage() -> void:
|
||||
_state.joy_button_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
_state.joy_button_state_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh():
|
||||
_value.x = 1.0 if _state.is_joy_button_pressed(joy_index, button) else 0.0
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return other is GUIDEInputJoyButton and \
|
||||
other.button == button and \
|
||||
other.joy_index == joy_index
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputJoyButton: button=" + str(button) + ", joy_index=" + str(joy_index) + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Joy Button"
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "A button press from a joy button."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.BOOL
|
1
addons/guide/inputs/guide_input_joy_button.gd.uid
Normal file
1
addons/guide/inputs/guide_input_joy_button.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://rvttn472ix6v
|
126
addons/guide/inputs/guide_input_key.gd
Normal file
126
addons/guide/inputs/guide_input_key.gd
Normal file
@ -0,0 +1,126 @@
|
||||
@tool
|
||||
class_name GUIDEInputKey
|
||||
extends GUIDEInput
|
||||
|
||||
## The physical keycode of the key.
|
||||
@export var key:Key:
|
||||
set(value):
|
||||
if value == key:
|
||||
return
|
||||
key = value
|
||||
emit_changed()
|
||||
|
||||
|
||||
@export_group("Modifiers")
|
||||
## Whether shift must be pressed.
|
||||
@export var shift:bool = false:
|
||||
set(value):
|
||||
if value == shift:
|
||||
return
|
||||
shift = value
|
||||
emit_changed()
|
||||
|
||||
## Whether control must be pressed.
|
||||
@export var control:bool = false:
|
||||
set(value):
|
||||
if value == control:
|
||||
return
|
||||
control = value
|
||||
emit_changed()
|
||||
|
||||
## Whether alt must be pressed.
|
||||
@export var alt:bool = false:
|
||||
set(value):
|
||||
if value == alt:
|
||||
return
|
||||
alt = value
|
||||
emit_changed()
|
||||
|
||||
|
||||
## Whether meta/win/cmd must be pressed.
|
||||
@export var meta:bool = false:
|
||||
set(value):
|
||||
if value == meta:
|
||||
return
|
||||
meta = value
|
||||
emit_changed()
|
||||
|
||||
## Whether this input should fire if additional
|
||||
## modifier keys are currently pressed.
|
||||
@export var allow_additional_modifiers:bool = true:
|
||||
set(value):
|
||||
if value == allow_additional_modifiers:
|
||||
return
|
||||
allow_additional_modifiers = value
|
||||
emit_changed()
|
||||
|
||||
## Helper array. All keys that must be pressed for this input to considered actuated.
|
||||
var _must_be_pressed:Array[Key] = []
|
||||
## Helper array. All keys that must not be pressed for this input to considered actuated.
|
||||
var _must_not_be_pressed:Array[Key] = []
|
||||
|
||||
func _begin_usage() -> void:
|
||||
_must_be_pressed = [key]
|
||||
|
||||
# also add the modifiers to the list of keys that must be pressed
|
||||
if shift:
|
||||
_must_be_pressed.append(KEY_SHIFT)
|
||||
if control:
|
||||
_must_be_pressed.append(KEY_CTRL)
|
||||
if alt:
|
||||
_must_be_pressed.append(KEY_ALT)
|
||||
if meta:
|
||||
_must_be_pressed.append(KEY_META)
|
||||
|
||||
_must_not_be_pressed = []
|
||||
# now unless additional modifiers are allowed, add all modifiers
|
||||
# that are not required to the list of keys that must not be pressed
|
||||
# except if the bound key is actually the modifier itself
|
||||
if not allow_additional_modifiers:
|
||||
if not shift and key != KEY_SHIFT:
|
||||
_must_not_be_pressed.append(KEY_SHIFT)
|
||||
if not control and key != KEY_CTRL:
|
||||
_must_not_be_pressed.append(KEY_CTRL)
|
||||
if not alt and key != KEY_ALT:
|
||||
_must_not_be_pressed.append(KEY_ALT)
|
||||
if not meta and key != KEY_META:
|
||||
_must_not_be_pressed.append(KEY_META)
|
||||
|
||||
# subscribe to input events
|
||||
_state.keyboard_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from input events
|
||||
_state.keyboard_state_changed.disconnect(_refresh)
|
||||
|
||||
|
||||
func _refresh():
|
||||
# We are actuated if all keys that must be pressed are pressed and none of the keys that must not be pressed
|
||||
# are pressed.
|
||||
var is_actuated:bool = _state.are_all_keys_pressed(_must_be_pressed) and not _state.is_at_least_one_key_pressed(_must_not_be_pressed)
|
||||
_value.x = 1.0 if is_actuated else 0.0
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput) -> bool:
|
||||
return other is GUIDEInputKey \
|
||||
and other.key == key \
|
||||
and other.shift == shift \
|
||||
and other.control == control \
|
||||
and other.alt == alt \
|
||||
and other.meta == meta \
|
||||
and other.allow_additional_modifiers == allow_additional_modifiers
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputKey: key=" + str(key) + ", shift=" + str(shift) + ", alt=" + str(alt) + ", control=" + str(control) + ", meta="+ str(meta) + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Key"
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "A button press on the keyboard."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.BOOL
|
1
addons/guide/inputs/guide_input_key.gd.uid
Normal file
1
addons/guide/inputs/guide_input_key.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cw71o87tvdx3q
|
57
addons/guide/inputs/guide_input_mouse_axis_1d.gd
Normal file
57
addons/guide/inputs/guide_input_mouse_axis_1d.gd
Normal file
@ -0,0 +1,57 @@
|
||||
@tool
|
||||
class_name GUIDEInputMouseAxis1D
|
||||
extends GUIDEInput
|
||||
|
||||
enum GUIDEInputMouseAxis {
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
@export var axis:GUIDEInputMouseAxis:
|
||||
set(value):
|
||||
if value == axis:
|
||||
return
|
||||
axis = value
|
||||
emit_changed()
|
||||
|
||||
# we don't get mouse updates when the mouse is not moving, so this needs to be
|
||||
# reset every frame
|
||||
func _needs_reset() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to mouse movement events
|
||||
_state.mouse_position_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from mouse movement events
|
||||
_state.mouse_position_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh() -> void:
|
||||
var delta:Vector2 = _state.get_mouse_delta_since_last_frame()
|
||||
match axis:
|
||||
GUIDEInputMouseAxis.X:
|
||||
_value.x = delta.x
|
||||
GUIDEInputMouseAxis.Y:
|
||||
_value.x = delta.y
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputMouseAxis1D and other.axis == axis
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputMouseAxis1D: axis=" + str(axis) + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Mouse Axis 1D"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Relative mouse movement on a single axis."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_1D
|
1
addons/guide/inputs/guide_input_mouse_axis_1d.gd.uid
Normal file
1
addons/guide/inputs/guide_input_mouse_axis_1d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://b6bwb7ie85kl1
|
42
addons/guide/inputs/guide_input_mouse_axis_2d.gd
Normal file
42
addons/guide/inputs/guide_input_mouse_axis_2d.gd
Normal file
@ -0,0 +1,42 @@
|
||||
@tool
|
||||
class_name GUIDEInputMouseAxis2D
|
||||
extends GUIDEInput
|
||||
|
||||
|
||||
# we don't get mouse updates when the mouse is not moving, so this needs to be
|
||||
# reset every frame
|
||||
func _needs_reset() -> bool:
|
||||
return true
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to mouse movement events
|
||||
_state.mouse_position_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from mouse movement events
|
||||
_state.mouse_position_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh() -> void:
|
||||
var delta:Vector2 = _state.get_mouse_delta_since_last_frame()
|
||||
_value.x = delta.x
|
||||
_value.y = delta.y
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputMouseAxis2D
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputMouseAxis2D)"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Mouse Axis 2D"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Relative mouse movement on 2 axes."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_2D
|
1
addons/guide/inputs/guide_input_mouse_axis_2d.gd.uid
Normal file
1
addons/guide/inputs/guide_input_mouse_axis_2d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dh0hf08e0yit5
|
74
addons/guide/inputs/guide_input_mouse_button.gd
Normal file
74
addons/guide/inputs/guide_input_mouse_button.gd
Normal file
@ -0,0 +1,74 @@
|
||||
@tool
|
||||
class_name GUIDEInputMouseButton
|
||||
extends GUIDEInput
|
||||
|
||||
@export var button: MouseButton = MOUSE_BUTTON_LEFT:
|
||||
set(value):
|
||||
if value == button:
|
||||
return
|
||||
button = value
|
||||
emit_changed()
|
||||
|
||||
# The value that this input will be reset to at the end of the frame.
|
||||
var _reset_to: Vector3
|
||||
var _was_pressed_this_frame: bool
|
||||
|
||||
|
||||
func _needs_reset() -> bool:
|
||||
# mouse wheel up and down can potentially send multiple inputs within a single frame
|
||||
# so we need to smooth this out a bit.
|
||||
return button == MOUSE_BUTTON_WHEEL_UP or button == MOUSE_BUTTON_WHEEL_DOWN
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
_was_pressed_this_frame = false
|
||||
_value = _reset_to
|
||||
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to mouse button events
|
||||
_state.mouse_button_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from mouse button events
|
||||
_state.mouse_button_state_changed.disconnect(_refresh)
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
var is_pressed: bool = _state.is_mouse_button_pressed(button)
|
||||
|
||||
if _needs_reset():
|
||||
# we always reset to the last event we received in a frame
|
||||
# so after the frame is over we're still in sync.
|
||||
_reset_to.x = 1.0 if is_pressed else 0.0
|
||||
|
||||
if is_pressed:
|
||||
_was_pressed_this_frame = true
|
||||
|
||||
if not is_pressed and _was_pressed_this_frame:
|
||||
# keep pressed state for this frame
|
||||
return
|
||||
|
||||
_value.x = 1.0 if is_pressed else 0.0
|
||||
|
||||
|
||||
func is_same_as(other: GUIDEInput) -> bool:
|
||||
return other is GUIDEInputMouseButton and other.button == button
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputMouseButton: button=" + str(button) + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Mouse Button"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "A press of a mouse button. The mouse wheel is also a button."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.BOOL
|
1
addons/guide/inputs/guide_input_mouse_button.gd.uid
Normal file
1
addons/guide/inputs/guide_input_mouse_button.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://vgjlx6p007lp
|
39
addons/guide/inputs/guide_input_mouse_position.gd
Normal file
39
addons/guide/inputs/guide_input_mouse_position.gd
Normal file
@ -0,0 +1,39 @@
|
||||
@tool
|
||||
class_name GUIDEInputMousePosition
|
||||
extends GUIDEInput
|
||||
|
||||
|
||||
func _begin_usage() -> void :
|
||||
# subscribe to mouse movement events
|
||||
_state.mouse_position_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from mouse movement events
|
||||
_state.mouse_position_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh():
|
||||
var position:Vector2 = _state.get_mouse_position()
|
||||
|
||||
_value.x = position.x
|
||||
_value.y = position.y
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputMousePosition
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputMousePosition)"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Mouse Position"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Position of the mouse in the main viewport."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_2D
|
1
addons/guide/inputs/guide_input_mouse_position.gd.uid
Normal file
1
addons/guide/inputs/guide_input_mouse_position.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://deeru16npi81q
|
327
addons/guide/inputs/guide_input_state.gd
Normal file
327
addons/guide/inputs/guide_input_state.gd
Normal file
@ -0,0 +1,327 @@
|
||||
## The GUIDEInputState holds the current state of all input. It is basically a wrapper around Godot's Input
|
||||
## class that provides some additional functionality like getting the information if any key or mouse button
|
||||
## is currently pressed. It also is the single entry point for all input events from Godot, so we don't have
|
||||
## process them in every GUIDEInput object and duplicate input handling code everywere. This also improves performance.
|
||||
##
|
||||
class_name GUIDEInputState
|
||||
|
||||
## Device ID for a virtual joystick that means "any joystick".
|
||||
## This relies on the fact that Godot's device IDs for joysticks are always >= 0.
|
||||
## https://github.com/godotengine/godot/blob/80a3d205f1ad22e779a64921fb56d62b893881ae/core/input/input.cpp#L1821
|
||||
const ANY_JOY_DEVICE_ID: int = -1
|
||||
|
||||
## Signalled, when the keyboard state has changed.
|
||||
signal keyboard_state_changed()
|
||||
## Signalled, when the mouse motion state has changed.
|
||||
signal mouse_position_changed()
|
||||
## Signalled, when the mouse button state has changed.
|
||||
signal mouse_button_state_changed()
|
||||
## Signalled, when the joy button state has changed.
|
||||
signal joy_button_state_changed()
|
||||
## Signalled, when the joy axis state has changed.
|
||||
signal joy_axis_state_changed()
|
||||
## Signalled, when the touch state has changed.
|
||||
signal touch_state_changed()
|
||||
|
||||
# Keys that are currently pressed. Key is the key index, value is not important. The presence of a key in the dictionary
|
||||
# indicates that the key is currently pressed.
|
||||
var _keys: Dictionary = {}
|
||||
# Fingers that are currently touching the screen. Key is the finger index, value is the position (Vector2).
|
||||
var _finger_positions: Dictionary = {}
|
||||
# The mouse movement since the last frame.
|
||||
var _mouse_movement: Vector2 = Vector2.ZERO
|
||||
# Mouse buttons that are currently pressed. Key is the button index, value is not important. The presence of a key
|
||||
# in the dictionary indicates that the button is currently pressed.
|
||||
var _mouse_buttons: Dictionary = {}
|
||||
# Joy buttons that are currently pressed. Key is device id, value is a dictionary with the button index as key. The
|
||||
# value of the inner dictionary is not important. The presence of a key in the inner dictionary indicates that the button
|
||||
# is currently pressed.
|
||||
var _joy_buttons: Dictionary = {}
|
||||
# Current values of joy axes. Key is device id, value is a dictionary with the axis index as key.
|
||||
# The value of the inner dictionary is the axis value. Once an axis is actuated, it will be added to the dictionary.
|
||||
# We will not remove it anymore after that.
|
||||
var _joy_axes: Dictionary = {}
|
||||
|
||||
# The current mapping of joy index to device id. This is used to map the joy index to the device id. A joy index
|
||||
# if -1 means "any device id".
|
||||
var _joy_index_to_device_id: Dictionary = {}
|
||||
|
||||
func _init():
|
||||
Input.joy_connection_changed.connect(_refresh_joy_device_ids)
|
||||
_clear()
|
||||
|
||||
# Used by the automated tests to make sure we don't have any leftovers from the
|
||||
# last test.
|
||||
func _clear():
|
||||
_keys.clear()
|
||||
_finger_positions.clear()
|
||||
_mouse_movement = Vector2.ZERO
|
||||
_mouse_buttons.clear()
|
||||
_joy_buttons.clear()
|
||||
_joy_axes.clear()
|
||||
|
||||
_refresh_joy_device_ids(0, 0)
|
||||
|
||||
# ensure we have an entry for the virtual "any device id"
|
||||
_joy_buttons[ANY_JOY_DEVICE_ID] = {}
|
||||
_joy_axes[ANY_JOY_DEVICE_ID] = {}
|
||||
|
||||
|
||||
# Called when any joy device is connected or disconnected. This will refresh the joy device ids and clear out any
|
||||
# joy state which is not valid anymore. Will also notify relevant inputs.
|
||||
func _refresh_joy_device_ids(_ignore1, _ignore2):
|
||||
# refresh the joy device ids
|
||||
_joy_index_to_device_id.clear()
|
||||
var connected_joys:Array[int] = Input.get_connected_joypads()
|
||||
for i in connected_joys.size():
|
||||
var device_id:int = connected_joys[i]
|
||||
_joy_index_to_device_id[i] = device_id
|
||||
# ensure we have an inner dictionary for the device id
|
||||
# by setting this here, we don't need to check for the device id
|
||||
# on every input event
|
||||
if not _joy_buttons.has(device_id):
|
||||
_joy_buttons[device_id] = {}
|
||||
if not _joy_axes.has(device_id):
|
||||
_joy_axes[device_id] = {}
|
||||
|
||||
# add a virtual device id for the "any device id" case
|
||||
_joy_index_to_device_id[-1] = ANY_JOY_DEVICE_ID
|
||||
|
||||
var dirty: bool = false
|
||||
|
||||
# clear out any joy state which is not valid anymore
|
||||
for device_id in _joy_buttons.keys():
|
||||
if device_id != ANY_JOY_DEVICE_ID and not connected_joys.has(device_id):
|
||||
dirty = true
|
||||
_joy_buttons.erase(device_id)
|
||||
|
||||
if dirty:
|
||||
# notify all inputs that the joy state has changed
|
||||
joy_button_state_changed.emit()
|
||||
|
||||
dirty = false
|
||||
for device_id in _joy_axes.keys():
|
||||
if device_id != ANY_JOY_DEVICE_ID and not connected_joys.has(device_id):
|
||||
dirty = true
|
||||
_joy_axes.erase(device_id)
|
||||
|
||||
if dirty:
|
||||
# notify all inputs that the joy state has changed
|
||||
joy_axis_state_changed.emit()
|
||||
|
||||
## Called at the end of the frame to reset the state before the next frame.
|
||||
func _reset() -> void:
|
||||
_mouse_movement = Vector2.ZERO
|
||||
|
||||
|
||||
## Processes an input event and updates the state.
|
||||
func _input(event: InputEvent) -> void:
|
||||
# ----------------------- KEYBOARD -----------------------------
|
||||
if event is InputEventKey:
|
||||
var index: int = event.physical_keycode
|
||||
|
||||
if event.pressed:
|
||||
_keys[index] = true
|
||||
else:
|
||||
_keys.erase(index)
|
||||
|
||||
# Emit the keyboard state changed signal
|
||||
keyboard_state_changed.emit()
|
||||
return
|
||||
|
||||
# ----------------------- MOUSE MOVEMENT -----------------------
|
||||
if event is InputEventMouseMotion:
|
||||
# Emit the mouse moved signal with the distance moved
|
||||
_mouse_movement += event.relative
|
||||
mouse_position_changed.emit()
|
||||
return
|
||||
|
||||
# ----------------------- MOUSE BUTTONS -----------------------
|
||||
if event is InputEventMouseButton:
|
||||
var index: int = event.button_index
|
||||
|
||||
if event.pressed:
|
||||
_mouse_buttons[index] = true
|
||||
else:
|
||||
_mouse_buttons.erase(index)
|
||||
|
||||
# Emit the mouse button state changed signal
|
||||
mouse_button_state_changed.emit()
|
||||
return
|
||||
|
||||
# ----------------------- JOYSTICK BUTTONS -----------------------
|
||||
if event is InputEventJoypadButton:
|
||||
var device_id: int = event.device
|
||||
var button: int = event.button_index
|
||||
|
||||
if event.pressed:
|
||||
# _refresh_joy_device_ids ensures we have an inner dictionary for the device id
|
||||
# so we don't need to check for it here
|
||||
_joy_buttons[device_id][button] = true
|
||||
else:
|
||||
_joy_buttons[device_id].erase(button)
|
||||
|
||||
# finally set the ANY_JOY_DEVICE_ID state based on what we know
|
||||
var any_value: bool = false
|
||||
for inner in _joy_buttons.keys():
|
||||
if inner != ANY_JOY_DEVICE_ID and _joy_buttons[inner].has(button):
|
||||
any_value = true
|
||||
break
|
||||
|
||||
if any_value:
|
||||
_joy_buttons[ANY_JOY_DEVICE_ID][button] = true
|
||||
else:
|
||||
_joy_buttons[ANY_JOY_DEVICE_ID].erase(button)
|
||||
|
||||
# Emit the joy button state changed signal
|
||||
joy_button_state_changed.emit()
|
||||
return
|
||||
|
||||
# ----------------------- JOYSTICK AXES -----------------------
|
||||
if event is InputEventJoypadMotion:
|
||||
var device_id: int = event.device
|
||||
var axis: int = event.axis
|
||||
|
||||
# update the axis value
|
||||
_joy_axes[device_id][axis] = event.axis_value
|
||||
|
||||
# for the ANY_JOY_DEVICE_ID, we apply the maximum actuation of all devices (in any direction)
|
||||
var any_value: float = 0.0
|
||||
var maximum_actuation: float = 0.0
|
||||
for inner in _joy_axes.keys():
|
||||
if inner != ANY_JOY_DEVICE_ID and _joy_axes[inner].has(axis):
|
||||
var strength: float = abs(_joy_axes[inner][axis])
|
||||
if strength > maximum_actuation:
|
||||
maximum_actuation = strength
|
||||
any_value = _joy_axes[inner][axis]
|
||||
|
||||
_joy_axes[ANY_JOY_DEVICE_ID][axis] = any_value
|
||||
|
||||
# Emit the joy axis state changed signal
|
||||
joy_axis_state_changed.emit()
|
||||
return
|
||||
|
||||
# ----------------------- TOUCH INPUT -----------------------
|
||||
|
||||
if event is InputEventScreenTouch:
|
||||
if event.pressed:
|
||||
_finger_positions[event.index] = event.position
|
||||
else:
|
||||
_finger_positions.erase(event.index)
|
||||
|
||||
touch_state_changed.emit()
|
||||
return
|
||||
|
||||
|
||||
if event is InputEventScreenDrag:
|
||||
_finger_positions[event.index] = event.position
|
||||
|
||||
touch_state_changed.emit()
|
||||
return
|
||||
|
||||
|
||||
## Returns true if the key with the given index is currently pressed.
|
||||
func is_key_pressed(key: Key) -> bool:
|
||||
return _keys.has(key)
|
||||
|
||||
# Returns true if at least one key in the given array is currently pressed.
|
||||
func is_at_least_one_key_pressed(keys:Array[Key]) -> bool:
|
||||
for key in keys:
|
||||
if _keys.has(key):
|
||||
return true
|
||||
return false
|
||||
|
||||
# Returns true if all keys in the given array are currently pressed.
|
||||
func are_all_keys_pressed(keys:Array[Key]) -> bool:
|
||||
return _keys.has_all(keys)
|
||||
|
||||
## Returns true if currently any key is pressed.
|
||||
func is_any_key_pressed() -> bool:
|
||||
return not _keys.is_empty()
|
||||
|
||||
## Gets the mouse movement since the last frame.
|
||||
## If no movement has been detected, returns Vector2.ZERO.
|
||||
func get_mouse_delta_since_last_frame() -> Vector2:
|
||||
return _mouse_movement
|
||||
|
||||
## Returns the current mouse position in the root viewport.
|
||||
func get_mouse_position() -> Vector2:
|
||||
return Engine.get_main_loop().root.get_mouse_position()
|
||||
|
||||
|
||||
## Returns true if the mouse button with the given index is currently pressed.
|
||||
func is_mouse_button_pressed(button_index: MouseButton) -> bool:
|
||||
return _mouse_buttons.has(button_index)
|
||||
|
||||
## Returns true if currently any mouse button is pressed.
|
||||
func is_any_mouse_button_pressed() -> bool:
|
||||
return not _mouse_buttons.is_empty()
|
||||
|
||||
## Returns the current value of the given joy axis on the device with the given index. If no
|
||||
## such device or axis exists, returns 0.0.
|
||||
func get_joy_axis_value(index:int, axis:JoyAxis) -> float:
|
||||
var device_id: int = _joy_index_to_device_id.get(index, -9999)
|
||||
# unknown device
|
||||
if device_id == -9999:
|
||||
return 0.0
|
||||
if _joy_axes.has(device_id):
|
||||
var inner = _joy_axes[device_id]
|
||||
return inner.get(axis, 0.0)
|
||||
return 0.0
|
||||
|
||||
## Returns true, if the given joy button is currentely pressed on the device with the given index.
|
||||
func is_joy_button_pressed(index:int, button:JoyButton) -> bool:
|
||||
var device_id: int = _joy_index_to_device_id.get(index, -9999)
|
||||
# unknown device
|
||||
if device_id == -9999:
|
||||
return false
|
||||
if _joy_buttons.has(device_id):
|
||||
return _joy_buttons[device_id].has(button)
|
||||
return false
|
||||
|
||||
## Returns true, if currently any joy button is pressed on any device.
|
||||
func is_any_joy_button_pressed() -> bool:
|
||||
for inner in _joy_buttons.values():
|
||||
if not inner.is_empty():
|
||||
return true
|
||||
return false
|
||||
|
||||
## Returns true if currently any joy axis is actuated with at least the given strength.
|
||||
func is_any_joy_axis_actuated(minimum_strength: float) -> bool:
|
||||
for inner in _joy_axes.values():
|
||||
for value in inner.values():
|
||||
if abs(value) >= minimum_strength:
|
||||
return true
|
||||
return false
|
||||
|
||||
## Gets the finger position of the finger at the given index.
|
||||
## If finger_index is < 0, returns the average of all finger positions.
|
||||
## Will only return a position if the amount of fingers
|
||||
## currently touching matches finger_count.
|
||||
##
|
||||
## If no finger position can be determined, returns Vector2.INF.
|
||||
func get_finger_position(finger_index: int, finger_count: int) -> Vector2:
|
||||
# if we have no finger positions right now, we can cut it short here
|
||||
if _finger_positions.is_empty():
|
||||
return Vector2.INF
|
||||
|
||||
# If the finger count doesn't match we have no position right now
|
||||
if _finger_positions.size() != finger_count:
|
||||
return Vector2.INF
|
||||
|
||||
# if a finger index is set, use this fingers position, if available
|
||||
if finger_index > -1:
|
||||
return _finger_positions.get(finger_index, Vector2.INF)
|
||||
|
||||
var result: Vector2 = Vector2.ZERO
|
||||
for value in _finger_positions.values():
|
||||
result += value
|
||||
|
||||
result /= float(finger_count)
|
||||
return result
|
||||
|
||||
## Returns true, if currently any finger is touching the screen.
|
||||
func is_any_finger_down() -> bool:
|
||||
return not _finger_positions.is_empty()
|
||||
|
||||
|
1
addons/guide/inputs/guide_input_state.gd.uid
Normal file
1
addons/guide/inputs/guide_input_state.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://c11q8ft87iu87
|
91
addons/guide/inputs/guide_input_touch_angle.gd
Normal file
91
addons/guide/inputs/guide_input_touch_angle.gd
Normal file
@ -0,0 +1,91 @@
|
||||
## Input representing angle changes between two fingers.
|
||||
@tool
|
||||
class_name GUIDEInputTouchAngle
|
||||
extends GUIDEInput
|
||||
|
||||
## Unit in which the angle should be provided
|
||||
enum AngleUnit {
|
||||
## Angle is provided in radians
|
||||
RADIANS = 0,
|
||||
## Angle is provided in degrees.
|
||||
DEGREES = 1
|
||||
}
|
||||
|
||||
## The unit in which the angle should be provided
|
||||
@export var unit:AngleUnit = AngleUnit.RADIANS
|
||||
|
||||
var _initial_angle:float = INF
|
||||
|
||||
# We use the reset call to calculate the angle for this frame
|
||||
# so it can serve as reference for the next frame
|
||||
func _needs_reset() -> bool:
|
||||
return true
|
||||
|
||||
func _reset():
|
||||
var angle = _calculate_angle()
|
||||
# update initial angle when input is actuated or stops being actuated
|
||||
if is_finite(_initial_angle) != is_finite(angle):
|
||||
_initial_angle = angle
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to relevant input events
|
||||
_state.touch_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from input events
|
||||
_state.touch_state_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh():
|
||||
var angle := _calculate_angle()
|
||||
# if either current angle or initial angle is not set,
|
||||
# we are zero
|
||||
if not is_finite(angle) or not is_finite(_initial_angle):
|
||||
_value = Vector3.ZERO
|
||||
return
|
||||
|
||||
# we assume that _initial_distance is never 0 because
|
||||
# you cannot have two fingers physically at the same place
|
||||
# on a touch screen (unless you're a ghost, which raises
|
||||
# the question how you are using a touch screen in the first place)
|
||||
var final_angle:float = angle - _initial_angle
|
||||
if unit == AngleUnit.DEGREES:
|
||||
final_angle = rad_to_deg(final_angle)
|
||||
|
||||
_value = Vector3(final_angle, 0, 0)
|
||||
|
||||
|
||||
func _calculate_angle() -> float:
|
||||
var pos1:Vector2 = _state.get_finger_position(0, 2)
|
||||
# if we have no position for first finger, we can immediately abort
|
||||
if not pos1.is_finite():
|
||||
return INF
|
||||
|
||||
var pos2:Vector2 = _state.get_finger_position(1, 2)
|
||||
# if there is no second finger, we can abort as well
|
||||
if not pos2.is_finite():
|
||||
return INF
|
||||
|
||||
# calculate distance for the fingers
|
||||
return -pos1.angle_to_point(pos2)
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputTouchAngle and \
|
||||
other.unit == unit
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputTouchAngle unit=" + ("radians" if unit == AngleUnit.RADIANS else "degrees") + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Touch Angle"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Angle changes of two touching fingers."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_1D
|
1
addons/guide/inputs/guide_input_touch_angle.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_angle.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://b3sxmqknm7ljs
|
44
addons/guide/inputs/guide_input_touch_axis_1d.gd
Normal file
44
addons/guide/inputs/guide_input_touch_axis_1d.gd
Normal file
@ -0,0 +1,44 @@
|
||||
@tool
|
||||
class_name GUIDEInputTouchAxis1D
|
||||
extends GUIDEInputTouchAxisBase
|
||||
|
||||
enum GUIDEInputTouchAxis {
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
@export var axis:GUIDEInputTouchAxis:
|
||||
set(value):
|
||||
if value == axis:
|
||||
return
|
||||
axis = value
|
||||
emit_changed()
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputTouchAxis1D and \
|
||||
other.finger_count == finger_count and \
|
||||
other.finger_index == finger_index and \
|
||||
other.axis == axis
|
||||
|
||||
func _apply_value(value:Vector2):
|
||||
match axis:
|
||||
GUIDEInputTouchAxis.X:
|
||||
_value = Vector3(value.x, 0, 0)
|
||||
GUIDEInputTouchAxis.Y:
|
||||
_value = Vector3(value.y, 0, 0)
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputTouchAxis1D finger_count=" + str(finger_count) + \
|
||||
" finger_index=" + str(finger_index) +" axis=" + ("X" if axis == GUIDEInputTouchAxis.X else "Y") + ")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Touch Axis1D"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Relative movement of a touching finger on a single axis."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_1D
|
1
addons/guide/inputs/guide_input_touch_axis_1d.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_axis_1d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://idi72xetfe0s
|
27
addons/guide/inputs/guide_input_touch_axis_2d.gd
Normal file
27
addons/guide/inputs/guide_input_touch_axis_2d.gd
Normal file
@ -0,0 +1,27 @@
|
||||
@tool
|
||||
class_name GUIDEInputTouchAxis2D
|
||||
extends GUIDEInputTouchAxisBase
|
||||
|
||||
func _apply_value(value:Vector2):
|
||||
_value = Vector3(value.x, value.y, 0)
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputTouchAxis2D and \
|
||||
other.finger_count == finger_count and \
|
||||
other.finger_index == finger_index
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputTouchAxis2D finger_count=" + str(finger_count) + \
|
||||
" finger_index=" + str(finger_index) +")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Touch Axis2D"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "2D relative movement of a touching finger."
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_2D
|
1
addons/guide/inputs/guide_input_touch_axis_2d.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_axis_2d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://83ggp3br4dqv
|
49
addons/guide/inputs/guide_input_touch_axis_base.gd
Normal file
49
addons/guide/inputs/guide_input_touch_axis_base.gd
Normal file
@ -0,0 +1,49 @@
|
||||
## Base class for axis-like touch input.
|
||||
@tool
|
||||
class_name GUIDEInputTouchAxisBase
|
||||
extends GUIDEInputTouchBase
|
||||
|
||||
var _last_position:Vector2 = Vector2.INF
|
||||
|
||||
# We use the reset call to calculate the position for this frame
|
||||
# so it can serve as reference for the next frame
|
||||
func _needs_reset() -> bool:
|
||||
return true
|
||||
|
||||
func _reset() -> void:
|
||||
_last_position = _state.get_finger_position(finger_index, finger_count)
|
||||
_apply_value(_calculate_value(_last_position))
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to relevant input events
|
||||
_state.touch_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from input events
|
||||
_state.touch_state_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh() -> void:
|
||||
# calculate live position from the cache
|
||||
var new_position:Vector2 = _state.get_finger_position(finger_index, finger_count)
|
||||
|
||||
_apply_value(_calculate_value(new_position))
|
||||
|
||||
func _apply_value(value:Vector2):
|
||||
pass
|
||||
|
||||
func _calculate_value(new_position:Vector2) -> Vector2:
|
||||
# if we cannot calculate a delta because old or new position
|
||||
# are undefined, we say the delta is zero
|
||||
if not _last_position.is_finite() or not new_position.is_finite():
|
||||
return Vector2.ZERO
|
||||
|
||||
return new_position - _last_position
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputTouchAxis2D and \
|
||||
other.finger_count == finger_count and \
|
||||
other.finger_index == finger_index
|
||||
|
||||
|
1
addons/guide/inputs/guide_input_touch_axis_base.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_axis_base.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://c32np6tk2l12
|
22
addons/guide/inputs/guide_input_touch_base.gd
Normal file
22
addons/guide/inputs/guide_input_touch_base.gd
Normal file
@ -0,0 +1,22 @@
|
||||
## Base class for generic touch input
|
||||
@tool
|
||||
class_name GUIDEInputTouchBase
|
||||
extends GUIDEInput
|
||||
|
||||
## The number of fingers to be tracked.
|
||||
@export_range(1, 5, 1, "or_greater") var finger_count:int = 1:
|
||||
set(value):
|
||||
if value < 1:
|
||||
value = 1
|
||||
finger_count = value
|
||||
emit_changed()
|
||||
|
||||
## The index of the finger for which the position/delta should be reported
|
||||
## (0 = first finger, 1 = second finger, etc.). If -1, reports the average position/delta for
|
||||
## all fingers currently touching.
|
||||
@export_range(-1, 4, 1, "or_greater") var finger_index:int = 0:
|
||||
set(value):
|
||||
if value < -1:
|
||||
value = -1
|
||||
finger_index = value
|
||||
emit_changed()
|
1
addons/guide/inputs/guide_input_touch_base.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_base.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dfmq3cijb3ju3
|
76
addons/guide/inputs/guide_input_touch_distance.gd
Normal file
76
addons/guide/inputs/guide_input_touch_distance.gd
Normal file
@ -0,0 +1,76 @@
|
||||
## Input representing the distance changes between two fingers.
|
||||
@tool
|
||||
class_name GUIDEInputTouchDistance
|
||||
extends GUIDEInput
|
||||
|
||||
var _initial_distance:float = INF
|
||||
|
||||
# We use the reset call to calculate the distance for this frame
|
||||
# so it can serve as reference for the next frame
|
||||
func _needs_reset() -> bool:
|
||||
return true
|
||||
|
||||
func _reset():
|
||||
var distance = _calculate_distance()
|
||||
# update initial distance when input is actuated or stops being actuated
|
||||
if is_finite(_initial_distance) != is_finite(distance):
|
||||
_initial_distance = distance
|
||||
|
||||
|
||||
func _begin_usage() -> void:
|
||||
# subscribe to relevant input events
|
||||
_state.touch_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage() -> void:
|
||||
# unsubscribe from input events
|
||||
_state.touch_state_changed.disconnect(_refresh)
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
var distance := _calculate_distance()
|
||||
# if either current distance or initial distance is not set,
|
||||
# we are zero
|
||||
if not is_finite(distance) or not is_finite(_initial_distance):
|
||||
_value = Vector3.ZERO
|
||||
return
|
||||
|
||||
# we assume that _initial_distance is never 0 because
|
||||
# you cannot have two fingers physically at the same place
|
||||
# on a touch screen
|
||||
_value = Vector3(distance / _initial_distance, 0, 0)
|
||||
|
||||
|
||||
func _calculate_distance() -> float:
|
||||
var pos1:Vector2 = _state.get_finger_position(0, 2)
|
||||
# if we have no position for first finger, we can immediately abort
|
||||
if not pos1.is_finite():
|
||||
return INF
|
||||
|
||||
var pos2:Vector2 = _state.get_finger_position(1, 2)
|
||||
# if there is no second finger, we can abort as well
|
||||
if not pos2.is_finite():
|
||||
return INF
|
||||
|
||||
# calculate distance for the fingers
|
||||
return pos1.distance_to(pos2)
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputTouchDistance
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputTouchDistance)"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Touch Distance"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Distance of two touching fingers."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_1D
|
1
addons/guide/inputs/guide_input_touch_distance.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_distance.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://hjbdbq1wud8h
|
45
addons/guide/inputs/guide_input_touch_position.gd
Normal file
45
addons/guide/inputs/guide_input_touch_position.gd
Normal file
@ -0,0 +1,45 @@
|
||||
@tool
|
||||
class_name GUIDEInputTouchPosition
|
||||
extends GUIDEInputTouchBase
|
||||
|
||||
|
||||
func _begin_usage():
|
||||
# subscribe to touch events
|
||||
_state.touch_state_changed.connect(_refresh)
|
||||
_refresh()
|
||||
|
||||
func _end_usage():
|
||||
# unsubscribe from touch events
|
||||
_state.touch_state_changed.disconnect(_refresh)
|
||||
|
||||
func _refresh() -> void:
|
||||
# update finger position
|
||||
var position:Vector2 = _state.get_finger_position(finger_index, finger_count)
|
||||
if not position.is_finite():
|
||||
_value = Vector3.INF
|
||||
return
|
||||
|
||||
_value = Vector3(position.x, position.y, 0)
|
||||
|
||||
|
||||
func is_same_as(other:GUIDEInput):
|
||||
return other is GUIDEInputTouchPosition and \
|
||||
other.finger_count == finger_count and \
|
||||
other.finger_index == finger_index
|
||||
|
||||
|
||||
func _to_string():
|
||||
return "(GUIDEInputTouchPosition finger_count=" + str(finger_count) + \
|
||||
" finger_index=" + str(finger_index) +")"
|
||||
|
||||
|
||||
func _editor_name() -> String:
|
||||
return "Touch Position"
|
||||
|
||||
|
||||
func _editor_description() -> String:
|
||||
return "Position of a touching finger."
|
||||
|
||||
|
||||
func _native_value_type() -> GUIDEAction.GUIDEActionValueType:
|
||||
return GUIDEAction.GUIDEActionValueType.AXIS_2D
|
1
addons/guide/inputs/guide_input_touch_position.gd.uid
Normal file
1
addons/guide/inputs/guide_input_touch_position.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://c2bvqibcqlmv5
|
1
addons/guide/inputs/guide_touch_state.gd.uid
Normal file
1
addons/guide/inputs/guide_touch_state.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://duw4xaip8onca
|
Reference in New Issue
Block a user