extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_joystick_control.gd # Verifies the virtual joystick steers the fleet: holding the stick in a # direction flies the fleet that way, and releasing brings it to a stop # rather than freezing it mid-flight. Replaces the old tap-to-move test, # since tapping no longer issues move orders. # # Touch events are handed to the joystick directly rather than through # Input.parse_input_event: headless mode's placeholder window rescales # synthetic touch coordinates by an environment-specific factor, which # would land them outside the joystick's rect for reasons that have nothing # to do with the behaviour under test. var main_scene: Node var fleet: Node var joystick: TouchJoystick var stage: int = 0 var elapsed: float = 0.0 var start_centroid: Vector2 var moving_centroid: Vector2 var stop_check_frames: int = 0 func _initialize() -> void: var packed: PackedScene = load("res://Main.tscn") main_scene = packed.instantiate() root.add_child(main_scene) fleet = main_scene.get_node("Fleet") joystick = main_scene.get_node("UI/Joystick") func _press(local: Vector2) -> void: var ev: InputEventScreenTouch = InputEventScreenTouch.new() ev.index = 0 ev.pressed = true ev.position = joystick.global_position + local joystick.handle_touch(ev) func _drag(local: Vector2) -> void: var ev: InputEventScreenDrag = InputEventScreenDrag.new() ev.index = 0 ev.position = joystick.global_position + local joystick.handle_drag(ev) func _release(local: Vector2) -> void: var ev: InputEventScreenTouch = InputEventScreenTouch.new() ev.index = 0 ev.pressed = false ev.position = joystick.global_position + local joystick.handle_touch(ev) func _process(delta: float) -> bool: elapsed += delta match stage: 0: main_scene.get_node("AsteroidField").set_process(false) main_scene.get_node("EnemySpawner").set_process(false) start_centroid = fleet.get_centroid() # The joystick receives touches via GameController's # _unhandled_input, which never fires for a Control that # consumes them through the GUI system first. With Godot's # default mouse_filter (STOP) the stick silently does nothing # on a real device - it must be IGNORE. Checked structurally # because the rest of this test drives handle_touch() directly # and so cannot catch it. if joystick.mouse_filter != Control.MOUSE_FILTER_IGNORE: print("joystick mouse_filter=%d (needs IGNORE=%d)" % [ joystick.mouse_filter, Control.MOUSE_FILTER_IGNORE ]) print("TEST_JOYSTICK_CONTROL: FAIL (stick would swallow its own touches)") quit(1) return true # Push the stick hard to the right. var centre: Vector2 = joystick.size / 2.0 _press(centre) _drag(centre + Vector2(joystick.max_radius, 0.0)) if joystick.direction.x < 0.9: print("joystick direction wrong: %s" % joystick.direction) print("TEST_JOYSTICK_CONTROL: FAIL (stick did not register)") quit(1) return true elapsed = 0.0 stage = 1 return false 1: if elapsed < 1.5: return false moving_centroid = fleet.get_centroid() var moved_right: bool = moving_centroid.x - start_centroid.x > 100.0 var stayed_level: bool = abs(moving_centroid.y - start_centroid.y) < 80.0 print("moved from %s to %s (right=%s level=%s)" % [ start_centroid, moving_centroid, moved_right, stayed_level ]) if not (moved_right and stayed_level): print("TEST_JOYSTICK_CONTROL: FAIL (fleet did not follow the stick)") quit(1) return true _release(joystick.size / 2.0) if joystick.direction != Vector2.ZERO: print("TEST_JOYSTICK_CONTROL: FAIL (direction not cleared on release)") quit(1) return true elapsed = 0.0 stage = 2 return false 2: # After release the fleet should coast to a halt, not freeze # instantly and not keep flying. if elapsed < 4.0: return false var speed: float = 0.0 for ship in fleet.ships: speed = max(speed, ship.velocity.length()) var stopped: bool = speed < 1.0 var coasted: bool = fleet.get_centroid().x > moving_centroid.x print("after release: max_speed=%.3f stopped=%s coasted_further=%s" % [ speed, stopped, coasted ]) if stopped and coasted: print("TEST_JOYSTICK_CONTROL: PASS") quit(0) else: print("TEST_JOYSTICK_CONTROL: FAIL (did not coast to a stop)") quit(1) return true return false