feat(movement): improve air control and ground snapping behavior
- Add directional air control modifier to prevent instant braking mid-air * Reduce control effectiveness by 50% when reversing direction (Dot < 0) * Allows side-to-side adjustments while maintaining forward momentum - Remove input requirement for velocity projection on ground snap * Project velocity onto slope even without player input * Preserves momentum when landing on slopes - Add KINDA_SMALL_NUMBER checks to prevent division by zero - Add GetRenderLocation() Blueprint accessor for VFX positioning - Clean up comments: translate Russian text to English This makes air movement feel more realistic (less like flying) while improving slope handling consistency.main
parent
abe8f565b6
commit
14d3696805
|
|
@ -313,33 +313,49 @@ void UTengriMovementComponent::TickPhysics(
|
||||||
const float CurrentZ = PhysicsVelocity.Z;
|
const float CurrentZ = PhysicsVelocity.Z;
|
||||||
FVector HorizontalVelocity(PhysicsVelocity.X, PhysicsVelocity.Y, 0.f);
|
FVector HorizontalVelocity(PhysicsVelocity.X, PhysicsVelocity.Y, 0.f);
|
||||||
|
|
||||||
// Select Acceleration/Friction based on state
|
|
||||||
const float CurrentAccel = bIsGrounded
|
|
||||||
? MovementConfig->Acceleration
|
|
||||||
: (MovementConfig->Acceleration * MovementConfig->AirControl);
|
|
||||||
|
|
||||||
const float CurrentFriction = bIsGrounded
|
const float CurrentFriction = bIsGrounded
|
||||||
? MovementConfig->Friction
|
? MovementConfig->Friction
|
||||||
: MovementConfig->AirFriction;
|
: MovementConfig->AirFriction;
|
||||||
|
|
||||||
if (!InputVector.IsNearlyZero())
|
if (!InputVector.IsNearlyZero())
|
||||||
{
|
{
|
||||||
|
// Calculate directional air control modifier
|
||||||
|
// Reduce control when trying to reverse direction mid-air
|
||||||
|
const FVector VelocityDir = HorizontalVelocity.GetSafeNormal();
|
||||||
|
const float Dot = FVector::DotProduct(VelocityDir, InputVector);
|
||||||
|
|
||||||
|
float FinalAirControl = MovementConfig->AirControl;
|
||||||
|
|
||||||
|
// Reduce braking effectiveness in air (prevents instant direction changes)
|
||||||
|
if (Dot < 0.0f)
|
||||||
|
{
|
||||||
|
FinalAirControl *= 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float CurrentAccel = MovementConfig->Acceleration * FinalAirControl;
|
||||||
const FVector TargetVelocity = InputVector * MovementConfig->MaxSpeed;
|
const FVector TargetVelocity = InputVector * MovementConfig->MaxSpeed;
|
||||||
HorizontalVelocity = FMath::VInterpTo(
|
|
||||||
HorizontalVelocity,
|
if (CurrentAccel > KINDA_SMALL_NUMBER)
|
||||||
TargetVelocity,
|
{
|
||||||
FixedDeltaTime,
|
HorizontalVelocity = FMath::VInterpTo(
|
||||||
CurrentAccel // <-- Uses Air Control if flying
|
HorizontalVelocity,
|
||||||
);
|
TargetVelocity,
|
||||||
|
FixedDeltaTime,
|
||||||
|
CurrentAccel
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HorizontalVelocity = FMath::VInterpTo(
|
if (CurrentFriction > KINDA_SMALL_NUMBER)
|
||||||
HorizontalVelocity,
|
{
|
||||||
FVector::ZeroVector,
|
HorizontalVelocity = FMath::VInterpTo(
|
||||||
FixedDeltaTime,
|
HorizontalVelocity,
|
||||||
CurrentFriction // <-- Usually 0 in air
|
FVector::ZeroVector,
|
||||||
);
|
FixedDeltaTime,
|
||||||
|
CurrentFriction
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsVelocity = HorizontalVelocity;
|
PhysicsVelocity = HorizontalVelocity;
|
||||||
|
|
@ -450,8 +466,8 @@ void UTengriMovementComponent::TickPhysics(
|
||||||
FHitResult SnapHit;
|
FHitResult SnapHit;
|
||||||
bJustSnapped = PerformGroundSnapping(PhysicsLocation, SnapHit);
|
bJustSnapped = PerformGroundSnapping(PhysicsLocation, SnapHit);
|
||||||
|
|
||||||
// Project velocity onto slope if snapped
|
// Always project velocity onto slope when snapped (preserves momentum)
|
||||||
if (bJustSnapped && !InputVector.IsNearlyZero())
|
if (bJustSnapped)
|
||||||
{
|
{
|
||||||
PhysicsVelocity = UTengriCollisionResolver::ProjectVelocity(
|
PhysicsVelocity = UTengriCollisionResolver::ProjectVelocity(
|
||||||
PhysicsVelocity,
|
PhysicsVelocity,
|
||||||
|
|
@ -571,8 +587,8 @@ bool UTengriMovementComponent::PerformGroundSnapping(
|
||||||
|
|
||||||
void UTengriMovementComponent::ForceRotation(const FRotator& NewRotation)
|
void UTengriMovementComponent::ForceRotation(const FRotator& NewRotation)
|
||||||
{
|
{
|
||||||
// Обновляем все внутренние состояния, чтобы физика "подхватила" новый поворот
|
// Update all internal states so physics adopts new rotation immediately
|
||||||
PhysicsRotation = NewRotation;
|
PhysicsRotation = NewRotation;
|
||||||
RenderRotation = NewRotation;
|
RenderRotation = NewRotation;
|
||||||
PreviousPhysicsRotation = NewRotation; // Сбрасываем интерполяцию
|
PreviousPhysicsRotation = NewRotation; // Reset interpolation
|
||||||
}
|
}
|
||||||
|
|
@ -32,6 +32,10 @@ class TENGRIPLATFORMER_API UTengriMovementComponent : public UActorComponent
|
||||||
public:
|
public:
|
||||||
UTengriMovementComponent();
|
UTengriMovementComponent();
|
||||||
|
|
||||||
|
/** Get interpolated render location */
|
||||||
|
UFUNCTION(BlueprintPure, Category = "Tengri Movement")
|
||||||
|
FVector GetRenderLocation() const { return RenderLocation; }
|
||||||
|
|
||||||
/** Event triggered when character lands (broadcast after physics loop completes) */
|
/** Event triggered when character lands (broadcast after physics loop completes) */
|
||||||
UPROPERTY(BlueprintAssignable, Category = "Tengri Movement|Events")
|
UPROPERTY(BlueprintAssignable, Category = "Tengri Movement|Events")
|
||||||
FOnTengriLandingSignature OnLanded;
|
FOnTengriLandingSignature OnLanded;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue