# 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)