205 lines
7.7 KiB
GDScript
205 lines
7.7 KiB
GDScript
extends SceneTree
|
|
## Verifica i controlli on-screen e il loro rilevamento automatico.
|
|
##
|
|
## --touch forza la UI touch e prova joystick, area di puntamento e pulsanti
|
|
## --no-touch verifica che su un contesto non mobile/touch la UI NON compaia
|
|
##
|
|
## Gli eventi sono iniettati con Input.parse_input_event come fa il browser
|
|
## (DisplayServerWeb invia InputEventScreenTouch/Drag), quindi passa la stessa
|
|
## pipeline di multitouch usata su dispositivo reale.
|
|
##
|
|
## Nota sull'ambiente: in --headless la finestra del DisplayServer e' 64x36,
|
|
## quindi la stretch transform (canvas_items) riscala le coordinate iniettate e
|
|
## una posizione di (200,500) arriva al Control come (4000,9720). In quel caso il
|
|
## test pilota direttamente i gestori in spazio viewport; con un display reale
|
|
## usa la pipeline eventi completa. La logica verificata e' la stessa.
|
|
const TouchScript = preload("res://scripts/touch_controls.gd")
|
|
|
|
var _direct := false
|
|
|
|
var world: Node
|
|
var touch: Node
|
|
var player: Node
|
|
var _log: Array[String] = []
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
func _touch(index: int, pos: Vector2, pressed: bool) -> void:
|
|
if _direct:
|
|
if pressed:
|
|
touch._press(index, pos)
|
|
else:
|
|
touch._release(index)
|
|
return
|
|
var ev := InputEventScreenTouch.new()
|
|
ev.index = index
|
|
ev.position = pos
|
|
ev.pressed = pressed
|
|
Input.parse_input_event(ev)
|
|
|
|
func _drag(index: int, pos: Vector2, relative: Vector2) -> void:
|
|
if _direct:
|
|
touch._drag(index, pos, relative)
|
|
return
|
|
var ev := InputEventScreenDrag.new()
|
|
ev.index = index
|
|
ev.position = pos
|
|
ev.relative = relative
|
|
ev.screen_relative = relative
|
|
Input.parse_input_event(ev)
|
|
Input.flush_buffered_events()
|
|
|
|
func _steps(count: int) -> void:
|
|
for i in count:
|
|
await physics_frame
|
|
|
|
func _frame() -> void:
|
|
await process_frame
|
|
|
|
func run_test() -> void:
|
|
var args := OS.get_cmdline_user_args()
|
|
world = load("res://scenes/main.tscn").instantiate()
|
|
root.add_child(world)
|
|
player = world.player
|
|
|
|
if "--no-touch" in args:
|
|
await _frame()
|
|
assert(world.touch == null)
|
|
assert(not player.touch_mode)
|
|
print("PASS: nessuna UI touch quando il contesto non e' mobile ne' touchscreen")
|
|
world.queue_free()
|
|
await _frame()
|
|
quit()
|
|
return
|
|
|
|
await _frame()
|
|
# --- presenza e costruzione della UI ---
|
|
assert(world.touch != null)
|
|
assert(player.touch_mode)
|
|
touch = world.touch
|
|
_direct = DisplayServer.get_name() == "headless"
|
|
assert(touch._buttons.size() == 4)
|
|
for role in [TouchScript.Role.FIRE, TouchScript.Role.GRENADE, TouchScript.Role.RELOAD, TouchScript.Role.JUMP]:
|
|
assert(touch._buttons.has(role))
|
|
print("PASS: UI touch creata con joystick, area di puntamento e 4 pulsanti%s" % (" [modalita' diretta: headless]" if _direct else " [pipeline eventi: display reale]"))
|
|
|
|
# --- joystick: trascinamento nella meta' sinistra -> movimento ---
|
|
var start_pos := Vector2(200, 500)
|
|
var before: Vector3 = player.global_position
|
|
_touch(0, start_pos, true)
|
|
await _frame()
|
|
_drag(0, start_pos + Vector2(140, -120), Vector2(140, -120))
|
|
await _frame()
|
|
assert(touch.move_vector.length() > 0.5)
|
|
await _steps(12)
|
|
var moved: Vector3 = player.global_position
|
|
assert(moved.x > before.x + 0.05) # verso destra (asse +X)
|
|
assert(moved.z < before.z - 0.05) # verso avanti (asse -Z)
|
|
print("PASS: joystick muovi -> delta (%.2f, %.2f) con vettore %.2f" % [moved.x - before.x, moved.z - before.z, touch.move_vector.length()])
|
|
|
|
# --- area di puntamento: trascinamento nella meta' destra -> rotazione ---
|
|
var yaw_before: float = player.rotation.y
|
|
_touch(1, Vector2(900, 300), true)
|
|
await _frame()
|
|
_drag(1, Vector2(960, 320), Vector2(60, 20))
|
|
await _frame()
|
|
assert(player.rotation.y < yaw_before - 0.001) # trascinare a destra ruota a destra
|
|
assert(absf(player.camera.rotation.x) <= 1.4)
|
|
print("PASS: area di puntamento -> rotazione visuale (yaw %.3f -> %.3f)" % [yaw_before, player.rotation.y])
|
|
|
|
# --- multitouch: i due tocchi coesistono, il rilascio di uno non ferma l'altro ---
|
|
assert(touch._touches.values().has(TouchScript.Role.JOY))
|
|
assert(touch._touches.values().has(TouchScript.Role.LOOK))
|
|
_touch(1, Vector2(960, 320), false)
|
|
await _frame()
|
|
assert(touch._touches.values().has(TouchScript.Role.JOY)) # il movimento resta attivo
|
|
print("PASS: multitouch simultaneo (movimento + puntamento) con indici separati")
|
|
|
|
_touch(0, start_pos + Vector2(140, -120), false)
|
|
await _frame()
|
|
assert(touch.move_vector == Vector2.ZERO)
|
|
await _steps(6)
|
|
assert(absf(player.velocity.x) < 0.01 and absf(player.velocity.z) < 0.01)
|
|
print("PASS: rilascio del joystick -> vettore azzerato e giocatore fermo")
|
|
|
|
# --- pulsante FUOCO: tenere premuto spara in automatico ---
|
|
player.ammo = 30
|
|
player.cooldown = 0.0
|
|
var fire_pos: Vector2 = touch._buttons[TouchScript.Role.FIRE].center
|
|
_touch(0, fire_pos, true)
|
|
await _steps(10)
|
|
assert(player.ammo < 30)
|
|
var fire_yaw: float = player.rotation.y
|
|
_drag(0, fire_pos + Vector2(-80, 10), Vector2(-80, 10))
|
|
await _frame()
|
|
assert(player.rotation.y > fire_yaw + 0.001)
|
|
assert(Input.is_action_pressed("shoot"))
|
|
print("PASS: trascinare FUOCO mira senza interrompere lo sparo")
|
|
var after_fire: int = player.ammo
|
|
_touch(0, fire_pos, false)
|
|
await _steps(10)
|
|
assert(player.ammo == after_fire) # rilasciato: non spara piu'
|
|
print("PASS: FUOCO tiene premuto -> 30 -> %d colpi, si ferma al rilascio" % after_fire)
|
|
|
|
# --- pulsante RICARICA ---
|
|
_touch(0, touch._buttons[TouchScript.Role.RELOAD].center, true)
|
|
await _steps(2) # l'edge dell'azione e' visto dal _physics_process
|
|
_touch(0, touch._buttons[TouchScript.Role.RELOAD].center, false)
|
|
assert(player.reloading)
|
|
await create_timer(1.8).timeout
|
|
assert(player.ammo == 30 and not player.reloading)
|
|
print("PASS: RICARICA -> caricatore pieno (30)")
|
|
|
|
# --- pulsante GRANATA ---
|
|
assert(player.grenade_cooldown <= 0)
|
|
_touch(0, touch._buttons[TouchScript.Role.GRENADE].center, true)
|
|
await _steps(2)
|
|
_touch(0, touch._buttons[TouchScript.Role.GRENADE].center, false)
|
|
assert(player.grenade_cooldown > 0)
|
|
assert(get_nodes_in_group("grenades").size() >= 1)
|
|
print("PASS: GRANATA -> lancio eseguito (cooldown %.0f s)" % player.grenade_cooldown)
|
|
player.grenade_cooldown = 0.0
|
|
|
|
# --- pulsante SALTO ---
|
|
await _steps(10)
|
|
assert(player.is_on_floor())
|
|
_touch(0, touch._buttons[TouchScript.Role.JUMP].center, true)
|
|
await _steps(2)
|
|
_touch(0, touch._buttons[TouchScript.Role.JUMP].center, false)
|
|
await _steps(2)
|
|
assert(player.velocity.y > 0.5)
|
|
print("PASS: SALTO -> velocita' verticale %.1f m/s" % player.velocity.y)
|
|
|
|
# --- rilevamento: su questo contesto (desktop headless) la UI non e' automatica ---
|
|
assert(not TouchScript.should_auto_enable())
|
|
print("PASS: TouchControls.should_auto_enable() = false su desktop -> la UI compare solo se serve")
|
|
assert(TouchScript._look_delta(Vector2(100, 20), 1000).is_equal_approx(TouchScript._look_delta(Vector2(200, 40), 2000)))
|
|
print("PASS: sensibilita' invariata per swipe proporzionali su schermi diversi")
|
|
_touch(0, start_pos, true)
|
|
player.take_damage(1000)
|
|
await _frame()
|
|
await _frame()
|
|
assert(not touch.enabled and touch._touches.is_empty())
|
|
assert(not Input.is_action_pressed("shoot"))
|
|
assert(is_instance_valid(player.restart_button))
|
|
await create_timer(0.7).timeout
|
|
assert(not player.restart_button.disabled)
|
|
current_scene = world
|
|
# Verifica anche il reload effettivo, non solo il pulsante.
|
|
var restart_event := InputEventScreenTouch.new()
|
|
restart_event.pressed = true
|
|
restart_event.position = player.restart_button.get_global_rect().get_center()
|
|
player._input(restart_event)
|
|
assert(player.restart_button.disabled)
|
|
await _frame()
|
|
await _frame()
|
|
assert(current_scene != world)
|
|
assert(current_scene.player.hp > 0)
|
|
world = current_scene
|
|
print("PASS: morte libera input, RICOMINCIA touch ricarica davvero la partita")
|
|
print("PASS: controlli touch completi")
|
|
world.queue_free()
|
|
await _frame()
|
|
quit()
|