feat: Implement core game managers, player movement logic, and initial UI scenes.
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
extends Control
|
||||
|
||||
# VirtualJoystick - Touch joystick for mobile movement control
|
||||
# Provides 8-directional movement input
|
||||
|
||||
signal direction_changed(direction: Vector2i)
|
||||
signal joystick_released
|
||||
|
||||
@export var dead_zone: float = 0.2
|
||||
@export var clamp_zone: float = 0.8
|
||||
@export var joystick_radius: float = 60.0
|
||||
@export var knob_radius: float = 25.0
|
||||
@export var repeat_delay: float = 0.3 # Initial delay before repeat
|
||||
@export var repeat_rate: float = 0.15 # Repeat rate for continuous movement
|
||||
|
||||
var base_color: Color = Color(1, 1, 1, 0.4)
|
||||
var knob_color: Color = Color(1, 1, 1, 0.7)
|
||||
var pressed_color: Color = Color(0.4, 0.9, 0.4, 0.8)
|
||||
|
||||
var is_pressed: bool = false
|
||||
var touch_index: int = -1
|
||||
var center_position: Vector2
|
||||
var current_direction: Vector2 = Vector2.ZERO
|
||||
var last_grid_direction: Vector2i = Vector2i.ZERO
|
||||
|
||||
var _repeat_timer: float = 0.0
|
||||
var _initial_repeat: bool = true
|
||||
|
||||
func _ready():
|
||||
# Set minimum size for touch target
|
||||
custom_minimum_size = Vector2(joystick_radius * 2 + 40, joystick_radius * 2 + 40)
|
||||
center_position = size / 2
|
||||
|
||||
# Enable touch input
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
set_process(true)
|
||||
|
||||
func _draw():
|
||||
# Draw base circle
|
||||
var base_circle_color = pressed_color if is_pressed else base_color
|
||||
draw_circle(center_position, joystick_radius, base_circle_color)
|
||||
draw_arc(center_position, joystick_radius, 0, TAU, 64, Color.WHITE, 2.0)
|
||||
|
||||
# Draw knob
|
||||
var knob_pos = center_position + current_direction * joystick_radius * clamp_zone
|
||||
var knob_circle_color = pressed_color if is_pressed else knob_color
|
||||
draw_circle(knob_pos, knob_radius, knob_circle_color)
|
||||
draw_arc(knob_pos, knob_radius, 0, TAU, 32, Color.WHITE, 1.5)
|
||||
|
||||
# Draw direction indicators (8 directions)
|
||||
for i in range(8):
|
||||
var angle = i * TAU / 8
|
||||
var line_start = center_position + Vector2.from_angle(angle) * (joystick_radius * 0.6)
|
||||
var line_end = center_position + Vector2.from_angle(angle) * (joystick_radius * 0.9)
|
||||
draw_line(line_start, line_end, Color(1, 1, 1, 0.3), 2.0)
|
||||
|
||||
func _process(delta: float):
|
||||
# Handle continuous movement while holding joystick
|
||||
if is_pressed and last_grid_direction != Vector2i.ZERO:
|
||||
_repeat_timer -= delta
|
||||
if _repeat_timer <= 0:
|
||||
emit_signal("direction_changed", last_grid_direction)
|
||||
_repeat_timer = repeat_rate
|
||||
_initial_repeat = false
|
||||
|
||||
func _gui_input(event: InputEvent):
|
||||
if event is InputEventScreenTouch:
|
||||
if event.pressed:
|
||||
_start_touch(event.index, event.position)
|
||||
elif event.index == touch_index:
|
||||
_end_touch()
|
||||
|
||||
elif event is InputEventScreenDrag:
|
||||
if event.index == touch_index:
|
||||
_update_touch(event.position)
|
||||
|
||||
# Mouse support for testing
|
||||
elif event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
_start_touch(0, event.position)
|
||||
else:
|
||||
_end_touch()
|
||||
|
||||
elif event is InputEventMouseMotion:
|
||||
if is_pressed and touch_index == 0:
|
||||
_update_touch(event.position)
|
||||
|
||||
func _start_touch(index: int, pos: Vector2):
|
||||
is_pressed = true
|
||||
touch_index = index
|
||||
_repeat_timer = repeat_delay # Use longer initial delay
|
||||
_initial_repeat = true
|
||||
_update_touch(pos)
|
||||
queue_redraw()
|
||||
|
||||
func _end_touch():
|
||||
is_pressed = false
|
||||
touch_index = -1
|
||||
current_direction = Vector2.ZERO
|
||||
last_grid_direction = Vector2i.ZERO
|
||||
_repeat_timer = 0.0
|
||||
_initial_repeat = true
|
||||
emit_signal("joystick_released")
|
||||
queue_redraw()
|
||||
|
||||
func _update_touch(pos: Vector2):
|
||||
var diff = pos - center_position
|
||||
var distance = diff.length()
|
||||
|
||||
if distance > 0:
|
||||
current_direction = diff.normalized() * clampf(distance / joystick_radius, 0, clamp_zone)
|
||||
else:
|
||||
current_direction = Vector2.ZERO
|
||||
|
||||
# Convert to 8-directional grid movement
|
||||
var grid_dir = _get_grid_direction(current_direction)
|
||||
if grid_dir != last_grid_direction:
|
||||
last_grid_direction = grid_dir
|
||||
if grid_dir != Vector2i.ZERO:
|
||||
emit_signal("direction_changed", grid_dir)
|
||||
|
||||
queue_redraw()
|
||||
|
||||
func _get_grid_direction(dir: Vector2) -> Vector2i:
|
||||
if dir.length() < dead_zone:
|
||||
return Vector2i.ZERO
|
||||
|
||||
# Determine 8-directional output
|
||||
var angle = dir.angle()
|
||||
|
||||
# Divide circle into 8 sectors (each 45 degrees)
|
||||
var sector = int(round(angle / (TAU / 8))) % 8
|
||||
if sector < 0:
|
||||
sector += 8
|
||||
|
||||
match sector:
|
||||
0: return Vector2i(1, 0) # East
|
||||
1: return Vector2i(1, 1) # Southeast
|
||||
2: return Vector2i(0, 1) # South
|
||||
3: return Vector2i(-1, 1) # Southwest
|
||||
4: return Vector2i(-1, 0) # West
|
||||
5: return Vector2i(-1, -1) # Northwest
|
||||
6: return Vector2i(0, -1) # North
|
||||
7: return Vector2i(1, -1) # Northeast
|
||||
|
||||
return Vector2i.ZERO
|
||||
|
||||
func get_direction() -> Vector2i:
|
||||
"""Get the current grid direction for polling."""
|
||||
return last_grid_direction
|
||||
Reference in New Issue
Block a user