# Movement System - Technical Documentation (Stage 9 Refactored) ## Обзор Детерминированная система движения для 3D-платформера, построенная на принципе **Functional Core, Imperative Shell**. Система обеспечивает математически предсказуемое поведение через композицию чистых функциональных модулей, с точной классификацией поверхностей, swept collision detection и ground snapping. **Версия:** Stage 9 (Post-Refactoring) **Архитектурный паттерн:** Pipeline Processor + Pure Function Libraries **Статус:** Production Ready ✅ --- ## Архитектурные принципы ### Core Design Philosophy 1. **Functional Core, Imperative Shell** - Pure business logic в BFL_* модулях - Imperative framework integration в AC_Movement 2. **Immutable State Transformation** - `S_MovementState → ProcessMovement() → S_MovementState` - Никогда не мутируем входящее состояние 3. **Pipeline Processing** - Четкие последовательные фазы обработки - Каждая фаза читает результаты предыдущей 4. **Separation of Concerns** - AC_Movement: координация и framework integration - BFL_MovementProcessor: бизнес-логика движения - BFL_*: специализированные подсистемы 5. **Data-Oriented Design** - Явные структуры данных (S_MovementState, S_MovementInput) - Функции оперируют данными, не прячут их --- ## Компоненты системы ### 1. AC_Movement (Component - Imperative Shell) **Роль:** Thin orchestration layer между UE Actor system и функциональной логикой **Ответственности:** - Lifecycle management (инициализация, cleanup) - Component references (CapsuleComponent, DebugHUD) - State storage (CurrentMovementState) - Framework integration (SetActorLocation, SetActorRotation) - Debug visualization coordination **Ключевые методы:** ```typescript // Инициализация системы InitializeMovementSystem( CapsuleComponentRef: CapsuleComponent | null, DebugHUDComponentRef: AC_DebugHUD | null ): void // Обработка движения (главная точка входа) ProcessMovementInput( InputVector: Vector, DeltaTime: Float ): void // Debug UpdateDebugPage(): void ``` **Приватные поля:** ```typescript CurrentMovementState: S_MovementState // Текущее состояние Config: DA_MovementConfig // Конфигурация AngleThresholdsRads: S_AngleThresholds // Кэшированные пороги в радианах CapsuleComponent: CapsuleComponent | null // Ссылка на капсулу DebugHUDComponent: AC_DebugHUD | null // Ссылка на debug HUD IsInitialized: boolean // Флаг инициализации ``` **Размер:** ~230 LOC (↓ 62% от original) --- ### 2. BFL_MovementProcessor (Core - Functional Heart) **Роль:** Unified movement processing pipeline - центральная точка всей логики движения **Ответственности:** - Orchestration всех подсистем в правильном порядке - State transformation (CurrentState + Input → NextState) - Phase sequencing и data flow - Integration между subsystems **Главный метод:** ```typescript ProcessMovement( CurrentState: S_MovementState, Input: S_MovementInput, IsShowVisualDebug: boolean = false ): S_MovementState ``` **Processing Pipeline (6 фаз):** ```typescript // PHASE 1: INPUT & ROTATION ├─ Calculate input magnitude └─ Update character rotation (BFL_RotationController) // PHASE 2: GROUND DETECTION ├─ Check ground with trace (BFL_GroundProbe) ├─ Determine IsGrounded └─ Classify surface type (BFL_SurfaceClassifier) // PHASE 3: PHYSICS CALCULATION ├─ Calculate ground velocity (BFL_Kinematics) [if grounded] │ OR Apply air friction (BFL_Kinematics) [if airborne] ├─ Apply gravity (BFL_Kinematics) └─ Calculate horizontal speed // PHASE 4: MOVEMENT APPLICATION (Sweep) ├─ Convert velocity to delta ├─ Perform swept collision (BFL_CollisionResolver) └─ Calculate slide vector if blocked // PHASE 5: GROUND SNAPPING └─ Snap to ground if conditions met (BFL_GroundProbe) // PHASE 6: STATE DETERMINATION └─ Determine movement state (BFL_MovementStateMachine) // RETURN: Complete new S_MovementState ``` **Вспомогательные методы:** ```typescript CreateInitialState( Location: Vector, Rotation: Rotator ): S_MovementState ``` **Purity:** Impure (из-за collision traces), но deterministic **Размер:** ~260 LOC --- ### 3. BFL_Kinematics (Physics Library) **Роль:** Pure physics calculations для движения **Ключевые методы:** ```typescript // Ground movement с acceleration CalculateGroundVelocity( CurrentVelocity: Vector, InputVector: Vector, DeltaTime: Float, Config: DA_MovementConfig ): Vector // Friction (deceleration) CalculateFriction( CurrentVelocity: Vector, DeltaTime: Float, Config: DA_MovementConfig ): Vector // Gravity application CalculateGravity( CurrentVelocity: Vector, IsGrounded: boolean, Config: DA_MovementConfig ): Vector // Horizontal speed query GetHorizontalSpeed(Velocity: Vector): Float ``` **Характеристики:** - ✅ Pure functions - ✅ VInterpTo для smooth movement - ✅ Frame-rate independent (uses DeltaTime) --- ### 4. BFL_CollisionResolver (Collision Library) **Роль:** Swept collision detection и surface sliding **Ключевые методы:** ```typescript // Главный swept trace PerformSweep( StartLocation: Vector, DesiredDelta: Vector, CapsuleComponent: CapsuleComponent | null, Config: DA_MovementConfig, DeltaTime: Float, IsShowVisualDebug: boolean ): S_SweepResult // Surface sliding projection ProjectOntoSurface( MovementDelta: Vector, SurfaceNormal: Vector ): Vector // Slide vector calculation CalculateSlideVector( SweepResult: S_SweepResult, OriginalDelta: Vector, StartLocation: Vector ): Vector // Adaptive step size для swept trace CalculateStepSize( Velocity: Vector, DeltaTime: Float, Config: DA_MovementConfig ): Float ``` **Характеристики:** - ⚠️ Impure (world traces) - ✅ Deterministic stepping - ✅ Tunneling protection - ✅ Adaptive precision --- ### 5. BFL_GroundProbe (Ground Detection Library) **Роль:** Ground detection, snapping, surface queries **Ключевые методы:** ```typescript // Ground detection trace CheckGround( CharacterLocation: Vector, CapsuleComponent: CapsuleComponent | null, AngleThresholdsRads: S_AngleThresholds, Config: DA_MovementConfig, IsShowVisualDebug: boolean ): HitResult // Ground snapping calculation CalculateSnapLocation( CurrentLocation: Vector, GroundHit: HitResult, CapsuleComponent: CapsuleComponent | null, SnapThreshold: Float ): Vector // Snapping condition check ShouldSnapToGround( CurrentVelocityZ: Float, GroundHit: HitResult, IsGrounded: boolean ): boolean // Surface type query GetSurfaceType( GroundHit: HitResult, AngleThresholdsRads: S_AngleThresholds ): E_SurfaceType ``` **Характеристики:** - ⚠️ Impure (LineTraceByChannel) - ✅ Separate snapping logic - ✅ Clear condition checking --- ### 6. BFL_RotationController (Rotation Library) **Роль:** Character rotation toward movement direction **Ключевые методы:** ```typescript // Calculate target yaw from direction CalculateTargetYaw(MovementDirection: Vector): Float // Calculate full target rotation CalculateTargetRotation(MovementDirection: Vector): Rotator // Smooth rotation interpolation InterpolateRotation( CurrentRotation: Rotator, TargetRotation: Rotator, RotationSpeed: Float, DeltaTime: Float, MinSpeedForRotation: Float, CurrentSpeed: Float ): S_RotationResult // Convenience method UpdateRotation( CurrentRotation: Rotator, MovementDirection: Vector, Config: DA_MovementConfig, DeltaTime: Float, CurrentSpeed: Float ): S_RotationResult ``` **Характеристики:** - ✅ Pure functions - ✅ Wraparound handling (180°/-180°) - ✅ Min speed threshold --- ### 7. BFL_MovementStateMachine (State Machine) **Роль:** Determine movement state from context **Ключевые методы:** ```typescript // Main state determination DetermineState(Context: S_MovementContext): E_MovementState // Internal helpers private DetermineAirborneState(Context: S_MovementContext): E_MovementState private DetermineGroundedState(Context: S_MovementContext): E_MovementState ``` **State Priority Logic:** ``` 1. IsGrounded? ├─ Yes: Check surface type │ ├─ SteepSlope → Sliding │ ├─ Wall/Ceiling → Blocked │ └─ Walkable → Check input │ ├─ Has input & speed > 1.0 → Walking │ └─ Else → Idle └─ No → Airborne ``` **Характеристики:** - ✅ Pure FSM logic - ✅ Priority-based transitions - ✅ Clear state rules --- ### 8. BFL_SurfaceClassifier (Surface Classification) **Роль:** Classify surface based on normal angle **Ключевые методы:** ```typescript // Main classification Classify( SurfaceNormal: Vector, AngleThresholdsRads: S_AngleThresholds ): E_SurfaceType // Type checking helpers IsWalkable(surfaceType: E_SurfaceType): boolean IsSteep(surfaceType: E_SurfaceType): boolean IsWall(surfaceType: E_SurfaceType): boolean IsCeiling(surfaceType: E_SurfaceType): boolean IsNone(surfaceType: E_SurfaceType): boolean ``` **Classification Rules:** ``` Surface Angle → Type ───────────────────── ≤ Walkable → Walkable (0°-50°) ≤ SteepSlope → SteepSlope (50°-85°) ≤ Wall → Wall (85°-95°) > Wall → Ceiling (95°-180°) ``` **Характеристики:** - ✅ Pure functions - ✅ Angle-based classification - ✅ Type-safe queries --- ## Структуры данных ### S_MovementState (Complete State Snapshot) **Роль:** Immutable snapshot всего состояния движения ```typescript interface S_MovementState { // ═══════════════════════════════════════════════════════ // TRANSFORM // ═══════════════════════════════════════════════════════ Location: Vector // World location Rotation: Rotator // Yaw rotation // ═══════════════════════════════════════════════════════ // VELOCITY & PHYSICS // ═══════════════════════════════════════════════════════ Velocity: Vector // Current velocity (cm/s) Speed: Float // Horizontal speed (cm/s) // ═══════════════════════════════════════════════════════ // GROUND STATE // ═══════════════════════════════════════════════════════ IsGrounded: boolean // On walkable ground? GroundHit: HitResult // Ground trace result SurfaceType: E_SurfaceType // Current surface classification // ═══════════════════════════════════════════════════════ // COLLISION STATE // ═══════════════════════════════════════════════════════ IsBlocked: boolean // Blocked by collision? CollisionCount: number // Collision checks this frame // ═══════════════════════════════════════════════════════ // ROTATION STATE // ═══════════════════════════════════════════════════════ IsRotating: boolean // Currently rotating? RotationDelta: Float // Remaining angular distance (degrees) // ═══════════════════════════════════════════════════════ // MOVEMENT STATE // ═══════════════════════════════════════════════════════ MovementState: E_MovementState // Current FSM state InputMagnitude: Float // Input magnitude (0-1) } ``` **Usage Pattern:** ```typescript // Immutable transformation const newState = BFL_MovementProcessor.ProcessMovement( currentState, // Never modified input, debugFlag ); // Apply to actor this.GetOwner().SetActorLocation(newState.Location); this.GetOwner().SetActorRotation(newState.Rotation); // Store for next frame this.CurrentMovementState = newState; ``` --- ### S_MovementInput (Input Encapsulation) **Роль:** All data needed для movement processing ```typescript interface S_MovementInput { InputVector: Vector // Player input (normalized XY) DeltaTime: Float // Frame delta time (seconds) CapsuleComponent: CapsuleComponent | null // Collision capsule Config: DA_MovementConfig // Movement config AngleThresholdsRads: S_AngleThresholds // Surface thresholds (radians) } ``` **Usage:** ```typescript const input: S_MovementInput = { InputVector: playerInput, DeltaTime: deltaTime, CapsuleComponent: this.CapsuleComponent, Config: this.Config, AngleThresholdsRads: this.AngleThresholdsRads }; const newState = BFL_MovementProcessor.ProcessMovement( this.CurrentMovementState, input, this.DebugHUDComponent?.ShowVisualDebug ?? false ); ``` --- ### S_MovementContext (State Machine Input) **Роль:** Context для state determination ```typescript interface S_MovementContext { IsGrounded: boolean // On walkable ground? SurfaceType: E_SurfaceType // Surface classification InputMagnitude: Float // Input strength (0-1) CurrentSpeed: Float // Horizontal speed (cm/s) VerticalVelocity: Float // Z velocity (cm/s) IsBlocked: boolean // Blocked by collision? } ``` --- ### DA_MovementConfig (Configuration Asset) **Роль:** Centralized movement constants ```typescript class DA_MovementConfig extends PrimaryDataAsset { // ═══════════════════════════════════════════════════════ // MOVEMENT PHYSICS // ═══════════════════════════════════════════════════════ readonly MaxSpeed: Float = 800.0 // Max horizontal speed (cm/s) readonly Acceleration: Float = 10.0 // VInterpTo acceleration rate readonly Friction: Float = 8.0 // VInterpTo friction rate readonly Gravity: Float = 980.0 // Gravity (cm/s²) // ═══════════════════════════════════════════════════════ // SURFACE DETECTION // ═══════════════════════════════════════════════════════ readonly AngleThresholdsDegrees: S_AngleThresholds = { Walkable: 50.0, // ≤50° = walkable SteepSlope: 85.0, // ≤85° = steep slope Wall: 95.0 // ≤95° = wall } // ═══════════════════════════════════════════════════════ // COLLISION SETTINGS // ═══════════════════════════════════════════════════════ readonly GroundTraceDistance: Float = 50.0 // Ground detection distance readonly MinStepSize: Float = 1.0 // Min sweep step size readonly MaxStepSize: Float = 50.0 // Max sweep step size readonly MaxCollisionChecks: Float = 25 // Max checks per frame // ═══════════════════════════════════════════════════════ // CHARACTER ROTATION // ═══════════════════════════════════════════════════════ RotationSpeed: Float = 360.0 // Rotation speed (deg/s) MinSpeedForRotation: Float = 50.0 // Min speed to rotate ShouldRotateToMovement: boolean = true // Enable rotation } ``` --- ### Enums ```typescript // Movement FSM states enum E_MovementState { Idle = 'Idle', // Stationary on ground Walking = 'Walking', // Moving on ground Airborne = 'Airborne', // In the air Sliding = 'Sliding', // Sliding on steep slope Blocked = 'Blocked' // Blocked by collision } // Surface classification enum E_SurfaceType { None = 'None', // No ground contact Walkable = 'Walkable', // Normal walking ≤50° SteepSlope = 'SteepSlope', // Sliding 50°-85° Wall = 'Wall', // Collision 85°-95° Ceiling = 'Ceiling' // Overhead >95° } ``` --- ## Data Flow Diagram ``` ┌──────────────────────────────────────────────────────────────┐ │ AC_Movement │ │ (Imperative Shell) │ ├──────────────────────────────────────────────────────────────┤ │ │ │ ProcessMovementInput(InputVector, DeltaTime) │ │ │ │ │ ▼ │ │ ┌────────────────────────┐ │ │ │ Prepare S_MovementInput│ │ │ └────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────┐ │ │ │ BFL_MovementProcessor │ │ │ │ .ProcessMovement() │ │ │ │ │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ PHASE 1: Input & Rotation │ │ │ │ │ │ • Calculate input magnitude │ │ │ │ │ │ • BFL_RotationController │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────▼───────────────────┐ │ │ │ │ │ PHASE 2: Ground Detection │ │ │ │ │ │ • BFL_GroundProbe │ │ │ │ │ │ • BFL_SurfaceClassifier │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────▼───────────────────┐ │ │ │ │ │ PHASE 3: Physics │ │ │ │ │ │ • BFL_Kinematics │ │ │ │ │ │ - Ground velocity / Friction│ │ │ │ │ │ - Gravity │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────▼───────────────────┐ │ │ │ │ │ PHASE 4: Movement (Sweep) │ │ │ │ │ │ • BFL_CollisionResolver │ │ │ │ │ │ - Perform sweep │ │ │ │ │ │ - Calculate slide │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────▼───────────────────┐ │ │ │ │ │ PHASE 5: Ground Snapping │ │ │ │ │ │ • BFL_GroundProbe │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────▼───────────────────┐ │ │ │ │ │ PHASE 6: State Determination│ │ │ │ │ │ • BFL_MovementStateMachine │ │ │ │ │ └─────────────────────────────┘ │ │ │ │ │ │ │ │ │ ┌─────────▼───────────────────┐ │ │ │ │ │ Return S_MovementState │ │ │ │ │ └─────────────────────────────┘ │ │ │ └─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌────────────────────────┐ │ │ │ Apply to Actor │ │ │ │ • SetActorLocation() │ │ │ │ • SetActorRotation() │ │ │ └────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘ ``` --- ## Processing Pipeline Details ### Phase 1: Input & Rotation ```typescript // Calculate input strength const inputMagnitude = VectorLength(InputVector); // Update rotation const rotationResult = BFL_RotationController.UpdateRotation( CurrentState.Rotation, InputVector, Config, DeltaTime, CurrentState.Speed ); ``` **Output:** `rotationResult.Rotation`, `rotationResult.IsRotating` --- ### Phase 2: Ground Detection ```typescript // Perform ground trace const groundHit = BFL_GroundProbe.CheckGround( CurrentState.Location, CapsuleComponent, AngleThresholdsRads, Config ); // Determine ground state const isGrounded = groundHit.BlockingHit; // Classify surface const surfaceType = BFL_GroundProbe.GetSurfaceType( groundHit, AngleThresholdsRads ); ``` **Output:** `groundHit`, `isGrounded`, `surfaceType` --- ### Phase 3: Physics Calculation ```typescript let newVelocity = CurrentState.Velocity; // Ground movement OR air friction if (IsWalkable(surfaceType) && isGrounded) { newVelocity = BFL_Kinematics.CalculateGroundVelocity( newVelocity, InputVector, DeltaTime, Config ); } else { newVelocity = BFL_Kinematics.CalculateFriction( newVelocity, DeltaTime, Config ); } // Apply gravity newVelocity = BFL_Kinematics.CalculateGravity( newVelocity, isGrounded, Config ); // Calculate speed const newSpeed = BFL_Kinematics.GetHorizontalSpeed(newVelocity); ``` **Output:** `newVelocity`, `newSpeed` --- ### Phase 4: Movement Application (Sweep) ```typescript // Convert velocity to displacement const desiredDelta = newVelocity * DeltaTime; // Perform swept collision const sweepResult = BFL_CollisionResolver.PerformSweep( CurrentState.Location, desiredDelta, CapsuleComponent, Config, DeltaTime, IsShowVisualDebug ); let finalLocation = sweepResult.Location; // Handle collision sliding if (sweepResult.Blocked) { const slideVector = BFL_CollisionResolver.CalculateSlideVector( sweepResult, desiredDelta, CurrentState.Location ); // Apply slide if valid if (VectorLength(slideVector) > 0.5 && Dot(Normal(slideVector), sweepResult.Hit.ImpactNormal) >= -0.1) { finalLocation = sweepResult.Location + slideVector; } } ``` **Output:** `finalLocation`, `sweepResult` --- ### Phase 5: Ground Snapping ```typescript if (BFL_GroundProbe.ShouldSnapToGround( newVelocity.Z, groundHit, isGrounded )) { finalLocation = BFL_GroundProbe.CalculateSnapLocation( finalLocation, groundHit, CapsuleComponent, Config.GroundTraceDistance ); } ``` **Output:** `finalLocation` (potentially snapped) --- ### Phase 6: State Determination ```typescript const movementState = BFL_MovementStateMachine.DetermineState({ IsGrounded: isGrounded, SurfaceType: surfaceType, InputMagnitude: inputMagnitude, CurrentSpeed: newSpeed, VerticalVelocity: newVelocity.Z, IsBlocked: sweepResult.Blocked }); ``` **Output:** `movementState` (E_MovementState) --- ### Final State Construction ```typescript return { Location: finalLocation, Rotation: rotationResult.Rotation, Velocity: newVelocity, Speed: newSpeed, IsGrounded: isGrounded, GroundHit: groundHit, SurfaceType: surfaceType, IsBlocked: sweepResult.Blocked, CollisionCount: sweepResult.CollisionCount, IsRotating: rotationResult.IsRotating, RotationDelta: rotationResult.RemainingDelta, MovementState: movementState, InputMagnitude: inputMagnitude }; ``` --- ## API Reference ### AC_Movement #### InitializeMovementSystem() ```typescript InitializeMovementSystem( CapsuleComponentRef: CapsuleComponent | null = null, DebugHUDComponentRef: AC_DebugHUD | null = null ): void ``` **Описание:** Инициализирует систему движения **Эффекты:** - Sets `IsInitialized = true` - Converts angle thresholds degrees → radians - Creates initial movement state - Registers debug page if HUD provided **Пример:** ```typescript this.MovementComponent.InitializeMovementSystem( this.CharacterCapsule, this.DebugHUDComponent ); ``` --- #### ProcessMovementInput() ```typescript ProcessMovementInput( InputVector: Vector, DeltaTime: Float ): void ``` **Описание:** Main movement processing entry point **Параметры:** - `InputVector` - Camera-relative movement input (normalized) - `DeltaTime` - Frame delta time (seconds) **Flow:** 1. Constructs S_MovementInput 2. Calls BFL_MovementProcessor.ProcessMovement() 3. Applies resulting Location and Rotation to actor **Пример:** ```typescript // In EventTick this.MovementComponent.ProcessMovementInput( this.CalculateMovementInput(), DeltaSeconds ); ``` --- ### BFL_MovementProcessor #### ProcessMovement() ```typescript ProcessMovement( CurrentState: S_MovementState, Input: S_MovementInput, IsShowVisualDebug: boolean = false ): S_MovementState ``` **Описание:** Unified movement processing - executes all 6 phases **Параметры:** - `CurrentState` - Current movement state (immutable) - `Input` - Movement input data - `IsShowVisualDebug` - Show debug traces in world **Возвращает:** New complete movement state **Purity:** Impure (collision traces), but deterministic **Пример:** ```typescript const newState = BFL_MovementProcessor.ProcessMovement( this.CurrentMovementState, { InputVector: inputVector, DeltaTime: deltaTime, CapsuleComponent: this.CapsuleComponent, Config: this.Config, AngleThresholdsRads: this.AngleThresholdsRads }, this.ShowDebug ); // Apply results this.GetOwner().SetActorLocation(newState.Location); this.GetOwner().SetActorRotation(newState.Rotation); this.CurrentMovementState = newState; ``` --- #### CreateInitialState() ```typescript CreateInitialState( Location: Vector, Rotation: Rotator ): S_MovementState ``` **Описание:** Creates initial movement state with defaults **Purity:** Pure **Пример:** ```typescript this.CurrentMovementState = BFL_MovementProcessor.CreateInitialState( this.GetOwner().GetActorLocation(), this.GetOwner().GetActorRotation() ); ``` --- ## Best Practices ### Initialization ```typescript // ✅ Good - proper initialization order class BP_MainCharacter extends Character { private MovementComponent: AC_Movement; ReceiveBeginPlay(): void { this.MovementComponent.InitializeMovementSystem( this.GetCapsuleComponent(), this.DebugHUDComponent ); } } // ❌ Bad - using before initialization ReceiveBeginPlay(): void { this.MovementComponent.ProcessMovementInput(input, dt); // Will early-return! } ``` --- ### State Access ```typescript // ✅ Good - direct state access const currentSpeed = this.MovementComponent.CurrentMovementState.Speed; const isGrounded = this.MovementComponent.CurrentMovementState.IsGrounded; // 🔄 Alternative - expose via getter class AC_Movement { public GetMovementState(): S_MovementState { return this.CurrentMovementState; } } const state = this.MovementComponent.GetMovementState(); const speed = state.Speed; ``` --- ### Testing ```typescript // ✅ Good - test processor directly describe('BFL_MovementProcessor', () => { it('should accelerate on ground', () => { const initialState = BFL_MovementProcessor.CreateInitialState( new Vector(0, 0, 100), new Rotator(0, 0, 0) ); const input: S_MovementInput = { InputVector: new Vector(1, 0, 0), DeltaTime: 0.016, CapsuleComponent: mockCapsule, Config: testConfig, AngleThresholdsRads: testThresholds }; const newState = BFL_MovementProcessor.ProcessMovement( initialState, input, false ); expect(newState.Velocity.X).toBeGreaterThan(0); expect(newState.MovementState).toBe(E_MovementState.Walking); }); }); ``` --- ### Performance ```typescript // ✅ Good - check initialization once if (this.MovementComponent.IsInitialized) { // Movement processing } // ✅ Good - cache config access const maxSpeed = this.Config.MaxSpeed; for (let i = 0; i < 100; i++) { // Use maxSpeed } // ❌ Bad - repeated property access for (let i = 0; i < 100; i++) { if (speed > this.Config.MaxSpeed) { ... } } ``` --- ## Configuration Guidelines ### Movement Physics | Parameter | Default | Description | Tuning Guide | |-----------|---------|-------------|--------------| | MaxSpeed | 800.0 | Max horizontal speed (cm/s) | Higher = faster character | | Acceleration | 10.0 | VInterpTo acceleration rate | Higher = more responsive | | Friction | 8.0 | VInterpTo friction rate | Higher = faster stopping | | Gravity | 980.0 | Gravity force (cm/s²) | Standard Earth gravity | ### Surface Detection | Parameter | Default | Description | |-----------|---------|-------------| | Walkable | 50° | Max walkable angle | | SteepSlope | 85° | Max steep slope angle | | Wall | 95° | Max wall angle | ### Collision | Parameter | Default | Description | Performance Impact | |-----------|---------|-------------|-------------------| | MinStepSize | 1.0 | Min sweep step (cm) | Smaller = more precise, slower | | MaxStepSize | 50.0 | Max sweep step (cm) | Larger = faster, less precise | | MaxCollisionChecks | 25 | Max checks per frame | Higher = safer, more expensive | | GroundTraceDistance | 50.0 | Ground detection distance (cm) | Balance precision vs cost | ### Rotation | Parameter | Default | Description | |-----------|---------|-------------| | RotationSpeed | 360.0 | Rotation speed (deg/s) | | MinSpeedForRotation | 50.0 | Min speed to rotate (cm/s) | | ShouldRotateToMovement | true | Enable rotation | --- ## Performance Characteristics ### Frame Budget - **Target:** <1ms per frame (60 FPS) - **Typical:** 0.3-0.7ms - **Max:** ~1.5ms (complex collision scenarios) ### Bottlenecks 1. **Swept Collision** (most expensive) - Multiple CapsuleTraceByChannel calls - Adaptive stepping helps - Monitor `CollisionCount` in debug HUD 2. **Ground Detection** (moderate) - Single LineTraceByChannel per frame - Always runs (even airborne) 3. **Physics Calculations** (cheap) - Pure math operations - VInterpTo is optimized ### Optimization Tips 1. Increase `MinStepSize` if collision checks too high 2. Decrease `GroundTraceDistance` to minimum needed 3. Disable `ShouldRotateToMovement` for static NPCs 4. Use `IsShowVisualDebug = false` in production --- ## Testing Strategy ### Unit Testing (BFL_* modules) ```typescript describe('BFL_Kinematics', () => { it('should apply friction correctly', () => { const velocity = new Vector(800, 0, 0); const result = BFL_Kinematics.CalculateFriction( velocity, 0.016, testConfig ); expect(result.X).toBeLessThan(velocity.X); expect(result.Z).toBe(0); }); }); ``` ### Integration Testing (BFL_MovementProcessor) ```typescript describe('Movement Pipeline', () => { it('should process complete frame', () => { const state = processFullFrame(initialState, input); expect(state.Location).not.toEqual(initialState.Location); expect(state.Velocity).toBeDefined(); expect(state.MovementState).toBeDefined(); }); }); ``` ### Manual Testing (see ManualTestingChecklist.md) - Ground detection - Surface classification - Collision response - Rotation behavior - Debug visualization --- ## Known Limitations ### Current Constraints 1. **Binary Ground State:** No partial contact detection 2. **Single Collision Shape:** Capsule only 3. **Frame-Dependent Stepping:** Sweep precision varies with framerate 4. **No Material Physics:** Surface material not considered 5. **Simple Sliding:** Basic projection, no advanced friction ### By Design 1. **Capsule Component Required:** System assumes capsule collision 2. **Deterministic Traces:** Relies on UE physics determinism 3. **Horizontal Focus:** Optimized for ground-based movement 4. **No Network Code:** Not yet optimized for multiplayer --- ## Future Extensions (Stage 10+) ### Planned Features - **Jump System:** Vertical velocity application, coyote time, jump buffering - **Steep Slope Sliding:** Physics-based sliding on non-walkable surfaces - **Moving Platforms:** Platform attachment and relative movement - **Wall Running:** Advanced surface interaction - **Ledge Detection:** Edge detection for platforming - **Material-Based Physics:** Surface material awareness --- ## File Structure ``` Content/ ├── Movement/ │ ├── Components/ │ │ └── AC_Movement.ts # Imperative shell (~230 LOC) │ ├── Core/ │ │ ├── BFL_MovementProcessor.ts # Functional core (~260 LOC) │ │ ├── DA_MovementConfig.ts # Configuration asset │ │ ├── DA_MovementConfigDefault.ts # Default config │ │ ├── E_MovementState.ts # Movement states enum │ │ ├── S_MovementInput.ts # Input structure │ │ └── S_MovementState.ts # State structure │ ├── Collision/ │ │ ├── BFL_CollisionResolver.ts # Swept collision │ │ ├── BFL_GroundProbe.ts # Ground detection │ │ └── S_SweepResult.ts # Sweep result structure │ ├── Physics/ │ │ └── BFL_Kinematics.ts # Movement physics │ ├── Rotation/ │ │ ├── BFL_RotationController.ts # Rotation logic │ │ └── S_RotationResult.ts # Rotation result structure │ ├── State/ │ │ ├── BFL_MovementStateMachine.ts # FSM logic │ │ └── S_MovementContext.ts # State context structure │ ├── Surface/ │ │ ├── BFL_SurfaceClassifier.ts # Surface classification │ │ ├── E_SurfaceType.ts # Surface types enum │ │ └── S_AngleThresholds.ts # Angle thresholds structure │ └── Documentation/ │ ├── TDD.md # This document │ └── ManualTestingChecklist.md # Testing procedures └── Math/ └── Libraries/ └── BFL_Vectors.ts # Vector math utilities ``` --- ## Troubleshooting ### Character Falling Through Ground **Symptoms:** Character drops through walkable surface **Checks:** 1. `GroundTraceDistance > 0` 2. Ground has `Visibility` collision channel 3. `CapsuleComponent` initialized correctly 4. `IsInitialized == true` **Debug:** ```typescript // Enable visual debug this.DebugHUDComponent.ShowVisualDebug = true; // Check ground hit in debug HUD // Look for green line trace downward ``` --- ### Excessive Collision Checks **Symptoms:** `CollisionCount` consistently hitting `MaxCollisionChecks` **Fixes:** 1. Increase `MaxStepSize` (careful: less precision) 2. Decrease `MaxSpeed` 3. Increase `MaxCollisionChecks` (careful: performance cost) 4. Check for collision geometry issues **Debug:** ```typescript // Monitor in debug HUD CollisionCount / MaxCollisionChecks // Should be <50% most frames ``` --- ### Jittery Z Position **Symptoms:** Character bouncing up/down slightly **Fixes:** 1. Verify ground detection working (`IsGrounded` in debug HUD) 2. Check ground snapping active 3. Slightly increase `GroundTraceDistance` 4. Verify ground has consistent collision --- ### Character Not Rotating **Symptoms:** Character faces wrong direction **Checks:** 1. `Config.ShouldRotateToMovement == true` 2. `CurrentSpeed > Config.MinSpeedForRotation` 3. `SetActorRotation()` called each frame 4. Input vector not zero **Debug:** ```typescript // Check in debug HUD Is Rotating: true/false Rotation Delta: [degrees remaining] Current Yaw: [current angle] ``` --- ## Conclusion Movement System представляет собой production-ready систему с следующими характеристиками: ### Архитектурные достижения ✅ - **Functional Core, Imperative Shell** pattern реализован полностью - **Clear separation of concerns** между subsystems - **Pipeline processing** с явными фазами - **Immutable state transformations** везде где возможно - **Testable design** благодаря pure function libraries ### Production Readiness ✅ - **Deterministic physics** с VInterpTo - **Tunneling protection** через swept collision - **Frame-rate independence** через DeltaTime - **Performance optimized** (<1ms typical frame time) - **Debug visualization** comprehensive - **Extensible architecture** для future features ### Code Quality ✅ - **LOC reduced** 62% в AC_Movement (600 → 230) - **Modularity** high - 8 focused modules - **Documentation** comprehensive - **Type safety** strong через TypeScript structs **Production Status:** ✅ **Ready for Stage 10** Архитектура готова для расширения Jump System и последующих features без major refactoring.