Files
MovementTests/addons/guide/modifiers/guide_modifier_virtual_cursor.gd
2025-05-27 19:20:46 +02:00

106 lines
3.5 KiB
GDScript

## Stateful modifier which provides a virtual "mouse" cursor driven by input. The modifier
## returns the current cursor position in pixels releative to the origin of the currently
## active window.
@tool
class_name GUIDEModifierVirtualCursor
extends GUIDEModifier
enum ScreenScale {
## Input is not scaled with input screen size. This means that the cursor will
## visually move slower on higher resolutions.
NONE = 0,
## Input is scaled with the longer axis of the screen size (e.g. width in
## landscape mode, height in portrait mode). The cursor will move with
## the same visual speed on all resolutions.
LONGER_AXIS = 1,
## Input is scaled with the shorter axis of the screen size (e.g. height in
## landscape mode, width in portrait mode). The cursor will move with the
## same visual speed on all resolutions.
SHORTER_AXIS = 2
}
## The initial position of the virtual cursor (given in screen relative coordinates)
@export var initial_position:Vector2 = Vector2(0.5, 0.5):
set(value):
initial_position = value.clamp(Vector2.ZERO, Vector2.ONE)
## The cursor movement speed in pixels.
@export var speed:Vector3 = Vector3.ONE
## Screen scaling to be applied to the cursor movement. This controls
## whether the cursor movement speed is resolution dependent or not.
## If set to anything but [code]None[/code] then the input value will
## be multiplied with the window width/height depending on the setting.
@export var screen_scale:ScreenScale = ScreenScale.LONGER_AXIS
## The scale by which the input should be scaled.
## @deprecated: use [member speed] instead.
var scale:Vector3:
get: return speed
set(value): speed = value
## If true, the cursor movement speed is in pixels per second, otherwise it is in pixels
## per frame.
@export var apply_delta_time:bool = true
## Cursor offset in pixels.
var _offset:Vector3 = Vector3.ZERO
## Returns the scaled screen size. This takes Godot's scaling factor for windows into account.
func _get_scaled_screen_size():
# Get window size, including scaling factor
var window = Engine.get_main_loop().get_root()
return window.get_screen_transform().affine_inverse() * Vector2(window.size)
func _begin_usage():
var window_size = _get_scaled_screen_size()
_offset = Vector3(window_size.x * initial_position.x, window_size.y * initial_position.y, 0)
func _modify_input(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> Vector3:
if not input.is_finite():
# input is invalid, so just return current cursor position
return _offset
var window_size = _get_scaled_screen_size()
input *= speed
if apply_delta_time:
input *= delta
var screen_scale_factor:float = 1.0
match screen_scale:
ScreenScale.LONGER_AXIS:
screen_scale_factor = max(window_size.x, window_size.y)
ScreenScale.SHORTER_AXIS:
screen_scale_factor = min(window_size.x, window_size.y)
input *= screen_scale_factor
# apply input and clamp to window size
_offset = (_offset + input).clamp(Vector3.ZERO, Vector3(window_size.x, window_size.y, 0))
return _offset
func _editor_name() -> String:
return "Virtual Cursor"
func _editor_description() -> String:
return "Stateful modifier which provides a virtual \"mouse\" cursor driven by input. The modifier\n" + \
"returns the current cursor position in pixels releative to the origin of the currently \n" + \
"active window."
# support for legacy properties
func _get_property_list():
return [
{
"name": "scale",
"type": TYPE_VECTOR3,
"usage": PROPERTY_USAGE_NO_EDITOR
}
]