239 lines
8.3 KiB
TypeScript
239 lines
8.3 KiB
TypeScript
// 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_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.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,
|
|
DebugComponentRef: AC_DebugHUD
|
|
): void {
|
|
this.InputDeviceComponent = InputDeviceRef;
|
|
this.DebugHUDComponent = DebugComponentRef;
|
|
|
|
this.IsInitialized = true;
|
|
|
|
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
|
this.DebugHUDComponent.AddDebugPage(
|
|
this.DebugPageID,
|
|
'Camera System',
|
|
60
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update debug HUD with current camera info
|
|
* @category Debug
|
|
*/
|
|
public UpdateDebugPage(): void {
|
|
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
|
if (
|
|
this.DebugHUDComponent.ShouldUpdatePage(
|
|
this.DebugPageID,
|
|
SystemLibrary.GetGameTimeInSeconds()
|
|
)
|
|
) {
|
|
this.DebugHUDComponent.UpdatePageContent(
|
|
this.DebugPageID,
|
|
`Current Device: ${SystemLibrary.IsValid(this.InputDeviceComponent) ? this.InputDeviceComponent.GetCurrentInputDevice() : 'Input Device Component Not Found'}\n` +
|
|
`Sensitivity: ${SystemLibrary.IsValid(this.InputDeviceComponent) && this.InputDeviceComponent.IsGamepad() ? this.CameraSettings.GamepadSensitivity : this.CameraSettings.MouseSensitivity}\n` +
|
|
`Pitch: ${this.GetCameraRotation().Pitch}°\n` +
|
|
`Yaw: ${this.GetCameraRotation().Yaw}°\n` +
|
|
`Is Rotating: ${this.IsCameraRotating() ? 'Yes' : 'No'}\n` +
|
|
`Smoothing: ${this.CameraSettings.SmoothingSpeed}\n` +
|
|
`Invert Y: ${this.CameraSettings.InvertYAxis ? 'Yes' : 'No'}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
// 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;
|
|
|
|
/**
|
|
* Reference to debug HUD component for displaying camera info
|
|
* Optional, used for debugging purposes
|
|
* @category Components
|
|
*/
|
|
public DebugHUDComponent: AC_DebugHUD | null = null;
|
|
|
|
/**
|
|
* Debug page identifier for organizing debug output
|
|
* Used by debug HUD to categorize information
|
|
* @category Debug
|
|
*/
|
|
public readonly DebugPageID: string = 'CameraInfo';
|
|
}
|