Files
CosmicClash/Game/scripts/ship.gd
T
2025-07-13 21:01:57 +01:00

235 lines
7.7 KiB
GDScript

class_name Ship
extends RigidBody3D
@export_group("Movement")
@export var thrust_power = 150.0 # Main thruster power
@export var maneuvering_thrust = 75.0 # Side/vertical thruster power
@export var vertical_thrust = 120.0 # Up/down thruster power
@export var turbo_multiplier = 2.5 # Turbo boost multiplier
@export var max_speed = 25.0 # Maximum velocity
@export var rotation_power = 20.0 # Angular thrust power
@export var max_angular_speed = 3.0 # Maximum rotation speed
@export var drag_coefficient = 0.98 # Linear drag (air resistance)
@export var angular_drag = 0.95 # Rotational drag
@export_group("Camera")
var camera_distance = 8.0
var camera_height = 4.0
var camera_smoothing = 10.0
@onready var camera : Camera3D = get_node("Camera3D")
@onready var ball = get_parent().get_node("Ball")
@onready var timer_label = get_node("HUD/Control/TimerLabel")
var ball_cam_enabled = true
func _ready():
mass = 5
gravity_scale = 1.0
# Set custom inertia for better rotation
inertia = Vector3(1.0, 1.0, 1.0)
# Create and apply low-friction physics material
var ship_material = PhysicsMaterial.new()
ship_material.friction = 0.1 # Very low friction
ship_material.bounce = 0.2 # Slight bounce
physics_material_override = ship_material
# Make sure the RigidBody is completely free to move and rotate
freeze = false
lock_rotation = false
# Ensure all axes can rotate
axis_lock_angular_x = false
axis_lock_angular_y = false
axis_lock_angular_z = false
var game_scene = get_parent()
if game_scene and game_scene.has_signal("timer_updated"):
game_scene.timer_updated.connect(_on_timer_updated)
else:
print("Game scene not found or doesn't have timer_updated signal")
print("Ship physics configured - Mass: ", mass, " Gravity scale: ", gravity_scale, " Inertia: ", inertia)
print("Rotation locks - X:", axis_lock_angular_x, " Y:", axis_lock_angular_y, " Z:", axis_lock_angular_z)
func _input(event):
if event.is_action_pressed("ui_accept"): # Enter key
ball_cam_enabled = !ball_cam_enabled
func _on_timer_updated(minutes: int, seconds: int):
timer_label.text = "%02d:%02d" % [minutes, seconds]
func _physics_process(delta):
if camera:
_update_camera(delta)
func _update_camera(delta):
if ball_cam_enabled and ball:
_update_ball_cam(delta)
else:
_update_ship_cam(delta)
func _update_ball_cam(delta):
# In ball cam, camera positions itself so the ship is between camera and ball
var ship_pos = global_transform.origin
var ball_pos = ball.global_transform.origin
# Calculate direction from ball to ship
var ball_to_ship = (ship_pos - ball_pos).normalized()
# Position camera behind the ship relative to the ball's position
# This ensures the ship is always between the camera and ball
var camera_target_pos = ship_pos + ball_to_ship * camera_distance + Vector3.UP * camera_height
# Smoothly move camera to target position
camera.global_transform.origin = camera.global_transform.origin.lerp(camera_target_pos, camera_smoothing * delta)
# Make camera look at the ball
if camera.global_transform.origin.distance_to(ball_pos) > 0.1:
# Calculate direction to ball
var camera_pos = camera.global_transform.origin
var to_ball = (ball_pos - camera_pos).normalized()
# Create look-at transform manually
var camera_transform = Transform3D()
camera_transform.origin = camera_pos
camera_transform.basis = Basis.looking_at(to_ball, Vector3.UP)
# Apply the rotation smoothly
camera.global_transform.basis = camera.global_transform.basis.slerp(camera_transform.basis, camera_smoothing * delta)
func _update_ship_cam(delta):
# In ship cam, camera follows and looks in the same direction as the ship
var ship_pos = global_transform.origin
var ship_forward = -global_transform.basis.z
# Position camera behind and above the ship
var camera_target_pos = ship_pos - ship_forward * camera_distance + Vector3.UP * camera_height
# Smoothly move camera
camera.global_transform.origin = camera.global_transform.origin.lerp(camera_target_pos, camera_smoothing * delta)
# Make camera look in the same direction as the ship
var look_target = ship_pos + ship_forward * 10.0 # Look ahead of the ship
camera.look_at(look_target, Vector3.UP)
func _integrate_forces(state):
# Get thruster input
var thrust_input = get_thrust_input()
var rotation_input = get_rotation_input()
# === TRANSLATION (Movement) ===
apply_thruster_forces(state, thrust_input)
# === ROTATION (Turning) ===
apply_rotation_forces(state, rotation_input)
# === DRAG AND LIMITS ===
apply_drag_and_limits(state, rotation_input)
func get_thrust_input() -> Vector3:
var thrust = Vector3.ZERO
# Forward/Backward thrust (main engines)
if Input.is_action_pressed("move_forward"):
thrust.z += 1.0
if Input.is_action_pressed("move_back"):
thrust.z -= 1.0
# Strafe thrusters (left/right)
if Input.is_action_pressed("move_left"):
thrust.x -= 1.0
if Input.is_action_pressed("move_right"):
thrust.x += 1.0
# Vertical thrusters (up/down)
if Input.is_action_pressed("move_up"):
thrust.y += 1.0
if Input.is_action_pressed("move_down"):
thrust.y -= 1.0
return thrust
func get_rotation_input() -> Vector3:
var rotation = Vector3.ZERO
# Yaw (turn left/right around Y axis) - only use these if they exist
if Input.is_action_pressed("turn_left"):
rotation.y += 1.0
if Input.is_action_pressed("turn_right"):
rotation.y -= 1.0
# Pitch (nose up/down around X axis)
if Input.is_action_pressed("pitch_up"):
rotation.x -= 1.0
if Input.is_action_pressed("pitch_down"):
rotation.x += 1.0
# Roll (bank left/right around Z axis)
if Input.is_action_pressed("roll_left"):
rotation.z += 1.0
if Input.is_action_pressed("roll_right"):
rotation.z -= 1.0
return rotation
func apply_thruster_forces(state: PhysicsDirectBodyState3D, thrust_input: Vector3):
if thrust_input.length() < 0.01:
return
# Convert thrust input to world space forces based on ship orientation
var ship_basis = global_transform.basis
var world_thrust = Vector3.ZERO
# All thrusters should work relative to ship orientation
# Forward/backward thrust (main engines)
world_thrust += -ship_basis.z * thrust_input.z * thrust_power
# Strafe thrust (left/right maneuvering thrusters)
world_thrust += ship_basis.x * thrust_input.x * maneuvering_thrust
# Vertical thrust (up/down thrusters relative to ship orientation)
world_thrust += ship_basis.y * thrust_input.y * vertical_thrust
# Check for turbo
var is_turbo = Input.is_action_pressed("turbo") and thrust_input.z > 0
if is_turbo:
world_thrust *= turbo_multiplier
# Apply the force
state.apply_central_force(world_thrust)
func apply_rotation_forces(state: PhysicsDirectBodyState3D, rotation_input: Vector3):
if rotation_input.length() < 0.01:
return
# Apply torque for rotation - simple and effective
var torque = Vector3(
rotation_input.x * rotation_power, # Pitch
rotation_input.y * rotation_power, # Yaw
rotation_input.z * rotation_power # Roll
)
state.apply_torque(torque)
func apply_drag_and_limits(state: PhysicsDirectBodyState3D, rotation_input: Vector3):
# Linear drag (air resistance)
state.linear_velocity *= drag_coefficient
# Angular drag (rotational resistance) - more aggressive when not rotating
if rotation_input.length() < 0.01:
# More drag when not actively rotating to stop quicker
state.angular_velocity *= 0.9
else:
# Normal drag when actively rotating
state.angular_velocity *= angular_drag
# Limit maximum speeds
if state.linear_velocity.length() > max_speed:
state.linear_velocity = state.linear_velocity.normalized() * max_speed
if state.angular_velocity.length() > max_angular_speed:
state.angular_velocity = state.angular_velocity.normalized() * max_angular_speed