Files
millimeters-of-aluminum/scripts/orbital_body_2d.gd
olof.pettersson 6c383425f5 WIP Character
2025-10-12 11:11:14 +02:00

101 lines
3.5 KiB
GDScript

extends Node2D
class_name OrbitalBody2D
# Mass of this individual component
@export var base_mass: float = 1.0
var mass: float = 0.0 # Aggregated mass of this body and all its OrbitalBody2D children
var linear_velocity: Vector2 = Vector2.ZERO
var angular_velocity: float = 0.0
# Variables to accumulate forces applied during the current physics frame
var accumulated_force: Vector2 = Vector2.ZERO
var accumulated_torque: float = 0.0
# Placeholder for Moment of Inertia.
@export var inertia: float = 1.0
# Determine if this body is the root of a physics simulation
var is_sim_root: bool = true
func _ready():
# FIX 1: Ensure mass update runs immediately before the first _physics_process.
_update_mass_and_inertia()
# Check if a parent is also an OrbitalBody2D
var p = get_parent()
while p:
if p is OrbitalBody2D:
is_sim_root = false
break
p = p.get_parent()
# FIX: Enable _physics_process for ALL OrbitalBody2D nodes (including Thrusters).
# The 'if is_sim_root' inside _physics_process will prevent integration for children.
set_physics_process(Engine.is_editor_hint())
# --- PUBLIC FORCE APPLICATION METHODS ---
# This method is called by a component (like Thruster) at its global position.
func apply_force(force: Vector2, position: Vector2 = Vector2.ZERO):
# This is the force routing logic.
if is_sim_root:
# If we are the root, accumulate the force and calculate torque on the total body.
accumulated_force += force
# Calculate torque (2D cross product: T = r x F = r.x * F.y - r.y * F.x)
# 'r' is the vector from the center of mass (global_position) to the point of force application (position).
var r = position - global_position
var torque = r.x * force.y - r.y * force.x
accumulated_torque += torque
else:
# If we are not the root, we must route the force to the next OrbitalBody2D parent.
var p = get_parent()
while p:
if p is OrbitalBody2D:
# Recursively call the parent's apply_force method.
# This sends the force (and its original global position) up the chain.
p.apply_force(force, position)
return # Stop at the first OrbitalBody2D parent
p = p.get_parent()
push_error("OrbitalBody2D tried to apply force but could not find a parent OrbitalBody2D.")
func _update_mass_and_inertia():
mass = base_mass
for child in get_children():
if child is OrbitalBody2D:
child._update_mass_and_inertia() # Recurse into children
mass += child.mass
print("Node: %s, Mass: %f" % [self, mass])
func _physics_process(delta):
if is_sim_root and not Engine.is_editor_hint():
# FIX 2: Safety Check for Division by Zero
var sim_mass = mass
if sim_mass <= 0.0:
# If mass is zero, stop all physics to prevent NaN explosion.
accumulated_force = Vector2.ZERO
accumulated_torque = 0.0
return
# 1. Calculate and accumulate gravitational force (F_g)
var total_gravity_force = OrbitalMechanics.calculate_n_body_gravity_forces(self)
# 2. Total all forces: F_total = F_g + F_accumulated_from_thrusters
var total_force = total_gravity_force + accumulated_force
# 3. Apply Linear Physics (F = ma)
var linear_acceleration = total_force / sim_mass # Division is now safe
linear_velocity += linear_acceleration * delta
global_position += linear_velocity * delta
# 4. Apply Rotational Physics (T = I * angular_acceleration)
var angular_acceleration = accumulated_torque / inertia
angular_velocity += angular_acceleration * delta
rotation += angular_velocity * delta
# 5. Reset accumulated forces for the next frame
accumulated_force = Vector2.ZERO
accumulated_torque = 0.0