mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
feat(*): Add rounded arena boundaries and reward shaping to curb corner-camping
This commit is contained in:
@@ -16,6 +16,12 @@ extends AIController3D
|
||||
# per sim-second); event terms fire once. Exported so tuning needs no code
|
||||
# edits. Goal rewards are added by TrainingMode, which owns goal events.
|
||||
@export var ball_touch_reward := 1.0
|
||||
# Ball touches pay out at most once per this many physics ticks (1 sim-
|
||||
# second at 60). Run07 lesson: body_entered re-fires on every micro-
|
||||
# separation, so pinning the ball against a surface farmed ~2 touches/s —
|
||||
# outearning every other term while the goal rate fell. The cooldown keeps
|
||||
# touches a stepping-stone signal instead of the objective.
|
||||
@export var ball_touch_cooldown_ticks := 60
|
||||
@export var velocity_to_ball_weight := 0.02
|
||||
@export var ball_velocity_to_goal_weight := 0.004
|
||||
# Per-tick penalty scaled by distance to the ball (full value at the arena's
|
||||
@@ -36,6 +42,12 @@ extends AIController3D
|
||||
# value (-0.12/s) when inverted. A penalty rather than an upright bonus so a
|
||||
# flat, idle ship farms nothing.
|
||||
@export var tilt_penalty := 0.002
|
||||
# Per-tick bonus for own speed: 0 stationary, full value (+0.24/s) at
|
||||
# max_speed. Run07 lesson: after the kickoff flurry both ships parked next to
|
||||
# a cornered ball — with every other dense term near zero there, standing
|
||||
# still was a rest state. Sized well below velocity_to_ball_weight so flying
|
||||
# fast toward the ball still beats flying fast anywhere else.
|
||||
@export var speed_reward_weight := 0.004
|
||||
|
||||
# Contact normals with y above this are floor contact (exempt from the wall
|
||||
# penalty); below it they read as wall (sideways) or ceiling (downward).
|
||||
@@ -55,6 +67,8 @@ var ball: RigidBody3D
|
||||
var opponent: Ship
|
||||
var attack_goal_position: Vector3
|
||||
|
||||
var _ticks_since_ball_touch := 1 << 30 # large so the first touch always pays
|
||||
|
||||
|
||||
# Wire up references after the ship is spawned. `attack_goal` is the goal
|
||||
# this ship scores into (goal.team == opponent's team).
|
||||
@@ -97,10 +111,16 @@ func set_action(action) -> void:
|
||||
rl_controller.action.turbo = int(action["turbo"]) == 1
|
||||
|
||||
|
||||
func reset():
|
||||
super()
|
||||
_ticks_since_ball_touch = 1 << 30
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
super(delta)
|
||||
if not is_instance_valid(ship) or not is_instance_valid(ball):
|
||||
return
|
||||
_ticks_since_ball_touch += 1
|
||||
|
||||
# Dense shaping: own velocity toward the ball
|
||||
var to_ball := ball.global_position - ship.global_position
|
||||
@@ -113,6 +133,11 @@ func _physics_process(delta):
|
||||
if ball_distance_penalty > 0.0:
|
||||
reward -= ball_distance_penalty * to_ball.length() / MAX_BALL_DISTANCE
|
||||
|
||||
# Dense bonus: own speed, so hovering in place is never a rest state
|
||||
# (see speed_reward_weight).
|
||||
if speed_reward_weight > 0.0:
|
||||
reward += speed_reward_weight * ship.linear_velocity.length() / ship.max_speed
|
||||
|
||||
# Dense shaping: ball velocity toward the goal we attack
|
||||
var ball_to_goal := attack_goal_position - ball.global_position
|
||||
if ball_to_goal.length_squared() > 0.0001:
|
||||
@@ -149,5 +174,6 @@ func _wall_or_ceiling_contact() -> bool:
|
||||
|
||||
|
||||
func _on_ship_body_entered(body: Node) -> void:
|
||||
if body.is_in_group("ball"):
|
||||
if body.is_in_group("ball") and _ticks_since_ball_touch >= ball_touch_cooldown_ticks:
|
||||
reward += ball_touch_reward
|
||||
_ticks_since_ball_touch = 0
|
||||
|
||||
Reference in New Issue
Block a user