// Camera/Components/AC_Camera.ts import type { S_CameraSettings } from '#root/Camera/Structs/S_CameraSettings.ts'; import type { S_CameraState } from '#root/Camera/Structs/S_CameraState.ts'; import type { AC_InputDevice } from '#root/Input/Components/AC_InputDevice.ts'; import { ActorComponent } from '#root/UE/ActorComponent.ts'; import type { Float } from '#root/UE/Float.ts'; import { MathLibrary } from '#root/UE/MathLibrary.ts'; import { SystemLibrary } from '#root/UE/SystemLibrary.ts'; import { Vector } from '#root/UE/Vector.ts'; /** * Camera System Component * Deterministic camera control with smooth rotation and device-aware sensitivity * Provides precise control over camera behavior for consistent experience */ export class AC_Camera extends ActorComponent { // ════════════════════════════════════════════════════════════════════════════════════════ // FUNCTIONS // ════════════════════════════════════════════════════════════════════════════════════════ /** * Process look input and update camera rotation * @param InputDelta - Input delta (X = Yaw, Y = Pitch) * @param DeltaTime - Time since last frame * @category Input Processing */ public ProcessLookInput(InputDelta: Vector, DeltaTime: Float): void { if (this.IsInitialized) { const invertMultiplier = this.CameraSettings.InvertYAxis ? -1.0 : 1.0; let sensitivity: Float = 0; if (SystemLibrary.IsValid(this.InputDeviceComponent)) { sensitivity = this.InputDeviceComponent.IsGamepad() ? this.CameraSettings.GamepadSensitivity : this.CameraSettings.MouseSensitivity; } else { sensitivity = this.CameraSettings.MouseSensitivity; } const CalculateTargetPitch = ( targetPitch: Float, inputDeltaY: Float, deltaTime: Float ): Float => targetPitch - inputDeltaY * sensitivity * invertMultiplier * deltaTime; const CalculateTargetYaw = ( targetYaw: Float, inputDeltaX: Float, deltaTime: Float ): Float => targetYaw + inputDeltaX * sensitivity * deltaTime; this.CameraState = { CurrentPitch: this.CameraState.CurrentPitch, CurrentYaw: this.CameraState.CurrentYaw, TargetPitch: MathLibrary.ClampFloat( CalculateTargetPitch( this.CameraState.TargetPitch, InputDelta.Y, DeltaTime ), this.CameraSettings.PitchMin, this.CameraSettings.PitchMax ), TargetYaw: CalculateTargetYaw( this.CameraState.TargetYaw, InputDelta.X, DeltaTime ), LastInputDelta: InputDelta, InputMagnitude: MathLibrary.VectorLength(InputDelta), }; } } /** * Update camera rotation with smooth interpolation * @param DeltaTime - Time since last frame * @category Camera Updates */ public UpdateCameraRotation(DeltaTime: Float): void { if (this.IsInitialized) { if (this.CameraSettings.SmoothingSpeed > 0) { // Smooth interpolation to target rotation this.CameraState.CurrentPitch = MathLibrary.FInterpTo( this.CameraState.CurrentPitch, this.CameraState.TargetPitch, DeltaTime, this.CameraSettings.SmoothingSpeed ); this.CameraState.CurrentYaw = MathLibrary.FInterpTo( this.CameraState.CurrentYaw, this.CameraState.TargetYaw, DeltaTime, this.CameraSettings.SmoothingSpeed ); } else { // Instant rotation (no smoothing) this.CameraState.CurrentPitch = this.CameraState.TargetPitch; this.CameraState.CurrentYaw = this.CameraState.TargetYaw; } } } /** * Get current camera rotation for applying to SpringArm * @returns Current camera rotation values * @category Public Interface * @pure true */ public GetCameraRotation(): { Pitch: Float; Yaw: Float } { return { Pitch: this.CameraState.CurrentPitch, Yaw: this.CameraState.CurrentYaw, }; } /** * Check if camera is currently rotating * @returns True if there's active rotation input * @category State Queries * @pure true */ public IsCameraRotating(): boolean { return this.CameraState.InputMagnitude > 0.01; } /** * Initialize camera system with default settings * @category System Setup */ public InitializeCameraSystem(InputDeviceRef: AC_InputDevice): void { this.InputDeviceComponent = InputDeviceRef; this.IsInitialized = true; // Reset camera state this.CameraState = { CurrentPitch: 0.0, CurrentYaw: 0.0, TargetPitch: 0.0, TargetYaw: 0.0, LastInputDelta: new Vector(0, 0, 0), InputMagnitude: 0.0, }; } // ════════════════════════════════════════════════════════════════════════════════════════ // VARIABLES // ════════════════════════════════════════════════════════════════════════════════════════ /** * Camera configuration settings * Controls sensitivity, limits, and smoothing behavior * @category Camera Config * @instanceEditable true */ public readonly CameraSettings: S_CameraSettings = { MouseSensitivity: 100.0, GamepadSensitivity: 150.0, InvertYAxis: false, PitchMin: -89.0, PitchMax: 89.0, SmoothingSpeed: 20.0, }; /** * Current camera rotation state * Tracks current, target, and input data * @category Camera State */ private CameraState: S_CameraState = { CurrentPitch: 0.0, CurrentYaw: 0.0, TargetPitch: 0.0, TargetYaw: 0.0, LastInputDelta: new Vector(0, 0, 0), InputMagnitude: 0.0, }; /** * System initialization state flag * @category Camera State */ private IsInitialized: boolean = false; /** * Reference to input device component for device detection * Set externally, used for sensitivity adjustments * @category Components */ public InputDeviceComponent: AC_InputDevice | null = null; }