extends Camera3D class_name TrainCamera enum CameraMode { FOLLOW, SIDE, TOP, COCKPIT, CINEMATIC } @export_group("Camera") @export var follow_distance: float = 18.0 @export var follow_height: float = 7.0 @export var smoothness: float = 4.0 var train: Train var mode: CameraMode = CameraMode.FOLLOW var orbit_angle: float = 0.0 var cinematic_angle: float = 0.0 func _process(delta: float) -> void: if not train or not train.locomotive: return _handle_input(delta) match mode: CameraMode.FOLLOW: _update_follow(delta) CameraMode.SIDE: _update_side(delta) CameraMode.TOP: _update_top(delta) CameraMode.COCKPIT: _update_cockpit(delta) CameraMode.CINEMATIC: _update_cinematic(delta) func _handle_input(delta: float) -> void: if Input.is_action_just_pressed("ui_accept"): mode = ((mode + 1) % CameraMode.size()) as CameraMode orbit_angle = 0.0 if Input.is_action_pressed("camera_left"): orbit_angle -= 2.0 * delta elif Input.is_action_pressed("camera_right"): orbit_angle += 2.0 * delta orbit_angle = clamp(orbit_angle, -PI/2.5, PI/2.5) func _update_follow(delta: float) -> void: var loco = train.locomotive var train_pos = loco.global_position var forward = -loco.global_transform.basis.z var right = loco.global_transform.basis.x var rotated = forward * cos(orbit_angle) + right * sin(orbit_angle) var target_pos = train_pos - rotated * follow_distance + Vector3.UP * follow_height var target_look = train_pos + Vector3.UP * 2.0 global_position = global_position.lerp(target_pos, smoothness * delta) look_at(target_look, Vector3.UP) func _update_side(delta: float) -> void: var loco = train.locomotive var train_pos = loco.global_position var right = loco.global_transform.basis.x var side_dir = sign(orbit_angle) if abs(orbit_angle) > 0.1 else 1.0 var target_pos = train_pos + right * 12.0 * side_dir + Vector3.UP * 4.0 global_position = global_position.lerp(target_pos, smoothness * delta) look_at(train_pos + Vector3.UP * 1.5, Vector3.UP) func _update_top(delta: float) -> void: var train_pos = train.get_locomotive_position() var target_pos = train_pos + Vector3.UP * 35.0 global_position = global_position.lerp(target_pos, smoothness * delta) look_at(train_pos, Vector3.FORWARD) func _update_cockpit(delta: float) -> void: var loco = train.locomotive # Offset: dentro la cabina var cockpit_pos = loco.global_position + loco.global_transform.basis * Vector3(0, 3.2, 1.5) var forward_point = loco.global_position - (-loco.global_transform.basis.z) * 40.0 var look_offset = loco.global_transform.basis.x * orbit_angle * 15.0 global_position = global_position.lerp(cockpit_pos, smoothness * 2.0 * delta) look_at(forward_point + look_offset + Vector3.UP * 1.0, Vector3.UP) func _update_cinematic(delta: float) -> void: var train_pos = train.get_locomotive_position() cinematic_angle += 0.25 * delta var offset = Vector3( cos(cinematic_angle) * 22.0, 8.0 + sin(cinematic_angle * 0.5) * 4.0, sin(cinematic_angle) * 22.0 ) var target_pos = train_pos + offset global_position = global_position.lerp(target_pos, smoothness * 0.4 * delta) look_at(train_pos + Vector3.UP * 1.5, Vector3.UP) func get_mode_name() -> String: match mode: CameraMode.FOLLOW: return "Segue" CameraMode.SIDE: return "Laterale" CameraMode.TOP: return "Alto" CameraMode.COCKPIT: return "Prima persona" CameraMode.CINEMATIC: return "Cinematica" return "Sconosciuta"