mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
04691aaa48
Lands the non-networked Phase 0 tasks from multiplayer-todo.md (ship/camera/ arena refactors, sim constants, background FPS handling) plus a first pass at exposing graphics/performance settings (presets, resolution scaling, vsync, FPS cap, perf overlay) and a GPU profiling harness for the real-hardware follow-up in task 0.15b.
112 lines
4.0 KiB
GDScript
112 lines
4.0 KiB
GDScript
class_name Ball
|
|
extends RigidBody3D
|
|
|
|
# Physics ball. Floor gravity is untouched (gravity_scale in ball.tscn); this
|
|
# only adds the wall/ceiling "surface pull" (see ArenaBoundary.get_surface_pull)
|
|
# so the ball can cling near a wall for dribbling or hang against the ceiling
|
|
# for ceiling shots, plus a safety speed clamp (the ball previously had none).
|
|
|
|
@export_group("Surface Pull")
|
|
@export var wall_pull_strength = 4.0 # Weaker than the ship's — assist, not adherence
|
|
@export var wall_pull_range = 2.0
|
|
@export var ceiling_pull_strength = 5.5 # Stays below the ball's effective gravity (0.8 * 9.8)
|
|
@export var ceiling_pull_range = 2.0
|
|
|
|
# Kept close to ship_observations.gd's BALL_SPEED_SCALE (30.0) so this feature
|
|
# doesn't push ball velocity further out of the range trained policies expect.
|
|
const MAX_SPEED := 32.0
|
|
|
|
var _boundary: ArenaBoundary
|
|
var _trail: GPUParticles3D
|
|
|
|
var _pending_teleport: Transform3D
|
|
var _has_pending_teleport := false
|
|
|
|
|
|
# Queues an authoritative teleport, applied at the top of the next
|
|
# _integrate_forces — the only Jolt-safe place to write state.transform
|
|
# directly (see GameMode._reset_body / task 0.15) — instead of racing the
|
|
# physics step via set_deferred("global_transform", ...).
|
|
func queue_teleport(to: Transform3D) -> void:
|
|
_pending_teleport = to
|
|
_has_pending_teleport = true
|
|
|
|
|
|
# -1 = use the real linear_velocity (default; see _physics_process below).
|
|
# A frozen remote ball (Phase 4) holds zero velocity — Godot/Jolt zeroes and
|
|
# ignores velocity writes on frozen bodies — so the trail needs a
|
|
# presentation-only speed fed in from outside instead of reading physics
|
|
# state that will never reflect the ball's true remote motion.
|
|
var _visual_speed_override: float = -1.0
|
|
|
|
|
|
func set_visual_speed(speed: float) -> void:
|
|
_visual_speed_override = speed
|
|
|
|
|
|
func _ready() -> void:
|
|
_boundary = get_tree().get_first_node_in_group("arena_boundary")
|
|
if DisplayServer.get_name() == "headless":
|
|
# _integrate_forces remains active; only the render-side trail updater is
|
|
# disabled across the many parallel RL environments.
|
|
set_physics_process(false)
|
|
else:
|
|
_build_trail()
|
|
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
if _trail:
|
|
var speed := _visual_speed_override if _visual_speed_override >= 0.0 else linear_velocity.length()
|
|
var speed_ratio := clampf(speed / MAX_SPEED, 0.0, 1.0)
|
|
_trail.emitting = speed_ratio > 0.12
|
|
_trail.amount_ratio = smoothstep(0.12, 1.0, speed_ratio)
|
|
|
|
|
|
func _build_trail() -> void:
|
|
var mat := StandardMaterial3D.new()
|
|
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
mat.billboard_mode = BaseMaterial3D.BILLBOARD_ENABLED
|
|
mat.albedo_color = Color(0.55, 0.85, 1.0, 0.42)
|
|
mat.emission_enabled = true
|
|
mat.emission = Color(0.35, 0.72, 1.0)
|
|
mat.emission_energy_multiplier = 1.8
|
|
var quad := QuadMesh.new()
|
|
quad.size = Vector2(0.22, 0.22)
|
|
quad.material = mat
|
|
var process := ParticleProcessMaterial.new()
|
|
process.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE
|
|
process.emission_sphere_radius = 0.35
|
|
process.gravity = Vector3.ZERO
|
|
process.scale_min = 0.35
|
|
process.scale_max = 1.0
|
|
_trail = GPUParticles3D.new()
|
|
_trail.name = "BallTrail"
|
|
_trail.amount = 48
|
|
_trail.lifetime = 0.48
|
|
_trail.local_coords = false
|
|
_trail.process_material = process
|
|
_trail.draw_pass_1 = quad
|
|
_trail.visibility_aabb = AABB(Vector3(-18, -18, -18), Vector3(36, 36, 36))
|
|
_trail.emitting = false
|
|
add_child(_trail)
|
|
|
|
|
|
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
|
|
if _has_pending_teleport:
|
|
_has_pending_teleport = false
|
|
state.transform = _pending_teleport
|
|
state.linear_velocity = Vector3.ZERO
|
|
state.angular_velocity = Vector3.ZERO
|
|
reset_physics_interpolation()
|
|
|
|
if _boundary:
|
|
var pull := _boundary.get_surface_pull(
|
|
global_position, wall_pull_strength, wall_pull_range,
|
|
ceiling_pull_strength, ceiling_pull_range
|
|
)
|
|
state.apply_central_force(pull * mass)
|
|
|
|
if state.linear_velocity.length() > MAX_SPEED:
|
|
state.linear_velocity = state.linear_velocity.normalized() * MAX_SPEED
|