diff --git a/Game/scripts/ship_ai_controller.gd b/Game/scripts/ship_ai_controller.gd index f7ae0326..af4bd042 100644 --- a/Game/scripts/ship_ai_controller.gd +++ b/Game/scripts/ship_ai_controller.gd @@ -29,9 +29,9 @@ extends AIController3D # flat, idle ship farms nothing. @export var tilt_penalty := 0.002 -# A ship (1x1x4 box) touching a wall has its centre within ~2.05 of it; -# 2.5 adds slack for contact jitter without misreading mid-field contact. -const WALL_PROXIMITY_MARGIN := 2.5 +# Contact normals with y above this are floor contact (exempt from the wall +# penalty); below it they read as wall (sideways) or ceiling (downward). +const FLOOR_NORMAL_MIN_Y := 0.7 var ship: Ship var rl_controller: RLShipController @@ -101,8 +101,9 @@ func _physics_process(delta): # Dense penalty: every tick spent pressed against a wall or the ceiling # (contact monitoring is already on for the ball-touch reward). Ships # bumping each other, the ball, or the floor is fine. The boundary is one - # body, so position tells us which surface the contact is. - if wall_contact_penalty > 0.0 and _touching_boundary() and _near_wall_or_ceiling(): + # body, so the contact normal tells us which surface: floor contact + # pushes the ship up (+Y), walls push sideways, the ceiling down. + if wall_contact_penalty > 0.0 and _wall_or_ceiling_contact(): reward -= wall_contact_penalty # Dense penalty: tilt away from upright (0 flat, max when inverted) — @@ -112,20 +113,20 @@ func _physics_process(delta): reward -= tilt_penalty * (1.0 - uprightness) * 0.5 -func _touching_boundary() -> bool: - for body in ship.get_colliding_bodies(): - if body is ArenaBoundary: +func _wall_or_ceiling_contact() -> bool: + var state := PhysicsServer3D.body_get_direct_state(ship.get_rid()) + if state == null: + return false + for i in state.get_contact_count(): + if not state.get_contact_collider_object(i) is ArenaBoundary: + continue + # Normal points from the surface into the ship: floor ≈ +Y (exempt), + # anything flatter or downward is a wall or the ceiling. + if state.get_contact_local_normal(i).y < FLOOR_NORMAL_MIN_Y: return true return false -func _near_wall_or_ceiling() -> bool: - var p := ship.global_position - return absf(p.x) > ArenaBoundary.INNER_HALF_X - WALL_PROXIMITY_MARGIN \ - or absf(p.z) > ArenaBoundary.INNER_HALF_Z - WALL_PROXIMITY_MARGIN \ - or p.y > ArenaBoundary.INNER_HEIGHT - WALL_PROXIMITY_MARGIN - - func _on_ship_body_entered(body: Node) -> void: if body.is_in_group("ball"): reward += ball_touch_reward