mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 08:23:45 +00:00
fix(*): Detect wall contact by contact normal so floor contact anywhere on the pitch is exempt from the wall penalty
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user