|
1 | 1 | extends Control |
2 | 2 |
|
3 | | -# Virtual joystick variables |
4 | | -var joystick_active = false |
5 | | -var joystick_origin = Vector2.ZERO |
6 | | -var joystick_position = Vector2.ZERO |
7 | | -var joystick_direction = Vector2.ZERO |
8 | | -var joystick_radius = 100.0 |
| 3 | +@export var joystick_radius := 140.0 |
| 4 | +@export var joystick_handle_radius := 52.0 |
| 5 | +@export var shoot_button_size := Vector2(180, 180) |
| 6 | +@export var screen_margin := 24.0 |
| 7 | +@export var joystick_deadzone := 0.12 |
9 | 8 |
|
10 | | -# Shoot button variables |
11 | | -var shoot_button_rect = Rect2(0, 0, 150, 150) |
12 | | -var shoot_button_pressed = false |
| 9 | +var joystick_active := false |
| 10 | +var joystick_origin := Vector2.ZERO |
| 11 | +var joystick_position := Vector2.ZERO |
| 12 | +var joystick_direction := Vector2.ZERO |
| 13 | +var joystick_touch_index := -1 |
13 | 14 |
|
14 | | -# Signals |
15 | | -signal joystick_input(direction) |
| 15 | +var shoot_button_rect := Rect2() |
| 16 | +var shoot_button_pressed := false |
| 17 | +var shoot_touch_index := -1 |
| 18 | + |
| 19 | +signal joystick_input(direction: Vector2) |
16 | 20 | signal shoot_pressed() |
17 | 21 |
|
18 | 22 | func _ready(): |
19 | | - # Position the shoot button in the bottom right corner |
20 | | - var screen_size = get_viewport().get_visible_rect().size |
21 | | - shoot_button_rect.position = Vector2(screen_size.x - shoot_button_rect.size.x - 20, |
22 | | - screen_size.y - shoot_button_rect.size.y - 20) |
| 23 | + var should_show := OS.has_feature("mobile") or OS.has_feature("web") or DisplayServer.is_touchscreen_mode_enabled() |
| 24 | + visible = should_show |
| 25 | + set_process(should_show) |
| 26 | + set_process_input(should_show) |
| 27 | + mouse_filter = Control.MOUSE_FILTER_IGNORE |
| 28 | + top_level = true |
| 29 | + focus_mode = Control.FOCUS_NONE |
| 30 | + z_index = 128 |
| 31 | + set_anchors_preset(Control.PRESET_FULL_RECT) |
| 32 | + update_layout() |
| 33 | + if get_viewport(): |
| 34 | + get_viewport().size_changed.connect(update_layout) |
| 35 | + |
| 36 | +func update_layout(): |
| 37 | + if not is_visible_in_tree(): |
| 38 | + return |
| 39 | + var screen_size := get_viewport().get_visible_rect().size if get_viewport() else size |
| 40 | + shoot_button_rect.size = shoot_button_size |
| 41 | + shoot_button_rect.position = Vector2( |
| 42 | + screen_size.x - shoot_button_rect.size.x - screen_margin, |
| 43 | + screen_size.y - shoot_button_rect.size.y - screen_margin |
| 44 | + ) |
| 45 | + queue_redraw() |
23 | 46 |
|
24 | 47 | func _input(event): |
25 | 48 | if event is InputEventScreenTouch: |
26 | | - if event.pressed: |
27 | | - # Check if touch is within shoot button |
28 | | - if shoot_button_rect.has_point(event.position): |
29 | | - shoot_button_pressed = true |
30 | | - emit_signal("shoot_pressed") |
31 | | - else: |
32 | | - # Start joystick |
33 | | - joystick_active = true |
34 | | - joystick_origin = event.position |
35 | | - joystick_position = event.position |
36 | | - else: |
37 | | - # End touch |
38 | | - if shoot_button_rect.has_point(event.position) and shoot_button_pressed: |
39 | | - shoot_button_pressed = false |
40 | | - |
41 | | - if joystick_active: |
42 | | - joystick_active = false |
43 | | - joystick_direction = Vector2.ZERO |
44 | | - emit_signal("joystick_input", Vector2.ZERO) |
45 | | - |
46 | | - elif event is InputEventScreenDrag and joystick_active: |
47 | | - joystick_position = event.position |
48 | | - |
49 | | - # Calculate joystick direction and limit to radius |
50 | | - var direction = joystick_position - joystick_origin |
51 | | - if direction.length() > joystick_radius: |
52 | | - direction = direction.normalized() * joystick_radius |
53 | | - joystick_position = joystick_origin + direction |
54 | | - |
55 | | - # Normalize for input |
56 | | - joystick_direction = direction / joystick_radius |
57 | | - emit_signal("joystick_input", joystick_direction) |
| 49 | + _handle_screen_touch(event) |
| 50 | + elif event is InputEventScreenDrag: |
| 51 | + _handle_screen_drag(event) |
58 | 52 |
|
59 | | -func _draw(): |
60 | | - if OS.has_feature("mobile") or OS.has_feature("web"): |
61 | | - # Draw joystick if active |
62 | | - if joystick_active: |
63 | | - # Draw joystick base |
64 | | - draw_circle(joystick_origin, joystick_radius, Color(0.5, 0.5, 0.5, 0.5)) |
65 | | - # Draw joystick handle |
66 | | - draw_circle(joystick_position, 50, Color(0.7, 0.7, 0.7, 0.7)) |
67 | | - |
68 | | - # Draw shoot button |
69 | | - var button_color = Color(1.0, 0.3, 0.3, 0.7) |
70 | | - if shoot_button_pressed: |
71 | | - button_color = Color(1.0, 0.5, 0.5, 0.8) |
72 | | - |
73 | | - draw_circle(shoot_button_rect.position + shoot_button_rect.size / 2, shoot_button_rect.size.x / 2, button_color) |
| 53 | +func _handle_screen_touch(event: InputEventScreenTouch) -> void: |
| 54 | + if event.pressed: |
| 55 | + if shoot_touch_index == -1 and shoot_button_rect.has_point(event.position): |
| 56 | + shoot_touch_index = event.index |
| 57 | + shoot_button_pressed = true |
| 58 | + emit_signal("shoot_pressed") |
| 59 | + queue_redraw() |
| 60 | + return |
74 | 61 |
|
75 | | - # Draw shoot icon (simple crosshair) |
76 | | - var center = shoot_button_rect.position + shoot_button_rect.size / 2 |
77 | | - var line_length = 30 |
78 | | - draw_line(center - Vector2(line_length, 0), center + Vector2(line_length, 0), Color.WHITE, 5) |
79 | | - draw_line(center - Vector2(0, line_length), center + Vector2(0, line_length), Color.WHITE, 5) |
| 62 | + if joystick_touch_index == -1: |
| 63 | + joystick_touch_index = event.index |
| 64 | + joystick_active = true |
| 65 | + joystick_origin = event.position |
| 66 | + joystick_position = event.position |
| 67 | + joystick_direction = Vector2.ZERO |
| 68 | + emit_signal("joystick_input", joystick_direction) |
| 69 | + queue_redraw() |
| 70 | + else: |
| 71 | + if event.index == shoot_touch_index: |
| 72 | + shoot_touch_index = -1 |
| 73 | + shoot_button_pressed = false |
| 74 | + queue_redraw() |
| 75 | + if event.index == joystick_touch_index: |
| 76 | + joystick_touch_index = -1 |
| 77 | + joystick_active = false |
| 78 | + joystick_direction = Vector2.ZERO |
| 79 | + emit_signal("joystick_input", Vector2.ZERO) |
| 80 | + queue_redraw() |
80 | 81 |
|
81 | | -func _process(_delta): |
82 | | - # Redraw the controls every frame to handle movement |
| 82 | +func _handle_screen_drag(event: InputEventScreenDrag) -> void: |
| 83 | + if event.index != joystick_touch_index or not joystick_active: |
| 84 | + return |
| 85 | + joystick_position = event.position |
| 86 | + var direction := joystick_position - joystick_origin |
| 87 | + var distance := direction.length() |
| 88 | + if distance > joystick_radius and distance > 0: |
| 89 | + direction = direction / distance * joystick_radius |
| 90 | + joystick_position = joystick_origin + direction |
| 91 | + var normalized := direction / joystick_radius |
| 92 | + if normalized.length() < joystick_deadzone: |
| 93 | + normalized = Vector2.ZERO |
| 94 | + joystick_direction = normalized |
| 95 | + emit_signal("joystick_input", joystick_direction) |
83 | 96 | queue_redraw() |
| 97 | + |
| 98 | +func _draw(): |
| 99 | + if not visible: |
| 100 | + return |
| 101 | + if not (OS.has_feature("mobile") or OS.has_feature("web") or DisplayServer.is_touchscreen_mode_enabled()): |
| 102 | + return |
| 103 | + if joystick_active: |
| 104 | + draw_circle(joystick_origin, joystick_radius, Color(0.5, 0.5, 0.5, 0.35)) |
| 105 | + draw_circle(joystick_position, joystick_handle_radius, Color(0.8, 0.8, 0.8, 0.55)) |
| 106 | + var button_color := Color(1.0, 0.3, 0.3, 0.65) |
| 107 | + if shoot_button_pressed: |
| 108 | + button_color = Color(1.0, 0.5, 0.5, 0.8) |
| 109 | + draw_circle(shoot_button_rect.position + shoot_button_rect.size / 2.0, shoot_button_rect.size.x / 2.0, button_color) |
| 110 | + var center := shoot_button_rect.position + shoot_button_rect.size / 2.0 |
| 111 | + var line_length := shoot_button_rect.size.x * 0.18 |
| 112 | + draw_line(center - Vector2(line_length, 0), center + Vector2(line_length, 0), Color.WHITE, 5) |
| 113 | + draw_line(center - Vector2(0, line_length), center + Vector2(0, line_length), Color.WHITE, 5) |
0 commit comments