// Movement/AC_Movement.ts import type { AC_DebugHUD } from '#root/Debug/Components/AC_DebugHUD.ts'; import { BFL_MovementProcessor } from '#root/Movement/Core/BFL_MovementProcessor.ts'; import type { DA_MovementConfig } from '#root/Movement/Core/DA_MovementConfig.ts'; import { DA_MovementConfigDefault } from '#root/Movement/Core/DA_MovementConfigDefault.ts'; import { E_MovementState } from '#root/Movement/Core/E_MovementState.ts'; import type { S_MovementState } from '#root/Movement/Core/S_MovementState.ts'; import { E_SurfaceType } from '#root/Movement/Surface/E_SurfaceType.ts'; import { S_AngleThresholds } from '#root/Movement/Surface/S_AngleThresholds.ts'; import { ActorComponent } from '#root/UE/ActorComponent.ts'; import type { CapsuleComponent } from '#root/UE/CapsuleComponent.ts'; import type { Float } from '#root/UE/Float.ts'; import { HitResult } from '#root/UE/HitResult.ts'; import { MathLibrary } from '#root/UE/MathLibrary.ts'; import { Rotator } from '#root/UE/Rotator.ts'; import { StringLibrary } from '#root/UE/StringLibrary.ts'; import { SystemLibrary } from '#root/UE/SystemLibrary.ts'; import { Vector } from '#root/UE/Vector.ts'; /** * Movement System Component * Core deterministic movement system for 3D platformer * Handles surface classification and movement physics calculations */ export class AC_Movement extends ActorComponent { // ════════════════════════════════════════════════════════════════════════════════════════ // FUNCTIONS // ════════════════════════════════════════════════════════════════════════════════════════ // ──────────────────────────────────────────────────────────────────────────────────────── // Debug // ──────────────────────────────────────────────────────────────────────────────────────── /** * Update debug HUD with current movement info * @category Debug */ public UpdateDebugPage(): void { if (SystemLibrary.IsValid(this.DebugHUDComponent)) { if ( this.DebugHUDComponent.ShouldUpdatePage( this.DebugPageID, SystemLibrary.GetGameTimeInSeconds() ) ) { this.DebugHUDComponent.UpdatePageContent( this.DebugPageID, // Constants `Max Speed: ${this.Config.MaxSpeed}\n` + `Acceleration: ${this.Config.Acceleration}\n` + `Friction: ${this.Config.Friction}\n` + `Gravity: ${this.Config.Gravity}\n` + `Initialized: ${this.IsInitialized}\n` + `\n` + // Current State `Current Velocity: ${StringLibrary.ConvVectorToString(this.CurrentMovementState.Velocity)}\n` + `Speed: ${this.CurrentMovementState.Speed}\n` + `Is Grounded: ${this.CurrentMovementState.IsGrounded}\n` + `Surface Type: ${this.CurrentMovementState.SurfaceType}\n` + `Movement State: ${this.CurrentMovementState.MovementState}\n` + `Input Magnitude: ${this.CurrentMovementState.InputMagnitude}` + `\n` + // Rotation `Current Yaw: ${this.CurrentMovementState.Rotation.yaw}\n` + `Rotation Delta: ${this.CurrentMovementState.RotationDelta}\n°` + `Is Rotating: ${this.CurrentMovementState.IsRotating}\n` + `Rotation Speed: ${this.Config.RotationSpeed}\n°` + `Min Speed: ${this.Config.MinSpeedForRotation}` + `\n` + // Position `Location: ${StringLibrary.ConvVectorToString(this.GetOwner().GetActorLocation())}` + `\n` + // Sweep Collision `Collision Checks: ${this.CurrentMovementState.CollisionCount}/${this.Config.MaxCollisionChecks}\n` + `Sweep Blocked: ${this.CurrentMovementState.IsBlocked}\n` + `Ground Distance: ${this.Config.GroundTraceDistance} cm` ); } } } // ──────────────────────────────────────────────────────────────────────────────────────── // Default // ──────────────────────────────────────────────────────────────────────────────────────── /** * Process movement input from player controller * Normalizes input and calculates target velocity * @param InputVector - Raw input from WASD/gamepad stick * @param DeltaTime - Time since last frame for frame-rate independence * @category Default */ public ProcessMovementInput(InputVector: Vector, DeltaTime: Float): void { if (this.IsInitialized) { this.CurrentMovementState = BFL_MovementProcessor.ProcessMovement( this.CurrentMovementState, { InputVector, DeltaTime, CapsuleComponent: this.CapsuleComponent, Config: this.Config, AngleThresholdsRads: this.AngleThresholdsRads, }, SystemLibrary.IsValid(this.DebugHUDComponent) ? this.DebugHUDComponent.ShowVisualDebug : false ); this.GetOwner().SetActorLocation(this.CurrentMovementState.Location); this.GetOwner().SetActorRotation(this.CurrentMovementState.Rotation); } } /** * Initialize movement system with angle conversion * Converts degree thresholds to radians for runtime performance * @category Default */ public InitializeMovementSystem( CapsuleComponentRef: CapsuleComponent | null = null, DebugHUDComponentRef: AC_DebugHUD | null = null ): void { this.CapsuleComponent = CapsuleComponentRef; this.DebugHUDComponent = DebugHUDComponentRef; this.IsInitialized = true; this.AngleThresholdsRads = { Walkable: MathLibrary.DegreesToRadians( this.Config.AngleThresholdsDegrees.Walkable ), SteepSlope: MathLibrary.DegreesToRadians( this.Config.AngleThresholdsDegrees.SteepSlope ), Wall: MathLibrary.DegreesToRadians( this.Config.AngleThresholdsDegrees.Wall ), }; this.CurrentMovementState = BFL_MovementProcessor.CreateInitialState( this.GetOwner().GetActorLocation(), this.GetOwner().GetActorRotation() ); if (SystemLibrary.IsValid(this.DebugHUDComponent)) { this.DebugHUDComponent.AddDebugPage( this.DebugPageID, 'Movement Info', 60 ); } } // ════════════════════════════════════════════════════════════════════════════════════════ // VARIABLES // ════════════════════════════════════════════════════════════════════════════════════════ // ──────────────────────────────────────────────────────────────────────────────────────── // Components // ──────────────────────────────────────────────────────────────────────────────────────── /** * Reference to debug HUD component for displaying camera info * Optional, used for debugging purposes * @category Components */ private DebugHUDComponent: AC_DebugHUD | null = null; /** * Reference to character's capsule component for collision detection * @category Components */ private CapsuleComponent: CapsuleComponent | null = null; // ──────────────────────────────────────────────────────────────────────────────────────── // Default // ──────────────────────────────────────────────────────────────────────────────────────── /** * Default movement state * @category Default */ private CurrentMovementState: S_MovementState = { Location: new Vector(0, 0, 0), Rotation: new Rotator(0, 0, 0), Velocity: new Vector(0, 0, 0), Speed: 0.0, IsGrounded: false, GroundHit: new HitResult(), SurfaceType: E_SurfaceType.None, IsBlocked: false, CollisionCount: 0, IsRotating: false, RotationDelta: 0.0, MovementState: E_MovementState.Idle, InputMagnitude: 0.0, }; /** * Default movement configuration * @category Default * @instanceEditable true */ private readonly Config: DA_MovementConfig = new DA_MovementConfigDefault(); /** * Runtime cached angle thresholds in radians * Converted from degrees during initialization for performance * @category Default */ private AngleThresholdsRads: S_AngleThresholds = { Walkable: 0.0, SteepSlope: 0.0, Wall: 0.0, }; /** * Debug page identifier for organizing debug output * Used by debug HUD to categorize information * @category Default * @instanceEditable true */ private readonly DebugPageID: string = 'MovementInfo'; /** * Flag indicating if movement system has been initialized * Ensures angle thresholds are converted before use * @category Debug */ private IsInitialized = false; }