// Request Games © All rights reserved // Source/TengriPlatformer/Movement/Core/TengriMovementConfig.h #pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" #include "TengriMovementConfig.generated.h" // ============================================================================ // SURFACE THRESHOLDS // ============================================================================ /** * Pre-calculated cosine thresholds for surface classification. * Generated from angle degrees in UTengriMovementConfig. */ USTRUCT(BlueprintType) struct TENGRIPLATFORMER_API FSurfaceThresholds { GENERATED_BODY() /** Minimum Normal.Z for walkable floor (cos of max walkable angle) */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Thresholds") float WalkableZ = 0.64f; /** Minimum Normal.Z for steep slopes */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Thresholds") float SteepSlopeZ = 0.08f; /** Minimum Normal.Z for walls (below = ceiling/overhang) */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Thresholds") float WallZ = -0.08f; // === Classification Helpers === FORCEINLINE bool IsWalkable(const float NormalZ) const { return NormalZ >= WalkableZ; } FORCEINLINE bool IsSteepSlope(const float NormalZ) const { return NormalZ >= SteepSlopeZ && NormalZ < WalkableZ; } FORCEINLINE bool IsWall(const float NormalZ) const { return NormalZ >= WallZ && NormalZ < SteepSlopeZ; } FORCEINLINE bool IsOverhang(const float NormalZ) const { return NormalZ < WallZ; } }; // ============================================================================ // MOVEMENT CONFIG // ============================================================================ /** * Data asset containing all movement system parameters. * Create instances in Content Browser for different character types. */ UCLASS(BlueprintType) class TENGRIPLATFORMER_API UTengriMovementConfig : public UDataAsset { GENERATED_BODY() public: // ======================================================================== // PHYSICS // ======================================================================== UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics") float MaxSpeed = 800.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics") float Acceleration = 2048.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics") float Friction = 8.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics") float RotationSpeed = 360.0f; /** Minimum speed (cm/s) required to rotate character toward movement */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics") float MinSpeedForRotation = 10.0f; // ======================================================================== // JUMP CONFIGURATION // ======================================================================== /** Target height of the jump in cm (UE units) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump", meta = (ClampMin = "10.0")) float MaxJumpHeight = 200.0f; /** Minimum height for a short hop (when button is released early) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump", meta = (ClampMin = "1.0")) float MinJumpHeight = 40.0f; /** Time (seconds) to reach the peak of the jump. Defines "heaviness". */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump", meta = (ClampMin = "0.1", ClampMax = "2.0")) float TimeToJumpApex = 0.5f; // ======================================================================== // JUMP FEEL (TIMINGS) // ======================================================================== /** Time (seconds) after falling off a ledge during which jump is still allowed */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump|Feel", meta = (ClampMin = "0.0")) float CoyoteTime = 0.15f; /** Time (seconds) to buffer a jump input before hitting the ground */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump|Feel", meta = (ClampMin = "0.0")) float JumpBufferTime = 0.15f; // ======================================================================== // AIR PHYSICS // ======================================================================== /** Multiplier for acceleration when in air (0 = no control, 1 = full control) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Air Physics", meta = (ClampMin = "0.0", ClampMax = "1.0")) float AirControl = 0.5f; /** Friction applied while in air (usually 0 for platformers) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Air Physics") float AirFriction = 0.0f; // ======================================================================== // CALCULATED VALUES (READ ONLY) // ======================================================================== /** Gravity magnitude calculated from Jump Height & Time */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Calculated") float Gravity = 980.0f; /** Initial Z velocity required to reach MaxJumpHeight */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Calculated") float JumpVelocity = 0.0f; /** Velocity cut-off for variable jump height */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Calculated") float MinJumpVelocity = 0.0f; // ======================================================================== // AIR PHYSICS // ======================================================================== /** Multiplier for gravity when falling. Makes jump feel "heavy" and snappy. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Air Physics", meta = (ClampMin = "1.0")) float FallingGravityScale = 1.5f; /** Maximum falling speed (cm/s). Prevents infinite acceleration. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Air Physics", meta = (ClampMin = "0.0")) float TerminalVelocity = 2000.0f; /** Z velocity threshold to consider a landing "heavy" (e.g. for landing animation/shake) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Air Physics") float HeavyLandVelocityThreshold = -1000.0f; // ======================================================================== // SURFACE ANGLES // ======================================================================== /** Maximum angle (degrees) that counts as walkable floor */ UPROPERTY(EditAnywhere, Category = "Surface Angles", meta = (ClampMin = "0", ClampMax = "89")) float WalkableAngleDeg = 50.0f; /** Maximum angle for steep slopes (limited movement) */ UPROPERTY(EditAnywhere, Category = "Surface Angles", meta = (ClampMin = "0", ClampMax = "89")) float SteepSlopeAngleDeg = 85.0f; /** Maximum angle for walls (above = ceiling) */ UPROPERTY(EditAnywhere, Category = "Surface Angles", meta = (ClampMin = "90", ClampMax = "180")) float WallAngleDeg = 95.0f; // ======================================================================== // COLLISION // ======================================================================== UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision") int32 MaxSlideIterations = 3; /** Maximum height of step that can be walked over */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collision") float MaxStepHeight = 45.0f; // ======================================================================== // GROUND SNAPPING // ======================================================================== /** Distance to search for ground when snapping */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ground Snapping") float GroundSnapDistance = 20.0f; /** Micro-offset above ground to prevent floor penetration */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ground Snapping") float GroundSnapOffset = 0.15f; // ======================================================================== // SLOPE PHYSICS // ======================================================================== /** Velocity damping on steep slopes (0 = full stop, 1 = full slide) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics", meta = (ClampMin = "0", ClampMax = "1")) float SteepSlopeSlideFactor = 0.0f; // ======================================================================== // FIXED TIMESTEP // ======================================================================== /** Physics update rate in Hz (default: 120) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Fixed Timestep", meta = (ClampMin = "30", ClampMax = "240")) float PhysicsTickRate = 120.0f; /** Maximum accumulated time before clamping (prevents spiral of death) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Fixed Timestep", meta = (ClampMin = "0.05", ClampMax = "0.5")) float MaxAccumulatedTime = 0.1f; /** Enable interpolation between physics states */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Fixed Timestep") bool bEnableInterpolation = true; // ======================================================================== // API // ======================================================================== /** Converts degree angles to cosine thresholds for runtime use */ FSurfaceThresholds GetThresholds() const; #if WITH_EDITOR virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif };