119 lines
3.4 KiB
GDScript
119 lines
3.4 KiB
GDScript
extends Camera3D
|
|
class_name TrainJourneyCamera
|
|
|
|
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("change_camera"):
|
|
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"
|