81 if (!CanStepUp(InHit) || MaxStepHeight <= 0.f)
86 const FVector OldLocation = UpdatedComponent->GetComponentLocation();
87 float PawnRadius, PawnHalfHeight;
88 CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
91 const float InitialImpactZ = InHit.ImpactPoint.Z;
92 if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius))
103 ensure(GravDir.IsNormalized());
105 float StepTravelUpHeight = MaxStepHeight;
106 float StepTravelDownHeight = StepTravelUpHeight;
107 const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir);
108 float PawnInitialFloorBaseZ = OldLocation.Z - PawnHalfHeight;
109 float PawnFloorPointZ = PawnInitialFloorBaseZ;
111 if (IsMovingOnGround() && CurrentFloor.IsWalkableFloor())
114 const float FloorDist = FMath::Max(0.f, CurrentFloor.GetDistanceToFloor());
115 PawnInitialFloorBaseZ -= FloorDist;
116 StepTravelUpHeight = FMath::Max(StepTravelUpHeight - FloorDist, 0.f);
117 StepTravelDownHeight = (MaxStepHeight + MAX_FLOOR_DIST*2.f);
119 const bool bHitVerticalFace = !IsWithinEdgeTolerance(InHit.Location, InHit.ImpactPoint, PawnRadius);
120 if (!CurrentFloor.bLineTrace && !bHitVerticalFace)
122 PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint.Z;
127 PawnFloorPointZ -= CurrentFloor.FloorDist;
132 if (InitialImpactZ <= PawnInitialFloorBaseZ)
141 FHitResult SweepUpHit(1.f);
142 const FQuat PawnRotation = UpdatedComponent->GetComponentQuat();
143 MoveUpdatedComponent(-GravDir * StepTravelUpHeight, PawnRotation,
true, &SweepUpHit);
145 if (SweepUpHit.bStartPenetrating)
148 ScopedStepUpMovement.RevertMove();
154 MoveUpdatedComponent(Delta, PawnRotation,
true, &Hit);
157 if (Hit.bBlockingHit)
159 if (Hit.bStartPenetrating)
162 ScopedStepUpMovement.RevertMove();
169 if (SweepUpHit.bBlockingHit && Hit.bBlockingHit)
171 HandleImpact(SweepUpHit);
182 ScopedStepUpMovement.RevertMove();
204 MoveUpdatedComponent(GravDir * StepTravelDownHeight, UpdatedComponent->GetComponentQuat(),
true, &Hit);
207 if (Hit.bStartPenetrating)
209 ScopedStepUpMovement.RevertMove();
213 FStepDownResult StepDownResult;
214 if (Hit.IsValidBlockingHit())
217 const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ;
218 if (DeltaZ > MaxStepHeight)
221 ScopedStepUpMovement.RevertMove();
226 if (!IsWalkable(Hit))
229 const bool bNormalTowardsMe = (Delta | Hit.ImpactNormal) < 0.f;
230 if (bNormalTowardsMe)
233 ScopedStepUpMovement.RevertMove();
239 if (Hit.Location.Z > OldLocation.Z)
242 ScopedStepUpMovement.RevertMove();
248 if (!IsWithinEdgeTolerance(Hit.Location, Hit.ImpactPoint, PawnRadius))
251 ScopedStepUpMovement.RevertMove();
256 if (DeltaZ > 0.f && !CanStepUp(Hit))
259 ScopedStepUpMovement.RevertMove();
264 if (OutStepDownResult != NULL)
266 FindFloor(UpdatedComponent->GetComponentLocation(), StepDownResult.FloorResult,
false, &Hit);
270 if (Hit.Location.Z > OldLocation.Z)
274 if (!StepDownResult.FloorResult.bBlockingHit && StepSideZ <
MAX_STEP_SIDE_ZZ)
276 ScopedStepUpMovement.RevertMove();
281 StepDownResult.bComputedFloor =
true;
286 if (OutStepDownResult != NULL)
288 *OutStepDownResult = StepDownResult;
292 bJustTeleported |= !bMaintainHorizontalGroundVelocity;
299 if (deltaTime < MIN_TICK_TIME)
304 RestorePreAdditiveRootMotionVelocity();
307 if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
309 if (bCheatFlying && Acceleration.IsZero())
311 Velocity = FVector::ZeroVector;
313 const float Friction = 0.5f * GetPhysicsVolume()->FluidFriction;
314 CalcVelocity(deltaTime, Friction,
true, BrakingDecelerationFlying);
317 ApplyRootMotionToVelocity(deltaTime);
321 bJustTeleported =
false;
323 FVector OldLocation = UpdatedComponent->GetComponentLocation();
324 const FVector Adjusted = Velocity * deltaTime;
326 SafeMoveUpdatedComponent(Adjusted, UpdatedComponent->GetComponentQuat(),
true, Hit);
330 const FVector GravDir = FVector(0.f, 0.f, -1.f);
331 const FVector VelDir = Velocity.GetSafeNormal();
332 const float UpDown = GravDir | VelDir;
334 bool bSteppedUp =
false;
335 if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
337 float stepZ = UpdatedComponent->GetComponentLocation().Z;
338 bSteppedUp = StepUp(GravDir, Adjusted * (1.f - Hit.Time), Hit);
341 OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
348 HandleImpact(Hit, deltaTime, Adjusted);
353 if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
355 Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / deltaTime;
363 if (deltaTime < MIN_TICK_TIME)
368 if ((!CharacterOwner || !CharacterOwner->Controller) && !bRunPhysicsWithNoController && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
370 Acceleration = FVector::ZeroVector;
371 Velocity = FVector::ZeroVector;
375 RestorePreAdditiveRootMotionVelocity();
379 MaintainHorizontalGroundVelocity();
380 devCodeSimple(ensureMsgf(!Velocity.ContainsNaN(), TEXT(
"PhysNavWalking: Velocity contains NaN before CalcVelocity (%s)\n%s"), *GetPathNameSafe(
this), *Velocity.ToString()));
383 Acceleration.Z = 0.f;
384 if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
386 CalcVelocity(deltaTime, GroundFriction,
false, BrakingDecelerationWalking);
387 devCodeSimple(ensureMsgf(!Velocity.ContainsNaN(), TEXT(
"PhysNavWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(
this), *Velocity.ToString()));
390 ApplyRootMotionToVelocity(deltaTime);
396 StartNewPhysics(deltaTime, Iterations);
402 FVector DesiredMove = Velocity;
405 const FVector OldLocation = GetActorFeetLocation();
406 const FVector DeltaMove = DesiredMove * deltaTime;
407 const bool bDeltaMoveNearlyZero = DeltaMove.IsNearlyZero();
409 FVector AdjustedDest = OldLocation + DeltaMove;
410 FNavLocation DestNavLocation;
412 bool bSameNavLocation =
false;
413 if (CachedNavLocation.NodeRef != INVALID_NAVNODEREF)
415 if (bProjectNavMeshWalking)
417 const float DistSq2D = (OldLocation - CachedNavLocation.Location).SizeSquared2D();
418 const float DistZ = FMath::Abs(OldLocation.Z - CachedNavLocation.Location.Z);
420 const float TotalCapsuleHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.0f;
421 const float ProjectionScale = (OldLocation.Z > CachedNavLocation.Location.Z) ? NavMeshProjectionHeightScaleUp : NavMeshProjectionHeightScaleDown;
422 const float DistZThr = TotalCapsuleHeight * FMath::Max(0.f, ProjectionScale);
424 bSameNavLocation = (DistSq2D <= KINDA_SMALL_NUMBER) && (DistZ < DistZThr);
428 bSameNavLocation = CachedNavLocation.Location.Equals(OldLocation);
431 if (bDeltaMoveNearlyZero && bSameNavLocation)
433 if (
const INavigationDataInterface * NavData = GetNavData())
435 if (!NavData->IsNodeRefValid(CachedNavLocation.NodeRef))
437 CachedNavLocation.NodeRef = INVALID_NAVNODEREF;
438 bSameNavLocation =
false;
445 if (bDeltaMoveNearlyZero && bSameNavLocation)
447 DestNavLocation = CachedNavLocation;
457 if (bSameNavLocation && bProjectNavMeshWalking)
459 AdjustedDest.Z = CachedNavLocation.Location.Z;
463 const bool bHasNavigationData = FindNavFloor(AdjustedDest, DestNavLocation);
464 if (!bHasNavigationData)
466 SetMovementMode(MOVE_Walking);
470 CachedNavLocation = DestNavLocation;
473 if (DestNavLocation.NodeRef != INVALID_NAVNODEREF)
475 FVector NewLocation(AdjustedDest.X, AdjustedDest.Y, DestNavLocation.Location.Z);
476 if (bProjectNavMeshWalking)
479 const float TotalCapsuleHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.0f;
480 const float UpOffset = TotalCapsuleHeight * FMath::Max(0.f, NavMeshProjectionHeightScaleUp);
481 const float DownOffset = TotalCapsuleHeight * FMath::Max(0.f, NavMeshProjectionHeightScaleDown);
482 NewLocation = ProjectLocationFromNavMesh(deltaTime, OldLocation, NewLocation, UpOffset, DownOffset);
485 FVector AdjustedDelta = NewLocation - OldLocation;
487 if (!AdjustedDelta.IsNearlyZero())
489 FHitResult HitResult;
490 SafeMoveUpdatedComponent(AdjustedDelta, UpdatedComponent->GetComponentQuat(), bSweepWhileNavWalking, HitResult);
494 if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasVelocity())
496 Velocity = (GetActorFeetLocation() - OldLocation) / deltaTime;
497 MaintainHorizontalGroundVelocity();
500 bJustTeleported =
false;
504 StartFalling(Iterations, deltaTime, deltaTime, DeltaMove, OldLocation);
512 if (deltaTime < MIN_TICK_TIME)
517 FVector FallAcceleration = GetFallingLateralAcceleration(deltaTime);
518 FallAcceleration.Z = 0.f;
519 const bool bHasLimitedAirControl = ShouldLimitAirControl(deltaTime, FallAcceleration);
521 float remainingTime = deltaTime;
522 while ((remainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations))
525 float timeTick = GetSimulationTimeStep(remainingTime, Iterations);
526 remainingTime -= timeTick;
528 const FVector OldLocation = UpdatedComponent->GetComponentLocation();
529 const FQuat PawnRotation = UpdatedComponent->GetComponentQuat();
530 bJustTeleported =
false;
532 RestorePreAdditiveRootMotionVelocity();
535 const FVector OldVelocity = Velocity;
538 const float MaxDecel = GetMaxBrakingDeceleration();
539 if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
544 TGuardValue<FVector> RestoreAcceleration(Acceleration, FallAcceleration);
546 CalcVelocity(timeTick, FallingLateralFriction,
false, BrakingDecelerationFalling);
547 Velocity.Z = OldVelocity.Z;
552 const FVector Gravity(0.f, 0.f, GetGravityZ());
554 float GravityTime = timeTick;
557 bool bEndingJumpForce =
false;
558 if (CharacterOwner->JumpForceTimeRemaining > 0.0f)
561 const float JumpForceTime = FMath::Min(CharacterOwner->JumpForceTimeRemaining, timeTick);
562 GravityTime = bApplyGravityWhileJumping ? timeTick : FMath::Max(0.0f, timeTick - JumpForceTime);
565 CharacterOwner->JumpForceTimeRemaining -= JumpForceTime;
566 if (CharacterOwner->JumpForceTimeRemaining <= 0.0f)
568 CharacterOwner->ResetJumpState();
569 bEndingJumpForce =
true;
574 Velocity = NewFallVelocity(Velocity, Gravity, GravityTime);
577 static const auto CVarForceJumpPeakSubstep = IConsoleManager::Get().FindConsoleVariable(TEXT(
"p.ForceJumpPeakSubstep"));
578 if (CVarForceJumpPeakSubstep->GetInt() != 0 && OldVelocity.Z > 0.f && Velocity.Z <= 0.f && NumJumpApexAttempts < MaxJumpApexAttemptsPerSimulation)
580 const FVector DerivedAccel = (Velocity - OldVelocity) / timeTick;
581 if (!FMath::IsNearlyZero(DerivedAccel.Z))
583 const float TimeToApex = -OldVelocity.Z / DerivedAccel.Z;
586 const float ApexTimeMinimum = 0.0001f;
587 if (TimeToApex >= ApexTimeMinimum && TimeToApex < timeTick)
589 const FVector ApexVelocity = OldVelocity + DerivedAccel * TimeToApex;
590 Velocity = ApexVelocity;
594 remainingTime += (timeTick - TimeToApex);
595 timeTick = TimeToApex;
597 NumJumpApexAttempts++;
604 ApplyRootMotionToVelocity(timeTick);
607 if (bNotifyApex && (Velocity.Z < 0.f))
618 if (bEndingJumpForce && !bApplyGravityWhileJumping)
622 const float NonGravityTime = FMath::Max(0.f, timeTick - GravityTime);
623 Adjusted = ((OldVelocity * NonGravityTime) + (0.5f * (OldVelocity + Velocity) * GravityTime)) + (
AdditionalVRInputVector );
629 SafeMoveUpdatedComponent(Adjusted, PawnRotation,
true, Hit);
636 float LastMoveTimeSlice = timeTick;
637 float subTimeTickRemaining = timeTick * (1.f - Hit.Time);
641 remainingTime += subTimeTickRemaining;
642 StartSwimming(OldLocation, OldVelocity, timeTick, remainingTime, Iterations);
645 else if (Hit.bBlockingHit)
647 if (IsValidLandingSpot(UpdatedComponent->GetComponentLocation(), Hit))
649 remainingTime += subTimeTickRemaining;
650 ProcessLanded(Hit, remainingTime, Iterations);
657 Adjusted = Velocity * timeTick;
660 if (!Hit.bStartPenetrating && ShouldCheckForValidLandingSpot(timeTick, Adjusted, Hit))
662 const FVector PawnLocation = UpdatedComponent->GetComponentLocation();
663 FFindFloorResult FloorResult;
664 FindFloor(PawnLocation, FloorResult,
false);
665 if (FloorResult.IsWalkableFloor() && IsValidLandingSpot(PawnLocation, FloorResult.HitResult))
667 remainingTime += subTimeTickRemaining;
668 ProcessLanded(FloorResult.HitResult, remainingTime, Iterations);
673 HandleImpact(Hit, LastMoveTimeSlice, Adjusted);
676 if (!HasValidData() || !IsFalling())
683 FVector VelocityNoAirControl = OldVelocity;
684 FVector AirControlAccel = Acceleration;
685 if (bHasLimitedAirControl)
690 TGuardValue<FVector> RestoreAcceleration(Acceleration, FVector::ZeroVector);
691 TGuardValue<FVector> RestoreVelocity(Velocity, OldVelocity);
693 CalcVelocity(timeTick, FallingLateralFriction,
false, MaxDecel);
694 VelocityNoAirControl = FVector(Velocity.X, Velocity.Y, OldVelocity.Z);
695 VelocityNoAirControl = NewFallVelocity(VelocityNoAirControl, Gravity, GravityTime);
699 const bool bCheckLandingSpot =
false;
700 AirControlAccel = (Velocity - VelocityNoAirControl) / timeTick;
701 const FVector AirControlDeltaV = LimitAirControl(LastMoveTimeSlice, AirControlAccel, Hit, bCheckLandingSpot) * LastMoveTimeSlice;
702 Adjusted = (VelocityNoAirControl + AirControlDeltaV) * LastMoveTimeSlice;
705 const FVector OldHitNormal = Hit.Normal;
706 const FVector OldHitImpactNormal = Hit.ImpactNormal;
707 FVector Delta = ComputeSlideVector(Adjusted, 1.f - Hit.Time, OldHitNormal, Hit);
710 if (subTimeTickRemaining > KINDA_SMALL_NUMBER && !bJustTeleported)
712 const FVector NewVelocity = (Delta / subTimeTickRemaining);
713 Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
716 if (subTimeTickRemaining > KINDA_SMALL_NUMBER && (Delta | Adjusted) > 0.f)
719 SafeMoveUpdatedComponent(Delta, PawnRotation,
true, Hit);
721 if (Hit.bBlockingHit)
724 LastMoveTimeSlice = subTimeTickRemaining;
725 subTimeTickRemaining = subTimeTickRemaining * (1.f - Hit.Time);
727 if (IsValidLandingSpot(UpdatedComponent->GetComponentLocation(), Hit))
729 remainingTime += subTimeTickRemaining;
730 ProcessLanded(Hit, remainingTime, Iterations);
734 HandleImpact(Hit, LastMoveTimeSlice, Delta);
737 if (!HasValidData() || !IsFalling())
745 const FVector LastMoveNoAirControl = VelocityNoAirControl * LastMoveTimeSlice;
746 Delta = ComputeSlideVector(LastMoveNoAirControl, 1.f, OldHitNormal, Hit);
749 FVector PreTwoWallDelta = Delta;
750 TwoWallAdjust(Delta, Hit, OldHitNormal);
753 if (bHasLimitedAirControl)
755 const bool bCheckLandingSpot =
false;
756 const FVector AirControlDeltaV = LimitAirControl(subTimeTickRemaining, AirControlAccel, Hit, bCheckLandingSpot) * subTimeTickRemaining;
759 if (FVector::DotProduct(AirControlDeltaV, OldHitNormal) > 0.f)
761 Delta += (AirControlDeltaV * subTimeTickRemaining);
766 if (subTimeTickRemaining > KINDA_SMALL_NUMBER && !bJustTeleported)
768 const FVector NewVelocity = (Delta / subTimeTickRemaining);
769 Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
773 bool bDitch = ((OldHitImpactNormal.Z > 0.f) && (Hit.ImpactNormal.Z > 0.f) && (FMath::Abs(Delta.Z) <= KINDA_SMALL_NUMBER) && ((Hit.ImpactNormal | OldHitImpactNormal) < 0.f));
774 SafeMoveUpdatedComponent(Delta, PawnRotation,
true, Hit);
778 FVector SideDelta = (OldHitNormal + Hit.ImpactNormal).GetSafeNormal2D();
779 if (SideDelta.IsNearlyZero())
781 SideDelta = FVector(OldHitNormal.Y, -OldHitNormal.X, 0).GetSafeNormal();
783 SafeMoveUpdatedComponent(SideDelta, PawnRotation,
true, Hit);
786 if (bDitch || IsValidLandingSpot(UpdatedComponent->GetComponentLocation(), Hit) || Hit.Time == 0.f)
789 ProcessLanded(Hit, remainingTime, Iterations);
792 else if (GetPerchRadiusThreshold() > 0.f && Hit.Time == 1.f && OldHitImpactNormal.Z >= GetWalkableFloorZ())
795 const FVector PawnLocation = UpdatedComponent->GetComponentLocation();
796 const float ZMovedDist = FMath::Abs(PawnLocation.Z - OldLocation.Z);
797 const float MovedDist2DSq = (PawnLocation - OldLocation).SizeSquared2D();
798 if (ZMovedDist <= 0.2f * timeTick && MovedDist2DSq <= 4.f * timeTick)
800 Velocity.X += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f);
801 Velocity.Y += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f);
802 Velocity.Z = FMath::Max<float>(JumpZVelocity * 0.25f, 1.f);
803 Delta = Velocity * timeTick;
804 SafeMoveUpdatedComponent(Delta, PawnRotation,
true, Hit);
812 if (Velocity.SizeSquared2D() <= KINDA_SMALL_NUMBER * 10.f)
824 if (deltaTime < MIN_TICK_TIME)
829 if (!CharacterOwner || (!CharacterOwner->Controller && !bRunPhysicsWithNoController && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (CharacterOwner->GetLocalRole() != ROLE_SimulatedProxy)))
831 Acceleration = FVector::ZeroVector;
832 Velocity = FVector::ZeroVector;
836 if (!UpdatedComponent->IsQueryCollisionEnabled())
838 SetMovementMode(MOVE_Walking);
842 devCodeSimple(ensureMsgf(!Velocity.ContainsNaN(), TEXT(
"PhysWalking: Velocity contains NaN before Iteration (%s)\n%s"), *GetPathNameSafe(
this), *Velocity.ToString()));
844 bJustTeleported =
false;
845 bool bCheckedFall =
false;
846 bool bTriedLedgeMove =
false;
847 float remainingTime = deltaTime;
852 while ((remainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations) && CharacterOwner && (CharacterOwner->Controller || bRunPhysicsWithNoController || HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocity() || (CharacterOwner->GetLocalRole() == ROLE_SimulatedProxy)))
855 bJustTeleported =
false;
856 const float timeTick = GetSimulationTimeStep(remainingTime, Iterations);
857 remainingTime -= timeTick;
860 UPrimitiveComponent *
const OldBase = GetMovementBase();
861 const FVector PreviousBaseLocation = (OldBase != NULL) ? OldBase->GetComponentLocation() : FVector::ZeroVector;
862 const FVector OldLocation = UpdatedComponent->GetComponentLocation();
863 const FFindFloorResult OldFloor = CurrentFloor;
865 RestorePreAdditiveRootMotionVelocity();
869 MaintainHorizontalGroundVelocity();
870 const FVector OldVelocity = Velocity;
871 Acceleration.Z = 0.f;
874 if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
876 CalcVelocity(timeTick, GroundFriction,
false, BrakingDecelerationWalking);
877 devCodeSimple(ensureMsgf(!Velocity.ContainsNaN(), TEXT(
"PhysWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(
this), *Velocity.ToString()));
880 ApplyRootMotionToVelocity(timeTick);
883 devCodeSimple(ensureMsgf(!Velocity.ContainsNaN(), TEXT(
"PhysWalking: Velocity contains NaN after Root Motion application (%s)\n%s"), *GetPathNameSafe(
this), *Velocity.ToString()));
893 StartNewPhysics(remainingTime + timeTick, Iterations - 1);
899 const FVector MoveVelocity = Velocity;
900 const FVector Delta = (timeTick * MoveVelocity);
902 const bool bZeroDelta = Delta.IsNearlyZero();
903 FStepDownResult StepDownResult;
913 MoveAlongFloor(MoveVelocity, timeTick, &StepDownResult);
918 const float DesiredDist = Delta.Size();
919 if (DesiredDist > KINDA_SMALL_NUMBER)
921 const float ActualDist = (UpdatedComponent->GetComponentLocation() - OldLocation).Size2D();
922 remainingTime += timeTick * (1.f - FMath::Min(1.f, ActualDist / DesiredDist));
924 StartNewPhysics(remainingTime, Iterations);
927 else if (IsSwimming())
929 StartSwimming(OldLocation, OldVelocity, timeTick, remainingTime, Iterations);
936 if (StepDownResult.bComputedFloor)
938 CurrentFloor = StepDownResult.FloorResult;
942 FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, bZeroDelta, NULL);
946 const bool bCheckLedges = !CanWalkOffLedges();
947 if (bCheckLedges && !CurrentFloor.IsWalkableFloor())
950 const FVector GravDir = FVector(0.f, 0.f, -1.f);
951 const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldLocation, Delta, GravDir);
952 if (!NewDelta.IsZero())
955 RevertMove(OldLocation, OldBase, PreviousBaseLocation, OldFloor,
false);
958 bTriedLedgeMove =
true;
961 Velocity = NewDelta / timeTick;
962 remainingTime += timeTick;
969 bool bMustJump = bZeroDelta || (OldBase == NULL || (!OldBase->IsQueryCollisionEnabled() && MovementBaseUtility::IsDynamicBase(OldBase)));
970 if ((bMustJump || !bCheckedFall) && CheckFall(OldFloor, CurrentFloor.HitResult, Delta, OldLocation, remainingTime, timeTick, Iterations, bMustJump))
977 RevertMove(OldLocation, OldBase, PreviousBaseLocation, OldFloor,
true);
985 if (CurrentFloor.IsWalkableFloor())
987 if (ShouldCatchAir(OldFloor, CurrentFloor))
989 HandleWalkingOffLedge(OldFloor.HitResult.ImpactNormal, OldFloor.HitResult.Normal, OldLocation, timeTick);
990 if (IsMovingOnGround())
993 StartFalling(Iterations, remainingTime, timeTick, Delta, OldLocation);
999 SetBase(CurrentFloor.HitResult.Component.Get(), CurrentFloor.HitResult.BoneName);
1001 else if (CurrentFloor.HitResult.bStartPenetrating && remainingTime <= 0.f)
1005 FHitResult Hit(CurrentFloor.HitResult);
1006 Hit.TraceEnd = Hit.TraceStart + FVector(0.f, 0.f, MAX_FLOOR_DIST);
1007 const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
1008 ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
1009 bForceNextFloorCheck =
true;
1015 StartSwimming(OldLocation, Velocity, timeTick, remainingTime, Iterations);
1020 if (!CurrentFloor.IsWalkableFloor() && !CurrentFloor.HitResult.bStartPenetrating)
1022 const bool bMustJump = bJustTeleported || bZeroDelta || (OldBase == NULL || (!OldBase->IsQueryCollisionEnabled() && MovementBaseUtility::IsDynamicBase(OldBase)));
1023 if ((bMustJump || !bCheckedFall) && CheckFall(OldFloor, CurrentFloor.HitResult, Delta, OldLocation, remainingTime, timeTick, Iterations, bMustJump))
1027 bCheckedFall =
true;
1033 if (IsMovingOnGround())
1036 if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && timeTick >= MIN_TICK_TIME)
1039 Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / timeTick;
1040 MaintainHorizontalGroundVelocity();
1045 if (UpdatedComponent->GetComponentLocation() == OldLocation)
1047 remainingTime = 0.f;
1052 if (IsMovingOnGround())
1054 MaintainHorizontalGroundVelocity();
1162 QUICK_SCOPE_CYCLE_COUNTER(VRCharacterMovementServerMove_PerformMovement);
1166 if (!HasValidData() || !IsActive())
1171 bool bAutoAcceptPacket =
false;
1181 ServerData->CurrentClientTimeStamp = MoveData.TimeStamp;
1182 bAutoAcceptPacket =
true;
1186 const float ClientTimeStamp = MoveData.TimeStamp;
1187 FVector_NetQuantize10 ClientAccel = MoveData.Acceleration;
1188 const uint8 ClientMoveFlags = MoveData.CompressedMoveFlags;
1189 const FRotator ClientControlRotation = MoveData.ControlRotation;
1193 const float ServerTimeStamp = ServerData->CurrentClientTimeStamp;
1195 static const auto CVarNetServerMoveTimestampExpiredWarningThreshold = IConsoleManager::Get().FindConsoleVariable(TEXT(
"net.NetServerMoveTimestampExpiredWarningThreshold"));
1196 if (ServerTimeStamp > 1.0f && FMath::Abs(ServerTimeStamp - ClientTimeStamp) > CVarNetServerMoveTimestampExpiredWarningThreshold->GetFloat())
1198 UE_LOG(LogNetPlayerMovement,
Warning, TEXT(
"ServerMove: TimeStamp expired: %f, CurrentTimeStamp: %f, Character: %s"), ClientTimeStamp, ServerTimeStamp, *GetNameSafe(CharacterOwner));
1202 UE_LOG(LogNetPlayerMovement, Log, TEXT(
"ServerMove: TimeStamp expired: %f, CurrentTimeStamp: %f, Character: %s"), ClientTimeStamp, ServerTimeStamp, *GetNameSafe(CharacterOwner));
1212 FVRCharacterScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, bEnableScopedMovementUpdates ? EScopedUpdate::DeferredUpdates : EScopedUpdate::ImmediateUpdates);
1214 bool bServerReadyForClient =
true;
1215 APlayerController* PC = Cast<APlayerController>(CharacterOwner->GetController());
1218 bServerReadyForClient = PC->NotifyServerReceivedClientData(CharacterOwner, ClientTimeStamp);
1219 if (!bServerReadyForClient)
1221 ClientAccel = FVector::ZeroVector;
1225 const UWorld* MyWorld = GetWorld();
1226 const float DeltaTime = ServerData->GetServerMoveDeltaTime(ClientTimeStamp, CharacterOwner->GetActorTimeDilation(*MyWorld));
1228 if (DeltaTime > 0.f)
1230 ServerData->CurrentClientTimeStamp = ClientTimeStamp;
1231 ServerData->ServerAccumulatedClientTimeStamp += DeltaTime;
1232 ServerData->ServerTimeStamp = MyWorld->GetTimeSeconds();
1233 ServerData->ServerTimeStampLastServerMove = ServerData->ServerTimeStamp;
1237 PC->SetControlRotation(ClientControlRotation);
1240 if (!bServerReadyForClient)
1246 if ((MyWorld->GetWorldSettings()->GetPauserPlayerState() == NULL))
1250 PC->UpdateRotation(DeltaTime);
1256 bHasRequestedVelocity =
true;
1274 MoveAutonomous(ClientTimeStamp, DeltaTime, ClientMoveFlags, ClientAccel);
1275 bHasRequestedVelocity =
false;
1278 UE_CLOG(CharacterOwner && UpdatedComponent, LogSimpleCharacterMovement, VeryVerbose, TEXT(
"ServerMove Time %f Acceleration %s Velocity %s Position %s Rotation %s DeltaTime %f Mode %s MovementBase %s.%s (Dynamic:%d)"),
1279 ClientTimeStamp, *ClientAccel.ToString(), *Velocity.ToString(), *UpdatedComponent->GetComponentLocation().ToString(), *UpdatedComponent->GetComponentRotation().ToCompactString(), DeltaTime, *GetMovementName(),
1280 *GetNameSafe(GetMovementBase()), *CharacterOwner->GetBasedMovement().BoneName.ToString(), MovementBaseUtility::IsDynamicBase(GetMovementBase()) ? 1 : 0);
1285 const uint8 CurrentPackedMovementMode = PackNetworkMovementMode();
1286 if (CurrentPackedMovementMode != MoveData.MovementMode)
1288 TEnumAsByte<EMovementMode> NetMovementMode(MOVE_None);
1289 TEnumAsByte<EMovementMode> NetGroundMode(MOVE_None);
1290 uint8 NetCustomMode(0);
1291 UnpackNetworkMovementMode(MoveData.MovementMode, NetMovementMode, NetCustomMode, NetGroundMode);
1294 if (NetMovementMode == EMovementMode::MOVE_Custom || MovementMode == EMovementMode::MOVE_Custom)
1297 SetMovementMode(NetMovementMode, NetCustomMode);
1302 if (MoveData.NetworkMoveType == FCharacterNetworkMoveData::ENetworkMoveType::NewMove)
1304 ServerMoveHandleClientError(ClientTimeStamp, DeltaTime, ClientAccel, MoveData.Location, MoveData.MovementBase, MoveData.MovementBaseBoneName, MoveData.MovementMode);
1311 SCOPE_CYCLE_COUNTER(STAT_CharacterMovementReplicateMoveToServerVRSimple);
1312 check(CharacterOwner != NULL);
1315 APlayerController* PC = Cast<APlayerController>(CharacterOwner->GetController());
1316 if (PC && PC->AcknowledgedPawn != CharacterOwner)
1323 if (PC && PC->Player ==
nullptr)
1335 DeltaTime = ClientData->UpdateTimeStampAndDeltaTime(DeltaTime, *CharacterOwner, *
this);
1340 FSavedMovePtr OldMove = NULL;
1341 if (ClientData->LastAckedMove.IsValid())
1343 const int32 NumSavedMoves = ClientData->SavedMoves.Num();
1344 for (int32 i = 0; i < NumSavedMoves - 1; i++)
1346 const FSavedMovePtr& CurrentMove = ClientData->SavedMoves[i];
1347 if (CurrentMove->IsImportantMove(ClientData->LastAckedMove))
1349 OldMove = CurrentMove;
1356 FSavedMovePtr NewMovePtr = ClientData->CreateSavedMove();
1358 if (NewMove ==
nullptr)
1363 NewMove->SetMoveFor(CharacterOwner, DeltaTime, NewAcceleration, *ClientData);
1364 const UWorld* MyWorld = GetWorld();
1370 if (PendingMove->CanCombineWith(NewMovePtr, CharacterOwner, ClientData->MaxMoveDeltaTime * CharacterOwner->GetActorTimeDilation(*MyWorld)))
1375 const FVector OldStartLocation = PendingMove->GetRevertedLocation();
1376 const bool bAttachedToObject = (NewMovePtr->StartAttachParent !=
nullptr);
1377 if (bAttachedToObject || !OverlapTest(OldStartLocation, PendingMove->StartRotation.Quaternion(), UpdatedComponent->GetCollisionObjectType(), GetPawnCapsuleCollisionShape(SHRINK_None), CharacterOwner))
1386 UE_LOG(LogSimpleCharacterMovement, VeryVerbose, TEXT(
"CombineMove: add delta %f + %f and revert from %f %f to %f %f"), DeltaTime, ClientData->PendingMove->DeltaTime, UpdatedComponent->GetComponentLocation().X, UpdatedComponent->GetComponentLocation().Y, OldStartLocation.X, OldStartLocation.Y);
1388 NewMove->CombineWith(PendingMove, CharacterOwner, PC, OldStartLocation);
1394 CharacterOwner->FaceRotation(PC->GetControlRotation(), NewMove->DeltaTime);
1398 NewMove->SetInitialPosition(CharacterOwner);
1401 if (ClientData->SavedMoves.Num() > 0 && ClientData->SavedMoves.Last() == ClientData->PendingMove)
1403 const bool bAllowShrinking =
false;
1404 ClientData->SavedMoves.Pop(bAllowShrinking);
1406 ClientData->FreeMove(ClientData->PendingMove);
1407 ClientData->PendingMove =
nullptr;
1408 PendingMove =
nullptr;
1412 UE_LOG(LogSimpleCharacterMovement, Verbose, TEXT(
"Not combining move [would collide at start location]"));
1422 Acceleration = NewMove->Acceleration.GetClampedToMaxSize(GetMaxAcceleration());
1423 AnalogInputModifier = ComputeAnalogInputModifier();
1426 CharacterOwner->ClientRootMotionParams.Clear();
1427 CharacterOwner->SavedRootMotion.Clear();
1430 NewMove->PostUpdate(CharacterOwner, FSavedMove_Character::PostUpdate_Record);
1433 if (CharacterOwner->IsReplicatingMovement())
1435 check(NewMove == NewMovePtr.Get());
1436 ClientData->SavedMoves.Push(NewMovePtr);
1439 static const auto CVarNetEnableMoveCombiningVRSimple = IConsoleManager::Get().FindConsoleVariable(TEXT(
"p.NetEnableMoveCombining"));
1440 const bool bCanDelayMove = (CVarNetEnableMoveCombiningVRSimple->GetInt() != 0) && CanDelaySendingMove(NewMovePtr);
1442 if (bCanDelayMove && ClientData->PendingMove.IsValid() ==
false)
1445 const float NetMoveDelta = FMath::Clamp(GetClientNetSendDeltaTime(PC, ClientData, NewMovePtr), 1.f / 120.f, 1.f / 5.f);
1447 if ((MyWorld->TimeSeconds - ClientData->ClientUpdateTime) * MyWorld->GetWorldSettings()->GetEffectiveTimeDilation() < NetMoveDelta)
1450 ClientData->PendingMove = NewMovePtr;
1456 ClientData->ClientUpdateTime = MyWorld->TimeSeconds;
1458 UE_CLOG(CharacterOwner&& UpdatedComponent, LogSimpleCharacterMovement, VeryVerbose, TEXT(
"ClientMove Time %f Acceleration %s Velocity %s Position %s Rotation %s DeltaTime %f Mode %s MovementBase %s.%s (Dynamic:%d) DualMove? %d"),
1459 NewMove->TimeStamp, *NewMove->Acceleration.ToString(), *Velocity.ToString(), *UpdatedComponent->GetComponentLocation().ToString(), *UpdatedComponent->GetComponentRotation().ToCompactString(), NewMove->DeltaTime, *GetMovementName(),
1460 *GetNameSafe(NewMove->EndBase.Get()), *NewMove->EndBoneName.ToString(), MovementBaseUtility::IsDynamicBase(NewMove->EndBase.Get()) ? 1 : 0, ClientData->PendingMove.IsValid() ? 1 : 0);
1462 bool bSendServerMove =
true;
1464#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
1467 const float TimeSinceLossStart = (MyWorld->RealTimeSeconds - ClientData->DebugForcedPacketLossTimerStart);
1468 static const auto CVarNetForceClientServerMoveLossDuration = IConsoleManager::Get().FindConsoleVariable(TEXT(
"p.NetForceClientServerMoveLossDuration"));
1469 static const auto CVarNetForceClientServerMoveLossPercent = IConsoleManager::Get().FindConsoleVariable(TEXT(
"p.NetForceClientServerMoveLossPercent"));
1470 if (ClientData->DebugForcedPacketLossTimerStart > 0.f && (TimeSinceLossStart < CVarNetForceClientServerMoveLossDuration->GetFloat()))
1472 bSendServerMove =
false;
1473 UE_LOG(LogSimpleCharacterMovement, Log, TEXT(
"Drop ServerMove, %.2f time remains"), CVarNetForceClientServerMoveLossDuration->GetFloat() - TimeSinceLossStart);
1475 else if (CVarNetForceClientServerMoveLossPercent->GetFloat() != 0.f && (RandomStream.FRand() < CVarNetForceClientServerMoveLossPercent->GetFloat()))
1477 bSendServerMove =
false;
1478 ClientData->DebugForcedPacketLossTimerStart = (CVarNetForceClientServerMoveLossDuration->GetFloat() > 0) ? MyWorld->RealTimeSeconds : 0.0f;
1479 UE_LOG(LogSimpleCharacterMovement, Log, TEXT(
"Drop ServerMove, %.2f time remains"), CVarNetForceClientServerMoveLossDuration->GetFloat());
1483 ClientData->DebugForcedPacketLossTimerStart = 0.f;
1488 if (bSendServerMove)
1490 SCOPE_CYCLE_COUNTER(STAT_CharacterMovementCallServerMoveVRSimple);
1491 if (ShouldUsePackedMovementRPCs())
1493 CallServerMovePacked(NewMove, ClientData->PendingMove.Get(), OldMove.Get());
1502 ClientData->PendingMove = NULL;