Files

87 lines
2.5 KiB
GDScript

# VirtualJoystick —— continuous left-thumb movement input.
extends Control
signal axis_changed(axis: Vector2)
const DEAD_ZONE := 0.12
const KNOB_RADIUS := 29.0
var _touch_index := -1
var _axis := Vector2.ZERO
var _knob := Vector2.ZERO
func setup(diameter := 164.0) -> void:
custom_minimum_size = Vector2(diameter, diameter)
size = Vector2(diameter, diameter)
mouse_filter = Control.MOUSE_FILTER_STOP
queue_redraw()
func axis() -> Vector2:
return _axis
func _gui_input(event: InputEvent) -> void:
if event is InputEventScreenTouch:
if event.pressed and _touch_index < 0:
_touch_index = event.index
_update_from_position(event.position)
elif not event.pressed and event.index == _touch_index:
_touch_index = -1
_set_axis(Vector2.ZERO)
accept_event()
return
if event is InputEventScreenDrag and event.index == _touch_index:
_update_from_position(event.position)
accept_event()
return
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
_touch_index = -2
_update_from_position(event.position)
else:
_touch_index = -1
_set_axis(Vector2.ZERO)
accept_event()
return
if event is InputEventMouseMotion and _touch_index == -2:
_update_from_position(event.position)
accept_event()
func _update_from_position(pos: Vector2) -> void:
var center := size * 0.5
var radius := maxf(1.0, minf(size.x, size.y) * 0.5 - KNOB_RADIUS - 8.0)
var delta := pos - center
var raw := delta / radius
var magnitude := minf(raw.length(), 1.0)
var direction := raw.normalized() if magnitude > 0.001 else Vector2.ZERO
var output := direction * magnitude
if output.length() < DEAD_ZONE:
output = Vector2.ZERO
else:
output = output.normalized() * ((output.length() - DEAD_ZONE) / (1.0 - DEAD_ZONE))
_knob = direction * radius * magnitude
_set_axis(output)
queue_redraw()
func _set_axis(value: Vector2) -> void:
var next := value.limit_length(1.0)
if next.is_equal_approx(_axis):
return
_axis = next
axis_changed.emit(_axis)
queue_redraw()
func cancel_press() -> void:
_touch_index = -1
_knob = Vector2.ZERO
_set_axis(Vector2.ZERO)
func _draw() -> void:
var center := size * 0.5
var outer := minf(size.x, size.y) * 0.5 - 5.0
if outer <= 0.0:
return
draw_circle(center, outer, Color(0.025, 0.04, 0.07, 0.38))
draw_arc(center, outer, 0.0, TAU, 48, Color(0.72, 0.83, 0.96, 0.42), 2.0)
draw_circle(center + _knob, KNOB_RADIUS, Color(0.18, 0.3, 0.48, 0.78))
draw_arc(center + _knob, KNOB_RADIUS, 0.0, TAU, 32, Color(0.7, 0.85, 1.0, 0.8), 2.0)