tengri/Source/TengriPlatformer/Movement/TengriMovementComponent.h

172 lines
6.3 KiB
C++

// Source/TengriPlatformer/Movement/TengriMovementComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "TengriPlatformer/Movement/Core/TengriMovementConfig.h"
#include "TengriMovementComponent.generated.h"
class UCapsuleComponent;
/**
* Custom movement component for deterministic 3D platformer physics.
* Uses fixed timestep physics with interpolated rendering.
*
* Architecture:
* - Physics State: Updated at fixed rate (default 120Hz) for determinism
* - Render State: Interpolated between physics states for smooth visuals
* - Accumulator: Manages variable frame delta accumulation
*/
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class TENGRIPLATFORMER_API UTengriMovementComponent : public UActorComponent
{
GENERATED_BODY()
public:
UTengriMovementComponent();
protected:
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
// ========================================================================
// BLUEPRINT API
// ========================================================================
/**
* Sets the movement input vector. Call every frame from PlayerController.
* @param NewInput - Raw input (will be clamped to 1.0 and Z zeroed)
*/
UFUNCTION(BlueprintCallable, Category = "Tengri Movement")
void SetInputVector(FVector NewInput);
// ========================================================================
// CONFIGURATION
// ========================================================================
/** Movement parameters data asset */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tengri Movement")
TObjectPtr<UTengriMovementConfig> MovementConfig;
// ========================================================================
// RUNTIME STATE
// ========================================================================
/** Current velocity in cm/s (synced from PhysicsVelocity each frame) */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tengri Movement|State")
FVector Velocity;
/** True when character is on walkable ground */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tengri Movement|State")
bool bIsGrounded = false;
/** Cached surface thresholds (cosines) for performance */
UPROPERTY(VisibleAnywhere, Category = "Tengri Movement|Debug")
FSurfaceThresholds CachedThresholds;
private:
// ========================================================================
// PHYSICS STATE (Deterministic)
// ========================================================================
/** Physics location updated at fixed timestep */
FVector PhysicsLocation = FVector::ZeroVector;
/** Physics rotation updated at fixed timestep */
FRotator PhysicsRotation = FRotator::ZeroRotator;
/** Physics velocity in cm/s */
FVector PhysicsVelocity = FVector::ZeroVector;
// ========================================================================
// RENDER STATE (Interpolated)
// ========================================================================
/** Interpolated location for smooth rendering */
FVector RenderLocation = FVector::ZeroVector;
/** Interpolated rotation for smooth rendering */
FRotator RenderRotation = FRotator::ZeroRotator;
// ========================================================================
// FIXED TIMESTEP
// ========================================================================
/** Fixed timestep duration in seconds (calculated from PhysicsTickRate) */
float FixedTimeStep = 1.0f / 120.0f;
/** Accumulated variable frame time */
float TimeAccumulator = 0.0f;
/** Maximum accumulator value to prevent spiral of death */
float MaxAccumulatorTime = 0.1f;
// ========================================================================
// INTERPOLATION HISTORY
// ========================================================================
/** Previous physics location for interpolation */
FVector PreviousPhysicsLocation = FVector::ZeroVector;
/** Previous physics rotation for interpolation */
FRotator PreviousPhysicsRotation = FRotator::ZeroRotator;
// ========================================================================
// INTERNAL STATE
// ========================================================================
UPROPERTY()
TObjectPtr<UCapsuleComponent> OwnerCapsule;
/** Normalized input vector (Z always 0) */
FVector InputVector = FVector::ZeroVector;
// ========================================================================
// INITIALIZATION
// ========================================================================
void InitializeSystem();
// ========================================================================
// PHYSICS TICK
// ========================================================================
/**
* Deterministic physics update at fixed timestep.
* All movement logic runs here with constant delta time.
* @param FixedDeltaTime - Fixed timestep duration (e.g., 1/120 sec)
*/
void TickPhysics(float FixedDeltaTime);
/** Save current physics state before next physics step */
void SavePreviousPhysicsState();
// ========================================================================
// INTERPOLATION
// ========================================================================
/**
* Interpolate render state between previous and current physics states.
* @param Alpha - Interpolation factor [0..1]
*/
void InterpolateRenderState(float Alpha);
/** Apply interpolated render state to actor transform */
void ApplyRenderState() const;
// ========================================================================
// PHYSICS HELPERS
// ========================================================================
/**
* Snap to ground to prevent slope jitter.
* @param InOutLocation - Physics location (modified if snap succeeds)
* @param OutSnapHit - Hit result if ground found
* @return True if snapped to ground
*/
bool PerformGroundSnapping(FVector& InOutLocation, FHitResult& OutSnapHit) const;
};