feat: complete mobile UI implementation
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user