diff --git a/GravityStomp/Content/Characters/BP_GSCharacter.uasset b/GravityStomp/Content/Characters/BP_GSCharacter.uasset index 29d649f..13b7e85 100644 --- a/GravityStomp/Content/Characters/BP_GSCharacter.uasset +++ b/GravityStomp/Content/Characters/BP_GSCharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d914c1a048f668db85b7c63fb9913ba0f5c2f71f4fcdf797e356c65d63c9903 -size 105098 +oid sha256:35e5001ea477718971711cf88b2302fb8c88f318cfb4bbe64295b02f7f75b1d8 +size 105153 diff --git a/GravityStomp/Content/Maps/Debug/Test.umap b/GravityStomp/Content/Maps/Debug/Test.umap index 3e5873f..166ae61 100644 --- a/GravityStomp/Content/Maps/Debug/Test.umap +++ b/GravityStomp/Content/Maps/Debug/Test.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb4b77af4aac3121eea4fd753a468000eb531179dd3f4fd1347b542bda3531cb +oid sha256:075af1c013f7de313b3bb00d98a658d8c6762ee49da224cee009312166abfb78 size 47476 diff --git a/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.cpp b/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.cpp index e684295..e3447ce 100644 --- a/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.cpp +++ b/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.cpp @@ -22,6 +22,13 @@ namespace CharacterMovementCVars ForceJumpPeakSubstep, TEXT("If 1, force a jump substep to always reach the peak position of a jump, which can often be cut off as framerate lowers."), ECVF_Default); + + static bool bAddFormerBaseVelocityToRootMotionOverrideWhenFalling = true; + FAutoConsoleVariableRef CVarAddFormerBaseVelocityToRootMotionOverrideWhenFalling( + TEXT("gs.AddFormerBaseVelocityToRootMotionOverrideWhenFalling"), + bAddFormerBaseVelocityToRootMotionOverrideWhenFalling, + TEXT("To avoid sudden velocity changes when a root motion source moves the pawn from a moving base to free fall, this CVar will enable the FormerBaseVelocityDecayHalfLife property on CharacterMovementComponent."), + ECVF_Default); } @@ -631,9 +638,619 @@ bool UGSCharacterMovementComponent::FloorSweepTest(FHitResult& OutHit, const FVe return bBlockingHit; } +bool UGSCharacterMovementComponent::IsCharacterUpAlignedToWorldUp() const +{ + return FMath::Abs(CharacterUpDirection.Dot(FVector::UpVector)) > UE_KINDA_SMALL_NUMBER; +} + void UGSCharacterMovementComponent::SetCharacterUpDirection(FVector NewUpDirection) { NewUpDirection.Normalize(); CharacterUpDirection = NewUpDirection; } + +void UGSCharacterMovementComponent::OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) +{ + if (!HasValidData()) + { + return; + } + + // Update collision settings if needed + if (MovementMode == MOVE_NavWalking) + { + // Reset cached nav location used by NavWalking + CachedNavLocation = FNavLocation(); + + SetGroundMovementMode(MovementMode); + // Walking is either only X or Z depending on the character's up direction + if(IsCharacterUpAlignedToWorldUp()) + { + Velocity.Z = 0.f; + } + else + { + Velocity.Y = 0.f; + } + SetNavWalkingPhysics(true); + } + else if (PreviousMovementMode == MOVE_NavWalking) + { + if (MovementMode == DefaultLandMovementMode || IsWalking()) + { + const bool bSucceeded = TryToLeaveNavWalking(); + if (!bSucceeded) + { + return; + } + } + else + { + SetNavWalkingPhysics(false); + } + } + + // React to changes in the movement mode. + if (MovementMode == MOVE_Walking) + { + // Walking is either only X or Z depending on the character's up direction + if(IsCharacterUpAlignedToWorldUp()) + { + Velocity.Z = 0.f; + } + else + { + Velocity.Y = 0.f; + } + bCrouchMaintainsBaseLocation = true; + SetGroundMovementMode(MovementMode); + + // make sure we update our new floor/base on initial entry of the walking physics + FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, false); + AdjustFloorHeight(); + SetBaseFromFloor(CurrentFloor); + } + else + { + CurrentFloor.Clear(); + bCrouchMaintainsBaseLocation = false; + + if (MovementMode == MOVE_Falling) + { + DecayingFormerBaseVelocity = GetImpartedMovementBaseVelocity(); + Velocity += DecayingFormerBaseVelocity; + if (bMovementInProgress && CurrentRootMotion.HasAdditiveVelocity()) + { + // If we leave a base during movement and we have additive root motion, we need to add the imparted velocity so that it retains it next tick + CurrentRootMotion.LastPreAdditiveVelocity += DecayingFormerBaseVelocity; + } + if (!CharacterMovementCVars::bAddFormerBaseVelocityToRootMotionOverrideWhenFalling || FormerBaseVelocityDecayHalfLife == 0.f) + { + DecayingFormerBaseVelocity = FVector::ZeroVector; + } + CharacterOwner->Falling(); + } + + SetBase(NULL); + + if (MovementMode == MOVE_None) + { + // Kill velocity and clear queued up events + StopMovementKeepPathing(); + CharacterOwner->ResetJumpState(); + ClearAccumulatedForces(); + } + } + + if (MovementMode == MOVE_Falling && PreviousMovementMode != MOVE_Falling) + { + IPathFollowingAgentInterface* PFAgent = GetPathFollowingAgent(); + if (PFAgent) + { + PFAgent->OnStartedFalling(); + } + } + + CharacterOwner->OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode); + ensureMsgf(GetGroundMovementMode() == MOVE_Walking || GetGroundMovementMode() == MOVE_NavWalking, TEXT("Invalid GroundMovementMode %d. MovementMode: %d, PreviousMovementMode: %d"), GetGroundMovementMode(), MovementMode.GetValue(), PreviousMovementMode); +} + +void UGSCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iterations) +{ + if (deltaTime < MIN_TICK_TIME) + { + return; + } + + if (!CharacterOwner || (!CharacterOwner->Controller && !bRunPhysicsWithNoController && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (CharacterOwner->GetLocalRole() != ROLE_SimulatedProxy))) + { + Acceleration = FVector::ZeroVector; + Velocity = FVector::ZeroVector; + return; + } + + if (!UpdatedComponent->IsQueryCollisionEnabled()) + { + SetMovementMode(MOVE_Walking); + return; + } + + bJustTeleported = false; + bool bCheckedFall = false; + bool bTriedLedgeMove = false; + float remainingTime = deltaTime; + + // Perform the move + while ( (remainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations) && CharacterOwner && (CharacterOwner->Controller || bRunPhysicsWithNoController || HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocity() || (CharacterOwner->GetLocalRole() == ROLE_SimulatedProxy)) ) + { + Iterations++; + bJustTeleported = false; + const float timeTick = GetSimulationTimeStep(remainingTime, Iterations); + remainingTime -= timeTick; + + // Save current values + UPrimitiveComponent * const OldBase = GetMovementBase(); + const FVector PreviousBaseLocation = (OldBase != NULL) ? OldBase->GetComponentLocation() : FVector::ZeroVector; + const FVector OldLocation = UpdatedComponent->GetComponentLocation(); + const FFindFloorResult OldFloor = CurrentFloor; + + RestorePreAdditiveRootMotionVelocity(); + + // Ensure velocity is horizontal. + MaintainHorizontalGroundVelocity(); + const FVector OldVelocity = Velocity; + + if(IsCharacterUpAlignedToWorldUp()) + { + Acceleration.Z = 0.f; + } + else + { + Acceleration.Y = 0.f; + } + + // Apply acceleration + if( !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() ) + { + CalcVelocity(timeTick, GroundFriction, false, GetMaxBrakingDeceleration()); + } + + ApplyRootMotionToVelocity(timeTick); + + if( IsFalling() ) + { + // Root motion could have put us into Falling. + // No movement has taken place this movement tick so we pass on full time/past iteration count + StartNewPhysics(remainingTime+timeTick, Iterations-1); + return; + } + + // Compute move parameters + const FVector MoveVelocity = Velocity; + const FVector Delta = timeTick * MoveVelocity; + const bool bZeroDelta = Delta.IsNearlyZero(); + FStepDownResult StepDownResult; + + if ( bZeroDelta ) + { + remainingTime = 0.f; + } + else + { + // try to move forward + MoveAlongFloor(MoveVelocity, timeTick, &StepDownResult); + + if ( IsFalling() ) + { + // pawn decided to jump up + const float DesiredDist = Delta.Size(); + if (DesiredDist > UE_KINDA_SMALL_NUMBER) + { + const float ActualDist = (UpdatedComponent->GetComponentLocation() - OldLocation).Size2D(); + remainingTime += timeTick * (1.f - FMath::Min(1.f,ActualDist/DesiredDist)); + } + StartNewPhysics(remainingTime,Iterations); + return; + } + else if ( IsSwimming() ) //just entered water + { + StartSwimming(OldLocation, OldVelocity, timeTick, remainingTime, Iterations); + return; + } + } + + // Update floor. + // StepUp might have already done it for us. + if (StepDownResult.bComputedFloor) + { + CurrentFloor = StepDownResult.FloorResult; + } + else + { + FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, bZeroDelta, NULL); + } + + // check for ledges here + const bool bCheckLedges = !CanWalkOffLedges(); + if ( bCheckLedges && !CurrentFloor.IsWalkableFloor() ) + { + // calculate possible alternate movement + const FVector GravDir = FVector(0.f,0.f,-1.f); + const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldLocation, Delta, GravDir); + if ( !NewDelta.IsZero() ) + { + // first revert this move + RevertMove(OldLocation, OldBase, PreviousBaseLocation, OldFloor, false); + + // avoid repeated ledge moves if the first one fails + bTriedLedgeMove = true; + + // Try new movement direction + Velocity = NewDelta/timeTick; + remainingTime += timeTick; + continue; + } + else + { + // see if it is OK to jump + // @todo collision : only thing that can be problem is that oldbase has world collision on + bool bMustJump = bZeroDelta || (OldBase == NULL || (!OldBase->IsQueryCollisionEnabled() && MovementBaseUtility::IsDynamicBase(OldBase))); + if ( (bMustJump || !bCheckedFall) && CheckFall(OldFloor, CurrentFloor.HitResult, Delta, OldLocation, remainingTime, timeTick, Iterations, bMustJump) ) + { + return; + } + bCheckedFall = true; + + // revert this move + RevertMove(OldLocation, OldBase, PreviousBaseLocation, OldFloor, true); + remainingTime = 0.f; + break; + } + } + else + { + // Validate the floor check + if (CurrentFloor.IsWalkableFloor()) + { + if (ShouldCatchAir(OldFloor, CurrentFloor)) + { + HandleWalkingOffLedge(OldFloor.HitResult.ImpactNormal, OldFloor.HitResult.Normal, OldLocation, timeTick); + if (IsMovingOnGround()) + { + // If still walking, then fall. If not, assume the user set a different mode they want to keep. + StartFalling(Iterations, remainingTime, timeTick, Delta, OldLocation); + } + return; + } + + AdjustFloorHeight(); + SetBase(CurrentFloor.HitResult.Component.Get(), CurrentFloor.HitResult.BoneName); + } + else if (CurrentFloor.HitResult.bStartPenetrating && remainingTime <= 0.f) + { + // The floor check failed because it started in penetration + // We do not want to try to move downward because the downward sweep failed, rather we'd like to try to pop out of the floor. + FHitResult Hit(CurrentFloor.HitResult); + Hit.TraceEnd = Hit.TraceStart + CharacterUpDirection * MAX_FLOOR_DIST; + const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit); + ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat()); + bForceNextFloorCheck = true; + } + + // check if just entered water + if ( IsSwimming() ) + { + StartSwimming(OldLocation, Velocity, timeTick, remainingTime, Iterations); + return; + } + + // See if we need to start falling. + if (!CurrentFloor.IsWalkableFloor() && !CurrentFloor.HitResult.bStartPenetrating) + { + const bool bMustJump = bJustTeleported || bZeroDelta || (OldBase == NULL || (!OldBase->IsQueryCollisionEnabled() && MovementBaseUtility::IsDynamicBase(OldBase))); + if ((bMustJump || !bCheckedFall) && CheckFall(OldFloor, CurrentFloor.HitResult, Delta, OldLocation, remainingTime, timeTick, Iterations, bMustJump) ) + { + return; + } + bCheckedFall = true; + } + } + + + // Allow overlap events and such to change physics state and velocity + if (IsMovingOnGround()) + { + // Make velocity reflect actual move + if( !bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && timeTick >= MIN_TICK_TIME) + { + // TODO-RootMotionSource: Allow this to happen during partial override Velocity, but only set allowed axes? + Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / timeTick; + MaintainHorizontalGroundVelocity(); + } + } + + // If we didn't move at all this iteration then abort (since future iterations will also be stuck). + if (UpdatedComponent->GetComponentLocation() == OldLocation) + { + remainingTime = 0.f; + break; + } + } + + if (IsMovingOnGround()) + { + MaintainHorizontalGroundVelocity(); + } +} + +void UGSCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, float DeltaSeconds, + FStepDownResult* OutStepDownResult) +{ + if (!CurrentFloor.IsWalkableFloor()) + { + return; + } + + // Move along the current floor + FVector MovementAxis = CharacterUpDirection.Cross(FVector::ForwardVector).GetAbs(); + FString DebugAxisMsg = FString::Printf(TEXT("Movement Axis: %s"), *MovementAxis.ToString()); + GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Cyan, DebugAxisMsg); + const FVector Delta = (MovementAxis * InVelocity) * DeltaSeconds; + + FString DebugVelMsg = FString::Printf(TEXT("Move Along Floor Velocity: %s"), *(MovementAxis * InVelocity).ToString()); + GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Cyan, DebugVelMsg); + + FHitResult Hit(1.f); + FVector RampVector = ComputeGroundMovementDelta(Delta, CurrentFloor.HitResult, CurrentFloor.bLineTrace); + SafeMoveUpdatedComponent(RampVector, UpdatedComponent->GetComponentQuat(), true, Hit); + float LastMoveTimeSlice = DeltaSeconds; + + if (Hit.bStartPenetrating) + { + // Allow this hit to be used as an impact we can deflect off, otherwise we do nothing the rest of the update and appear to hitch. + HandleImpact(Hit); + SlideAlongSurface(Delta, 1.f, Hit.Normal, Hit, true); + + if (Hit.bStartPenetrating) + { + OnCharacterStuckInGeometry(&Hit); + } + } + else if (Hit.IsValidBlockingHit()) + { + // We impacted something (most likely another ramp, but possibly a barrier). + float PercentTimeApplied = Hit.Time; + if ((Hit.Time > 0.f) && (IsCharacterUpAlignedToWorldUp() ? Hit.Normal.Z: Hit.Normal.Y > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit)) + { + // Another walkable ramp. + const float InitialPercentRemaining = 1.f - PercentTimeApplied; + RampVector = ComputeGroundMovementDelta(Delta * InitialPercentRemaining, Hit, false); + LastMoveTimeSlice = InitialPercentRemaining * LastMoveTimeSlice; + SafeMoveUpdatedComponent(RampVector, UpdatedComponent->GetComponentQuat(), true, Hit); + + const float SecondHitPercent = Hit.Time * InitialPercentRemaining; + PercentTimeApplied = FMath::Clamp(PercentTimeApplied + SecondHitPercent, 0.f, 1.f); + } + + if (Hit.IsValidBlockingHit()) + { + if (CanStepUp(Hit) || (CharacterOwner->GetMovementBase() != nullptr && Hit.HitObjectHandle == CharacterOwner->GetMovementBase()->GetOwner())) + { + // hit a barrier, try to step up + const FVector PreStepUpLocation = UpdatedComponent->GetComponentLocation(); + const FVector GravDir = -CharacterUpDirection; + if (!StepUp(GravDir, Delta * (1.f - PercentTimeApplied), Hit, OutStepDownResult)) + { + //UE_LOG(LogCharacterMovement, Verbose, TEXT("- StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString()); + HandleImpact(Hit, LastMoveTimeSlice, RampVector); + SlideAlongSurface(Delta, 1.f - PercentTimeApplied, Hit.Normal, Hit, true); + } + else + { + //UE_LOG(LogCharacterMovement, Verbose, TEXT("+ StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString()); + if (!bMaintainHorizontalGroundVelocity) + { + // Don't recalculate velocity based on this height adjustment, if considering vertical adjustments. Only consider horizontal movement. + bJustTeleported = true; + const float StepUpTimeSlice = (1.f - PercentTimeApplied) * DeltaSeconds; + if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && StepUpTimeSlice >= UE_KINDA_SMALL_NUMBER) + { + Velocity = (UpdatedComponent->GetComponentLocation() - PreStepUpLocation) / StepUpTimeSlice; + if(IsCharacterUpAlignedToWorldUp()) + { + Velocity.Z = 0; + } + else + { + Velocity.Y = 0; + } + } + } + } + } + else if ( Hit.Component.IsValid() && !Hit.Component.Get()->CanCharacterStepUp(CharacterOwner) ) + { + HandleImpact(Hit, LastMoveTimeSlice, RampVector); + SlideAlongSurface(Delta, 1.f - PercentTimeApplied, Hit.Normal, Hit, true); + } + } + } +} + +FVector UGSCharacterMovementComponent::ComputeGroundMovementDelta(const FVector& Delta, const FHitResult& RampHit, + const bool bHitFromLineTrace) const +{ + /* + const FVector FloorNormal = RampHit.ImpactNormal; + const FVector ContactNormal = RampHit.Normal; + + if (FloorNormal.Z < (1.f - UE_KINDA_SMALL_NUMBER) && FloorNormal.Z > UE_KINDA_SMALL_NUMBER && ContactNormal.Z > UE_KINDA_SMALL_NUMBER && !bHitFromLineTrace && IsWalkable(RampHit)) + { + // Compute a vector that moves parallel to the surface, by projecting the horizontal movement direction onto the ramp. + const float FloorDotDelta = (FloorNormal | Delta); + FVector RampMovement(Delta.X, Delta.Y, -FloorDotDelta / FloorNormal.Z); + + if (bMaintainHorizontalGroundVelocity) + { + return RampMovement; + } + else + { + return RampMovement.GetSafeNormal() * Delta.Size(); + } + } + + */ + + return Delta; +} + + +void UGSCharacterMovementComponent::MaintainHorizontalGroundVelocity() +{ + if (IsCharacterUpAlignedToWorldUp()) + { + if(Velocity.Z != 0.f) + { + if (bMaintainHorizontalGroundVelocity) + { + // Ramp movement already maintained the velocity, so we just want to remove the vertical component. + Velocity.Z = 0.f; + } + else + { + // Rescale velocity to be horizontal but maintain magnitude of last update. + Velocity = Velocity.GetSafeNormal2D() * Velocity.Size(); + } + } + } + else + { + if(Velocity.Y != 0.f) + { + if (bMaintainHorizontalGroundVelocity) + { + // Ramp movement already maintained the velocity, so we just want to remove the vertical component. + Velocity.Y = 0.f; + } + else + { + FVector NewVelocity = Velocity.GetSafeNormal(); + Velocity = FVector(0.0f, NewVelocity.Y, NewVelocity.Z) * Velocity.Size(); + } + } + } +} + + +float UGSCharacterMovementComponent::SlideAlongSurface(const FVector& Delta, float Time, const FVector& InNormal, + FHitResult& Hit, bool bHandleImpact) +{ + if (!Hit.bBlockingHit) + { + return 0.f; + } + + bool IsUp = IsCharacterUpAlignedToWorldUp(); + + FVector Normal(InNormal); + float ZComp = Normal.Dot(CharacterUpDirection); + if (IsMovingOnGround()) + { + // We don't want to be pushed up an unwalkable surface. + if (ZComp > 0.f) + { + if (!IsWalkable(Hit)) + { + FVector NewNormal = Normal.GetSafeNormal(); + Normal = IsUp ? FVector(NewNormal.X, NewNormal.Y, 0.0f) : FVector(0.0f, NewNormal.Y, NewNormal.Z); + } + } + else if (ZComp < -UE_KINDA_SMALL_NUMBER) + { + // Don't push down into the floor when the impact is on the upper portion of the capsule. + if (CurrentFloor.FloorDist < MIN_FLOOR_DIST && CurrentFloor.bBlockingHit) + { + const FVector FloorNormal = CurrentFloor.HitResult.Normal; + float FloorZComp = FloorNormal.Dot(CharacterUpDirection); + const bool bFloorOpposedToMovement = (Delta | FloorNormal) < 0.f && (FloorZComp < 1.f - UE_DELTA); + if (bFloorOpposedToMovement) + { + Normal = FloorNormal; + } + + FVector NewNormal = Normal.GetSafeNormal(); + Normal = IsUp ? FVector(NewNormal.X, NewNormal.Y, 0.0f) : FVector(0.0f, NewNormal.Y, NewNormal.Z); + } + } + } + + return Super::SlideAlongSurface(Delta, Time, Normal, Hit, bHandleImpact); +} + +void UGSCharacterMovementComponent::AdjustFloorHeight() +{ + bool IsUp = IsCharacterUpAlignedToWorldUp(); + + // If we have a floor check that hasn't hit anything, don't adjust height. + if (!CurrentFloor.IsWalkableFloor()) + { + return; + } + + float OldFloorDist = CurrentFloor.FloorDist; + if (CurrentFloor.bLineTrace) + { + // This would cause us to scale unwalkable walls + if (OldFloorDist < MIN_FLOOR_DIST && CurrentFloor.LineDist >= MIN_FLOOR_DIST) + { + return; + } + else + { + // Falling back to a line trace means the sweep was unwalkable (or in penetration). Use the line distance for the vertical adjustment. + OldFloorDist = CurrentFloor.LineDist; + } + } + + // Move up or down to maintain floor height. + if (OldFloorDist < MIN_FLOOR_DIST || OldFloorDist > MAX_FLOOR_DIST) + { + FHitResult AdjustHit(1.f); + const float InitialZ = IsUp ? UpdatedComponent->GetComponentLocation().Z : UpdatedComponent->GetComponentLocation().Y; + const float AvgFloorDist = (MIN_FLOOR_DIST + MAX_FLOOR_DIST) * 0.5f; + const float MoveDist = AvgFloorDist - OldFloorDist; + SafeMoveUpdatedComponent(CharacterUpDirection * MoveDist, UpdatedComponent->GetComponentQuat(), true, AdjustHit ); + + if (!AdjustHit.IsValidBlockingHit()) + { + CurrentFloor.FloorDist += MoveDist; + } + else if (MoveDist > 0.f) + { + const float CurrentZ = IsUp ? UpdatedComponent->GetComponentLocation().Z : UpdatedComponent->GetComponentLocation().Y; + CurrentFloor.FloorDist += CurrentZ - InitialZ; + } + else + { + checkSlow(MoveDist < 0.f); + const float CurrentZ = IsUp ? UpdatedComponent->GetComponentLocation().Z : UpdatedComponent->GetComponentLocation().Y; + CurrentFloor.FloorDist = CurrentZ - IsUp ? AdjustHit.Location.Z : AdjustHit.Location.Y; + if (IsWalkable(AdjustHit)) + { + CurrentFloor.SetFromSweep(AdjustHit, CurrentFloor.FloorDist, true); + } + } + + // Don't recalculate velocity based on this height adjustment, if considering vertical adjustments. + // Also avoid it if we moved out of penetration + bJustTeleported |= !bMaintainHorizontalGroundVelocity || (OldFloorDist < 0.f); + + // If something caused us to adjust our height (especially a depentration) we should ensure another check next frame or we will keep a stale result. + if (CharacterOwner && CharacterOwner->GetLocalRole() != ROLE_SimulatedProxy) + { + bForceNextFloorCheck = true; + } + } +} diff --git a/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.h b/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.h index 90cec3d..e027b33 100644 --- a/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.h +++ b/GravityStomp/Source/GravityStompGame/Character/GSCharacterMovementComponent.h @@ -23,6 +23,23 @@ public: void SetCharacterUpDirection(FVector NewUpDirection); // UCharacterMovementComponent interface + + virtual void OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) override; + + // walking + virtual void PhysWalking(float deltaTime, int32 Iterations) override; + + virtual void MoveAlongFloor(const FVector& InVelocity, float DeltaSeconds, FStepDownResult* OutStepDownResult) override; + + virtual FVector ComputeGroundMovementDelta(const FVector& Delta, const FHitResult& RampHit, const bool bHitFromLineTrace) const override; + + virtual void MaintainHorizontalGroundVelocity() override; + + virtual float SlideAlongSurface(const FVector& Delta, float Time, const FVector& InNormal, FHitResult& Hit, bool bHandleImpact) override; + + virtual void AdjustFloorHeight() override; + + // falling virtual void PhysFalling(float deltaTime, int32 Iterations) override; virtual void ComputeFloorDist(const FVector& CapsuleLocation, float LineDistance, float SweepDistance, FFindFloorResult& OutFloorResult, float SweepRadius, const FHitResult* DownwardSweepResult) const override; @@ -36,4 +53,6 @@ public: private: FVector CharacterUpDirection = FVector::UpVector; + + FORCEINLINE bool IsCharacterUpAlignedToWorldUp() const; };