Working calibration and thruster control

This commit is contained in:
2025-10-13 08:45:43 +02:00
parent 35d3110818
commit 5f30ab6c2f
12 changed files with 125 additions and 50 deletions

View File

@ -327,8 +327,8 @@ func _place_component_from_preview():
undo_redo.create_action("Place Component")
undo_redo.add_do_method(target_module, "attach_component", component_to_place, closest_point.position, closest_point.piece)
undo_redo.add_undo_method(target_module, "remove_child", component_to_place)
undo_redo.add_do_method(target_module, "_update_mass_and_inertia")
undo_redo.add_undo_method(target_module, "_update_mass_and_inertia")
undo_redo.add_do_method(target_module, "recalculate_physical_properties")
undo_redo.add_undo_method(target_module, "recalculate_physical_properties")
undo_redo.commit_action()
preview_node.global_position = closest_point.position

View File

@ -15,6 +15,8 @@
[node name="Module" type="Node2D"]
script = ExtResource("1_nqe0s")
mass = 13.0
inertia = 18.0
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="StructuralContainer" type="Node2D" parent="."]
@ -24,38 +26,49 @@ metadata/_custom_type_script = "uid://0isnsk356que"
[node name="AtmosphereVisualizer" type="Node2D" parent="."]
[node name="Hullplate" parent="." instance=ExtResource("2_foqop")]
mass = 1.0
[node name="@StaticBody2D@30634" parent="." instance=ExtResource("2_foqop")]
position = Vector2(0, 100)
mass = 1.0
[node name="@StaticBody2D@30635" parent="." instance=ExtResource("2_foqop")]
position = Vector2(0, -100)
mass = 1.0
[node name="Bulkhead" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(-50, 100)
mass = 1.0
[node name="@StaticBody2D@30636" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(-50, 0)
mass = 1.0
[node name="@StaticBody2D@30637" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(-50, -100)
mass = 1.0
[node name="@StaticBody2D@30638" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(50, -100)
mass = 1.0
[node name="@StaticBody2D@30639" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(0, -150)
rotation = 1.5708
mass = 1.0
[node name="@StaticBody2D@30640" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(0, 150)
rotation = 4.71239
mass = 1.0
[node name="@StaticBody2D@30641" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(50, 100)
mass = 1.0
[node name="@StaticBody2D@30642" parent="." instance=ExtResource("4_dmrms")]
position = Vector2(50, 0)
mass = 1.0
[node name="PilotBall" parent="." instance=ExtResource("4_i3kos")]
@ -63,6 +76,7 @@ position = Vector2(50, 0)
position = Vector2(1, -53)
panels = Array[ExtResource("6_oqcn4")]([ExtResource("7_vmx8o"), ExtResource("8_83bu1"), ExtResource("9_ixntg")])
installed_databanks = Array[ExtResource("6_ft4kn")]([ExtResource("7_dmrms")])
mass = 1.0
[node name="Thruster" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(-95, -130)
@ -84,5 +98,5 @@ position = Vector2(95, -130)
rotation = -1.5708
main_thruster = false
[node name="Thruster5" parent="." instance=ExtResource("12_vmx8o")]
[node name="MainEngine" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(0, 195)

View File

@ -66,7 +66,7 @@ func attach_component(component: Component, global_pos: Vector2, parent_piece: S
component.attached_piece = parent_piece
add_child(component)
component.owner = self
_update_mass_and_inertia()
recalculate_physical_properties()
# --- UPDATED: Logic now uses the helper function ---
func _recalculate_collision_shape():

View File

@ -14,6 +14,7 @@ enum AttachmentType { INTERIOR_WALL, EXTERIOR_HULL, FLOOR_OR_CEILING }
var attached_piece: StructuralPiece = null
func _ready():
super()
# OrbitalBody2D will handle mass initialization and physics setup.
pass

View File

@ -11,6 +11,7 @@ extends Component
@onready var rear_spring: DampedSpringJoint2D = $RearSpring
func _ready() -> void:
super()
# Configure the springs based on the exported variables
front_spring.stiffness = spring_stiffness
rear_spring.stiffness = spring_stiffness

View File

@ -9,6 +9,7 @@ class_name Ladder
# --- Inherited OrbitalBody2D & Component Setup ---
func _ready():
super()
# Set the base mass based on its material/size
base_mass = float(ladder_grid_height) * 25.0 # Example: 25kg per grid unit height

View File

@ -17,6 +17,7 @@ var occupant_panel_map: Dictionary = {}
# --- LIFECYCLE ---
func _ready():
super()
# --- FIX: Instantiate and initialize shards ONCE at startup ---
# The logic is now persistent and runs as long as the station exists.
var root_module = get_root_module()
@ -108,7 +109,7 @@ func _connect_internals(panel_instances: Array, shard_instances: Array):
# This logic remains the same, but it's now called with the relevant instances
# every time a user sits down.
var lever_panel
var helm_shard
var helm_shard: HelmLogicShard
var button_panel
for panel in panel_instances:
@ -128,6 +129,9 @@ func _connect_internals(panel_instances: Array, shard_instances: Array):
# You would do the same for the button panel, connecting its signals
# to a set_rotation_input or similar function on the helm_shard.
if helm_shard and button_panel:
button_panel.connect("button_1_pressed", func(): helm_shard.set_rotation_input(1.0))
button_panel.connect("button_2_pressed", func(): helm_shard.set_rotation_input(-1.0))
button_panel.connect("button_3_pressed", helm_shard.shutdown_rcs)
button_panel.connect("button_4_pressed", helm_shard.calibrate_rcs_performance)
# Helper to find the main ship/module this station belongs to

View File

@ -5,9 +5,6 @@ extends Component
@onready var pin_joint_a: PinJoint2D = $PinJointA
@onready var pin_joint_b: PinJoint2D = $PinJointB
# Get a reference to the parent ship.
@onready var ship: Spaceship = GameManager._find_parent_ship(self)
# Max force the thruster can produce (in scaled Newtons).
@export var max_thrust: float = 0.1
@ -26,23 +23,22 @@ var is_firing: bool = false
func _ready() -> void:
super()
# TODO: Figure out where this should go if anywhere
# --- Self-connecting logic ---
if ship and ship.get_path():
var ship_path = ship.get_path()
var self_path = get_path()
# --- Configure Pin Joint A ---
pin_joint_a.node_b = ship_path
# --- Configure Pin Joint B ---
pin_joint_b.node_b = ship_path
else:
print("Thruster Warning: 'Attach To Node' path is not set for ", self.name)
#if ship and ship.get_path():
#var ship_path = ship.get_path()
#var self_path = get_path()
#
## --- Configure Pin Joint A ---
#pin_joint_a.node_b = ship_path
#
## --- Configure Pin Joint B ---
#pin_joint_b.node_b = ship_path
#else:
#print("Thruster Warning: 'Attach To Node' path is not set for ", self.name)
# This thruster announces its existence to the whole scene tree.
add_to_group("ship_thrusters")
#self.body_entered.connect(_on_body_entered)
# This function calculates how much fuel is needed for a given thrust level and duration.
func calculate_fuel_consumption(thrust_force: float, delta_time: float) -> float:
@ -61,25 +57,25 @@ func _on_body_entered(body: Node) -> void:
# The controller calls this ONCE to activate the thruster.
func turn_on():
print("Thruster: Recieved Turn On Signal")
#print("Thruster: Recieved Turn On Signal")
if enabled:
is_firing = true
await get_tree().physics_frame
print(" - firing: %s" % is_firing)
#print(" - firing: %s" % is_firing)
# The controller calls this ONCE to deactivate the thruster.
func turn_off():
print("Thruster: Recieved Turn Off Signal")
#print("Thruster: Recieved Turn Off Signal")
is_firing = false
await get_tree().physics_frame
print(" - firing: %s" % is_firing)
#print(" - firing: %s" % is_firing)
# --- Godot Physics Callback ---
func _physics_process(_delta: float):
func _physics_process(delta: float):
super(delta)
if not enabled:
print("thruster not enabled")
is_firing = false
# If the thruster is active, apply a constant central force in its local "up" direction.

View File

@ -10,11 +10,11 @@ signal button_4_pressed
@onready var RCSPosBtn: Button = $RCSPos
@onready var RCSNegBtn: Button = $RCSNeg
@onready var MainEngineBtn: Button = $MainEngine
@onready var ShutdownBtn: Button = $RCSShutdown
@onready var CalibrateRcsBtn: Button = $CalibrateRCS
func _ready():
RCSPosBtn.pressed.connect(func(): emit_signal("button_1_pressed"))
RCSNegBtn.pressed.connect(func(): emit_signal("button_2_pressed"))
MainEngineBtn.pressed.connect(func(): emit_signal("button_3_pressed"))
ShutdownBtn.pressed.connect(func(): emit_signal("button_3_pressed"))
CalibrateRcsBtn.pressed.connect(func(): emit_signal("button_4_pressed"))

View File

@ -15,9 +15,9 @@ text = "RCS Pos"
layout_mode = 2
text = "RCS Neg"
[node name="MainEngine" type="Button" parent="."]
[node name="RCSShutdown" type="Button" parent="."]
layout_mode = 2
text = "Main"
text = "RCS Shutdown"
[node name="CalibrateRCS" type="Button" parent="."]
layout_mode = 2

View File

@ -76,16 +76,19 @@ func _perform_manual_hold():
# --- REFACTORED: This is the other key change ---
func apply_rotational_thrust(desired_torque: float):
print("Applying rotational thrust")
for thruster in thrusters:
if not thruster.main_thruster:
# Placeholder logic to determine which thrusters to fire
var thruster_torque_direction = 1.0 if thruster.position.x < 0 else -1.0
if sign(thruster_torque_direction) == sign(desired_torque):
thruster.turn_on()
else:
thruster.turn_off()
if not is_instance_valid(root_module):
return
# Iterate through all available RCS thrusters that have been calibrated
for thruster in thruster_data_map:
var thruster_data: ThrusterData = thruster_data_map[thruster]
# If this thruster can help apply the desired torque, turn it on.
# Otherwise, explicitly turn it off to ensure it's not firing incorrectly.
if sign(thruster_data.measured_torque) == sign(desired_torque) and desired_torque != 0:
thruster.turn_on()
else:
thruster.turn_off()
func shutdown_rcs():
for thruster in thrusters:

View File

@ -3,9 +3,9 @@ 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
@export var mass: float = 0.0 # Aggregated mass of this body and all its OrbitalBody2D children
@export var linear_velocity: Vector2 = Vector2.ZERO
@export var angular_velocity: float = 0.0
# Variables to accumulate forces applied during the current physics frame
var accumulated_force: Vector2 = Vector2.ZERO
@ -18,8 +18,6 @@ var accumulated_torque: float = 0.0
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()
@ -29,16 +27,18 @@ func _ready():
break
p = p.get_parent()
# Ensure mass update runs immediately before the first _physics_process.
recalculate_physical_properties()
# 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())
#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.
# 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)
@ -47,7 +47,7 @@ func apply_force(force: Vector2, position: Vector2 = Vector2.ZERO):
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.
# 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:
@ -98,3 +98,58 @@ func _physics_process(delta):
# 5. Reset accumulated forces for the next frame
accumulated_force = Vector2.ZERO
accumulated_torque = 0.0
# This is the new, corrected function.
func recalculate_physical_properties():
if not is_sim_root:
return
var all_parts: Array[OrbitalBody2D] = []
_collect_all_parts(all_parts)
if all_parts.is_empty():
mass = base_mass
inertia = 1.0
return
# --- Step 1: Calculate Total Mass and LOCAL Center of Mass ---
var total_mass = 0.0
var weighted_local_pos_sum = Vector2.ZERO
for part in all_parts:
total_mass += part.base_mass
# We get the part's position *relative to the root module*
var local_pos = part.global_position - self.global_position
weighted_local_pos_sum += local_pos * part.base_mass
var local_center_of_mass = Vector2.ZERO
if total_mass > 0:
local_center_of_mass = weighted_local_pos_sum / total_mass
# --- Step 2: Calculate Total Moment of Inertia around the LOCAL CoM ---
var total_inertia = 0.0
for part in all_parts:
# Get the part's position relative to the root module again
var local_pos = part.global_position - self.global_position
# The radius is the distance from the part's local position to the ship's local center of mass
var r_squared = (local_pos - local_center_of_mass).length_squared()
total_inertia += part.base_mass * r_squared
# --- Step 3: Assign the final values ---
self.mass = total_mass
# We apply a scaling factor here because our "units" are pixels.
# This brings the final value into a range that feels good for gameplay.
# You can tune this factor to make ships feel heavier or lighter.
self.inertia = total_inertia * 0.01
print("Physics Recalculated: Mass=%.2f kg, Inertia=%.2f" % [mass, inertia])
# A recursive helper function to get an array of all OrbitalBody2D children.
func _collect_all_parts(parts_array: Array):
# Add self to the list
parts_array.append(self)
# Recurse into children
# TODO: this assumes that all OrbitalBody2D that are attached are done in a clean chain without breaks, which may not be the case
for child in get_children():
if child is OrbitalBody2D:
child._collect_all_parts(parts_array)