class_name OrbitalCamera extends Node3D const POSITION_ACCELERATION := 4.0 const BASIS_ACCELERATION := 4.0 const TARGETER_ACCELERATION := 4.0 @export var target: Node3D @export var offset := Vector3(0, 1, 0) @export var angular_speed := 0.007 var target_basis := Basis.IDENTITY @onready var pivot: Node3D = %Pivot @onready var camera: Camera3D = %Camera @onready var targeter: Node3D = %Targeter static var scene := preload("res://src/ui/camera/orbital_camera/orbital_camera.tscn") func _physics_process(delta: float) -> void: var up := Vector3.UP # If target is a game ball, realign to gravity if target is GameBall: up = -(target as GameBall).current_gravity.normalized() var right := up.cross(global_basis.z).normalized() var forward := right.cross(up).normalized() target_basis = Basis(right, up, forward) # Slerp to basis global_basis = global_basis.slerp(target_basis, delta * BASIS_ACCELERATION) # Update position global_position = global_position.lerp( target.global_position + offset, delta * POSITION_ACCELERATION ) # Update rotation #camera.look_at(target.global_position, up) targeter.look_at(target.global_position, up) camera.global_basis = camera.global_basis.slerp( targeter.global_basis.orthonormalized(), delta * TARGETER_ACCELERATION ) # Update phase angle pivot.rotation.y += angular_speed static func create(_target: Node3D) -> OrbitalCamera: var instance: OrbitalCamera = scene.instantiate() instance.target = _target return instance