fix(game-mode): recover ships/ball that escape through an open goal in every mode

The goal mouths are now a real navigable hole in the end walls, sized to the
ball rather than the ship — a ship's 1x1 cross-section fits through it, and
there's nothing behind the net to stop it. The escape failsafe previously
only existed in TrainingMode (where a physics regression just wastes
training time); now that any ship can genuinely fly out through an open
goal, every mode needs it or a stray ship/ball falls into the void with no
way back short of quitting. Moved up to GameMode as the shared default
_physics_process, removing TrainingMode's now-duplicate copy.
This commit is contained in:
Josh Creek
2026-08-05 09:16:00 +01:00
parent 0f7603d3cb
commit 661c588fef
2 changed files with 34 additions and 24 deletions
-24
View File
@@ -90,12 +90,6 @@ const MAX_RANDOM_SHIP_SPEED := 8.0
# Sim runs at 60 physics ticks per sim-second regardless of speedup.
const TICKS_PER_SIM_SECOND := 60.0
# The arena is physically enclosed, so nothing should ever get this far out.
# If a body escapes anyway (physics regression, boundary edit), it is warned
# about and respawned with no reward change and no episode end — a multi-hour
# training run must survive it, and the escape must not shape rewards.
const ESCAPE_MARGIN := 15.0
var _agents: Array[ShipAIController] = []
# Eval mode state (see header comment)
@@ -320,24 +314,6 @@ func _physics_process(_delta):
return
# Escape failsafe: see ESCAPE_MARGIN.
func _respawn_escaped_bodies() -> void:
for ship in ships:
if is_instance_valid(ship) and _escaped(ship.global_position):
push_warning("TrainingMode: ship escaped the enclosed arena — check boundary colliders")
_place_body(ship, _ship_spawn_transforms[ship], Vector3.ZERO, Vector3.ZERO)
if is_instance_valid(ball) and _escaped(ball.global_position):
push_warning("TrainingMode: ball escaped the enclosed arena — check boundary colliders")
_place_body(ball, arena.get_ball_spawn(), Vector3.ZERO, Vector3.ZERO)
func _escaped(position: Vector3) -> bool:
return absf(position.x) > ArenaBoundary.INNER_HALF_X + ESCAPE_MARGIN \
or absf(position.z) > ArenaBoundary.INNER_HALF_Z + ESCAPE_MARGIN \
or position.y < -ESCAPE_MARGIN \
or position.y > ArenaBoundary.INNER_HEIGHT + ESCAPE_MARGIN
func _on_goal_scored(conceding_team: int) -> void:
if _eval:
_eval_goals[1 - conceding_team] += 1