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 Input.parse_input_event(ev) 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 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") print("PASS: controlli touch completi") world.queue_free() await _frame() quit()