float UMovementComponent::SlideAlongSurface(const FVector& Delta, float Time, const FVector& Normal, FHitResult& Hit, bool bHandleImpact) { if (!Hit.bBlockingHit) { return 0.f; } float PercentTimeApplied = 0.f; const FVector OldHitNormal = Normal; FVector SlideDelta = ComputeSlideVector(Delta, Time, Normal, Hit); if ((SlideDelta | Delta) > 0.f) { const FQuat Rotation = UpdatedComponent->GetComponentQuat(); SafeMoveUpdatedComponent(SlideDelta, Rotation, true, Hit); const float FirstHitPercent = Hit.Time; PercentTimeApplied = FirstHitPercent; if (Hit.IsValidBlockingHit()) { // Notify first impact if (bHandleImpact) { HandleImpact(Hit, FirstHitPercent * Time, SlideDelta); } // Compute new slide normal when hitting multiple surfaces. TwoWallAdjust(SlideDelta, Hit, OldHitNormal); // Only proceed if the new direction is of significant length and not in reverse of original attempted move. if (!SlideDelta.IsNearlyZero(1e-3f) && (SlideDelta | Delta) > 0.f) { // Perform second move SafeMoveUpdatedComponent(SlideDelta, Rotation, true, Hit); const float SecondHitPercent = Hit.Time * (1.f - FirstHitPercent); PercentTimeApplied += SecondHitPercent; // Notify second impact if (bHandleImpact && Hit.bBlockingHit) { HandleImpact(Hit, SecondHitPercent * Time, SlideDelta); } } } return FMath::Clamp(PercentTimeApplied, 0.f, 1.f); } return 0.f; }
void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // some amazing coding here // Make sure that every thing is still valid and we are allowed to move if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)) { return; } // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f)*DeltaTime*150.0f; if (!DesiredMovementThisFrame.IsNearlyZero()) { FHitResult Hit; SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit); // If we bumped into something, try to slide along it // this check is important because otherwise the pawn will simply stop /* if (Hit.IsValidBlockingHit()) { SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit); }*/ } }
bool UProjectileMovementComponent::HandleSliding(FHitResult& Hit, float& SubTickTimeRemaining) { FHitResult InitialHit(Hit); const FVector OldHitNormal = ConstrainDirectionToPlane(Hit.Normal); // Velocity is now parallel to the impact surface. // Perform the move now, before adding gravity/accel again, so we don't just keep hitting the surface. SafeMoveUpdatedComponent(Velocity * SubTickTimeRemaining, UpdatedComponent->GetComponentQuat(), true, Hit); if (HasStoppedSimulation()) { return false; } // A second hit can deflect the velocity (through the normal bounce code), for the next iteration. if (Hit.bBlockingHit) { const float TimeTick = SubTickTimeRemaining; SubTickTimeRemaining = TimeTick * (1.f - Hit.Time); if (HandleBlockingHit(Hit, TimeTick, Velocity * TimeTick, SubTickTimeRemaining) == EHandleBlockingHitResult::Abort || HasStoppedSimulation()) { return false; } } else { // Find velocity after elapsed time const FVector PostTickVelocity = ComputeVelocity(Velocity, SubTickTimeRemaining); // If pointing back into surface, apply friction and acceleration. const FVector Force = (PostTickVelocity - Velocity); const float ForceDotN = (Force | OldHitNormal); if (ForceDotN < 0.f) { const FVector ProjectedForce = FVector::VectorPlaneProject(Force, OldHitNormal); const FVector NewVelocity = Velocity + ProjectedForce; const FVector FrictionForce = -NewVelocity.GetSafeNormal() * FMath::Min(-ForceDotN * Friction, NewVelocity.Size()); Velocity = ConstrainDirectionToPlane(NewVelocity + FrictionForce); } else { Velocity = PostTickVelocity; } // Check min velocity if (Velocity.SizeSquared() < FMath::Square(BounceVelocityStopSimulatingThreshold)) { StopSimulating(InitialHit); return false; } SubTickTimeRemaining = 0.f; } return true; }
void UFloatingPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)) { return; } const AController* Controller = PawnOwner->GetController(); if (Controller && Controller->IsLocalController()) { ApplyControlInputToVelocity(DeltaTime); LimitWorldBounds(); bPositionCorrected = false; // Move actor FVector Delta = Velocity * DeltaTime; if (!Delta.IsNearlyZero(1e-6f)) { const FVector OldLocation = UpdatedComponent->GetComponentLocation(); const FRotator Rotation = UpdatedComponent->GetComponentRotation(); FVector TraceStart = OldLocation; FHitResult Hit(1.f); SafeMoveUpdatedComponent(Delta, Rotation, true, Hit); if (Hit.IsValidBlockingHit()) { HandleImpact(Hit, DeltaTime, Delta); // Try to slide the remaining distance along the surface. SlideAlongSurface(Delta, 1.f-Hit.Time, Hit.Normal, Hit, true); } // Update velocity // We don't want position changes to vastly reverse our direction (which can happen due to penetration fixups etc) if (!bPositionCorrected) { const FVector NewLocation = UpdatedComponent->GetComponentLocation(); Velocity = ((NewLocation - OldLocation) / DeltaTime); } } // Finalize UpdateComponentVelocity(); } };
void UProjectileMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { QUICK_SCOPE_CYCLE_COUNTER( STAT_ProjectileMovementComponent_TickComponent ); // skip if don't want component updated when not rendered or updated component can't move if (HasStoppedSimulation() || ShouldSkipUpdate(DeltaTime)) { return; } Super::TickComponent(DeltaTime, TickType, ThisTickFunction); AActor* ActorOwner = UpdatedComponent->GetOwner(); if ( !ActorOwner || !CheckStillInWorld() ) { return; } if (UpdatedComponent->IsSimulatingPhysics()) { return; } float RemainingTime = DeltaTime; uint32 NumBounces = 0; int32 Iterations = 0; FHitResult Hit(1.f); while( RemainingTime >= MIN_TICK_TIME && (Iterations < MaxSimulationIterations) && !ActorOwner->IsPendingKill() && !HasStoppedSimulation() ) { Iterations++; // subdivide long ticks to more closely follow parabolic trajectory const float TimeTick = ShouldUseSubStepping() ? GetSimulationTimeStep(RemainingTime, Iterations) : RemainingTime; RemainingTime -= TimeTick; Hit.Time = 1.f; const FVector OldVelocity = Velocity; const FVector MoveDelta = ComputeMoveDelta(OldVelocity, TimeTick); const FRotator NewRotation = (bRotationFollowsVelocity && !OldVelocity.IsNearlyZero(0.01f)) ? OldVelocity.Rotation() : ActorOwner->GetActorRotation(); // Move the component if (bShouldBounce) { // If we can bounce, we are allowed to move out of penetrations, so use SafeMoveUpdatedComponent which does that automatically. SafeMoveUpdatedComponent( MoveDelta, NewRotation, true, Hit ); } else { // If we can't bounce, then we shouldn't adjust if initially penetrating, because that should be a blocking hit that causes a hit event and stop simulation. TGuardValue<EMoveComponentFlags> ScopedFlagRestore(MoveComponentFlags, MoveComponentFlags | MOVECOMP_NeverIgnoreBlockingOverlaps); MoveUpdatedComponent(MoveDelta, NewRotation, true, &Hit ); } // If we hit a trigger that destroyed us, abort. if( ActorOwner->IsPendingKill() || HasStoppedSimulation() ) { return; } // Handle hit result after movement if( !Hit.bBlockingHit ) { PreviousHitTime = 1.f; bIsSliding = false; // Only calculate new velocity if events didn't change it during the movement update. if (Velocity == OldVelocity) { Velocity = ComputeVelocity(Velocity, TimeTick); } } else { // Only calculate new velocity if events didn't change it during the movement update. if (Velocity == OldVelocity) { // re-calculate end velocity for partial time Velocity = (Hit.Time > KINDA_SMALL_NUMBER) ? ComputeVelocity(OldVelocity, TimeTick * Hit.Time) : OldVelocity; } // Handle blocking hit float SubTickTimeRemaining = TimeTick * (1.f - Hit.Time); const EHandleBlockingHitResult HandleBlockingResult = HandleBlockingHit(Hit, TimeTick, MoveDelta, SubTickTimeRemaining); if (HandleBlockingResult == EHandleBlockingHitResult::Abort || HasStoppedSimulation()) { break; } else if (HandleBlockingResult == EHandleBlockingHitResult::Deflect) { NumBounces++; HandleDeflection(Hit, OldVelocity, NumBounces, SubTickTimeRemaining); PreviousHitTime = Hit.Time; PreviousHitNormal = ConstrainNormalToPlane(Hit.Normal); } else if (HandleBlockingResult == EHandleBlockingHitResult::AdvanceNextSubstep) { // Reset deflection logic to ignore this hit PreviousHitTime = 1.f; } else { // Unhandled EHandleBlockingHitResult checkNoEntry(); } // A few initial bounces should add more time and iterations to complete most of the simulation. if (NumBounces <= 2 && SubTickTimeRemaining >= MIN_TICK_TIME) { RemainingTime += SubTickTimeRemaining; Iterations--; } } } UpdateComponentVelocity(); }
bool UMovementComponent::K2_MoveUpdatedComponent(FVector Delta, FRotator NewRotation, FHitResult& OutHit, bool bSweep, bool bTeleport) { return SafeMoveUpdatedComponent(Delta, NewRotation.Quaternion(), bSweep, OutHit, TeleportFlagToEnum(bTeleport)); }
void UInterpToMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { QUICK_SCOPE_CYCLE_COUNTER(STAT_InterpToMovementComponent_TickComponent); Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // skip if don't want component updated when not rendered or updated component can't move if (!UpdatedComponent || ShouldSkipUpdate(DeltaTime)) { return; } AActor* ActorOwner = UpdatedComponent->GetOwner(); if (!ActorOwner || !CheckStillInWorld()) { return; } if (UpdatedComponent->IsSimulatingPhysics()) { return; } if((bStopped == true ) || ( ActorOwner->IsPendingKill() ) ) { return; } if( ControlPoints.Num()== 0 ) { return; } // This will update any control points coordinates that are linked to actors. UpdateControlPoints(false); float RemainingTime = DeltaTime; int32 NumBounces = 0; int32 Iterations = 0; FHitResult Hit(1.f); FVector WaitPos = FVector::ZeroVector; if (bIsWaiting == true) { WaitPos = UpdatedComponent->GetComponentLocation(); } while (RemainingTime >= MIN_TICK_TIME && (Iterations < MaxSimulationIterations) && !ActorOwner->IsPendingKill() && UpdatedComponent) { Iterations++; const float TimeTick = ShouldUseSubStepping() ? GetSimulationTimeStep(RemainingTime, Iterations) : RemainingTime; RemainingTime -= TimeTick; // Calculate the current time with this tick iteration float Time = FMath::Clamp(CurrentTime + ((DeltaTime*TimeMultiplier)*CurrentDirection),0.0f,1.0f); FVector MoveDelta = ComputeMoveDelta(Time); // Update the rotation on the spline if required FRotator CurrentRotation = UpdatedComponent->GetComponentRotation(); // Move the component if ((bPauseOnImpact == false ) && (BehaviourType != EInterpToBehaviourType::OneShot)) { // If we can bounce, we are allowed to move out of penetrations, so use SafeMoveUpdatedComponent which does that automatically. SafeMoveUpdatedComponent(MoveDelta, CurrentRotation, true, Hit); } else { // If we can't bounce, then we shouldn't adjust if initially penetrating, because that should be a blocking hit that causes a hit event and stop simulation. TGuardValue<EMoveComponentFlags> ScopedFlagRestore(MoveComponentFlags, MoveComponentFlags | MOVECOMP_NeverIgnoreBlockingOverlaps); MoveUpdatedComponent(MoveDelta, CurrentRotation, true, &Hit); } //DrawDebugPoint(GetWorld(), UpdatedComponent->GetComponentLocation(), 16, FColor::White,true,5.0f); // If we hit a trigger that destroyed us, abort. if (ActorOwner->IsPendingKill() || !UpdatedComponent) { return; } // Handle hit result after movement if (!Hit.bBlockingHit) { // If we were 'waiting' were not any more - broadcast we are off again if( bIsWaiting == true ) { OnWaitEndDelegate.Broadcast(Hit, Time); bIsWaiting = false; } else { CalculateNewTime(CurrentTime, TimeTick, Hit, true, bStopped); if (bStopped == true) { return; } } } else { if (HandleHitWall(Hit, TimeTick, MoveDelta)) { break; } NumBounces++; float SubTickTimeRemaining = TimeTick * (1.f - Hit.Time); // A few initial bounces should add more time and iterations to complete most of the simulation. if (NumBounces <= 2 && SubTickTimeRemaining >= MIN_TICK_TIME) { RemainingTime += SubTickTimeRemaining; Iterations--; } } } if( bIsWaiting == false ) { FHitResult DummyHit; CurrentTime = CalculateNewTime(CurrentTime, DeltaTime, DummyHit, false, bStopped); } UpdateComponentVelocity(); }
void UShardsMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); FHitResult HitResult; FHitResult ShapeTraceResult; FCollisionShape shape = FCollisionShape::MakeBox(FVector(0.72f*playerradius, 0.72f*playerradius,10.0f)); FCollisionQueryParams Params; Params.bFindInitialOverlaps = true; Params.AddIgnoredActor(UpdatedComponent->GetAttachmentRootActor()); // Telepads don't count. for (TActorIterator<ATelePad> ActorItr(GetWorld()); ActorItr; ++ActorItr) { Params.AddIgnoredActor(ActorItr.operator->()); } // Neither do destructibles. for (TActorIterator<ADestructibleBox> ActorItr(GetWorld()); ActorItr; ++ActorItr) { // When they're broken, that is. if (ActorItr->fadetimer >= 0.0f) { Params.AddIgnoredActor(ActorItr.operator->()); } } if (isclimbing) { return; } TArray<FHitResult> results; if (forceregiondirection.Z == 0.0f) { GetWorld()->SweepMultiByChannel(results, UpdatedComponent->GetComponentLocation() - 0.0f*45.0f*FVector::UpVector, UpdatedComponent->GetComponentLocation() - 1000.0f*FVector::UpVector, FQuat::Identity, ECC_Visibility, shape, Params); //100 for (FHitResult r : results) { if (r.Normal.Z > 0.6f) { ShapeTraceResult = r; break; } } if (!ShapeTraceResult.IsValidBlockingHit()) { GetWorld()->LineTraceSingleByChannel(ShapeTraceResult, UpdatedComponent->GetComponentLocation(), UpdatedComponent->GetComponentLocation() - 1000.0f*FVector::UpVector, ECC_Visibility, Params); } } FVector PlayerCapsuleBottom = UpdatedComponent->GetComponentLocation() - 45.0f * FVector::UpVector; // 50 float RequiredDistance = (onground ? 50.0f*grounddetecttfudgefactor : 10.0f)*FMath::Pow(playerhalfheight / 90.0f,4.0f) + playerhalfheight/2.0f; //50,1 DistanceFromImpact = (PlayerCapsuleBottom - ShapeTraceResult.ImpactPoint).Z; overground = ShapeTraceResult.IsValidBlockingHit(); FHitResult groundhitresult; GetWorld()->LineTraceSingleByChannel(groundhitresult, UpdatedComponent->GetComponentLocation(), UpdatedComponent->GetComponentLocation() - 10000.0f*FVector::UpVector, ECC_Visibility, Params); groundtracehit = groundhitresult.ImpactPoint; if (!onground) { offGroundTime += DeltaTime; } toosteep = false; if (enforcementtimer >= 0.0f) { enforcementtimer += DeltaTime; toosteep = true; } wasonground = onground; onground = false; prevgroundvelocity = groundvelocity; groundvelocity = FVector::ZeroVector; platformangularfrequency = 0.0f; platformspindir = 1; FloorNormal = FVector::ZeroVector; if ((enforcementtimer < timerlimit && ShapeTraceResult.Normal.Z>0.6f) && DistanceFromImpact < RequiredDistance && !justjumped) { // (PlayerVelocity.Z <= 0.0f || wasonground) if (ShapeTraceResult.Normal.Z < minnormalz) { if (enforcementtimer == -1.0f) { enforcementtimer = 0.0f; } } else { enforcementtimer = -1.0f; } FVector pvel; // Handle moving platforms. if (ShapeTraceResult.GetActor() != nullptr && ShapeTraceResult.GetComponent() != nullptr && ShapeTraceResult.GetComponent()->IsA(UStaticMeshComponent::StaticClass())) { // The motion of a point on a rigid body is the combination of its motion about the center of mass... FVector angvel = FMath::DegreesToRadians((((UStaticMeshComponent*)ShapeTraceResult.GetComponent())->GetPhysicsAngularVelocity())); FVector rr = GetActorLocation() - (((UStaticMeshComponent*)ShapeTraceResult.GetComponent())->GetComponentLocation()); FVector rvel = FVector::CrossProduct(angvel, rr); // ...and the motion of the center of mass itself. FVector cmvel = (((UStaticMeshComponent*)ShapeTraceResult.GetComponent())->GetPhysicsLinearVelocity()); groundvelocity = rvel + cmvel; platformangularfrequency = -angvel.Z; } if ((PlayerVelocity.Z <= groundvelocity.Z || wasonground)) { onground = true; offGroundTime = 0.0f; } } justjumped = false; bool TraceBlocked; FVector newlocation = UpdatedComponent->GetComponentLocation(); FHitResult TraceHitResult; TraceBlocked = GetWorld()->LineTraceSingleByChannel(TraceHitResult, ShapeTraceResult.ImpactPoint + 1.0f*FVector::UpVector, ShapeTraceResult.ImpactPoint - 10.0f*FVector::UpVector, ECC_Visibility,Params); if (TraceHitResult.Normal.Z > minnormalz) { enforcementtimer = -1.0f; } if (onground) { if (TraceBlocked) { newlocation.Z = TraceHitResult.ImpactPoint.Z + playerhalfheight; // 50 GetWorld()->LineTraceSingleByChannel(TraceHitResult, ShapeTraceResult.ImpactPoint + 1.0f*FVector::UpVector, ShapeTraceResult.ImpactPoint - 10.0f*FVector::UpVector, ECC_Visibility,Params); FloorNormal = TraceHitResult.ImpactNormal; } SafeMoveUpdatedComponent(newlocation - UpdatedComponent->GetComponentLocation(), UpdatedComponent->GetComponentRotation(), true, HitResult); SlideAlongSurface(newlocation - UpdatedComponent->GetComponentLocation(), 1.0 - HitResult.Time, HitResult.Normal, HitResult); } };
void UArchVisCharMovementComponent::PhysWalking(float DeltaTime, int32 Iterations) { // let character do its thing for translation Super::PhysWalking(DeltaTime, Iterations); // // now we will handle rotation // // update yaw if (CurrentRotInput.Yaw == 0) { // decelerate to 0 if (CurrentRotationalVelocity.Yaw > 0.f) { CurrentRotationalVelocity.Yaw -= RotationalDeceleration.Yaw * DeltaTime; CurrentRotationalVelocity.Yaw = FMath::Max(CurrentRotationalVelocity.Yaw, 0.f); // don't go below 0 because that would be re-accelerating the other way } else { CurrentRotationalVelocity.Yaw += RotationalDeceleration.Yaw * DeltaTime; CurrentRotationalVelocity.Yaw = FMath::Min(CurrentRotationalVelocity.Yaw, 0.f); // don't go above 0 because that would be re-accelerating the other way } } else { // accelerate in desired direction // clamp delta so it won't take us outside the acceptable speed range // note that if we're already out of that range, we won't clamp back float const MaxYawVelMag = FMath::Min(1.f, FMath::Abs(CurrentRotInput.Yaw)) * MaxRotationalVelocity.Yaw; float const MaxDeltaYawVel = FMath::Max(0.f, MaxYawVelMag - CurrentRotationalVelocity.Yaw); float const MinDeltaYawVel = FMath::Min(0.f, -(CurrentRotationalVelocity.Yaw + MaxYawVelMag)); float const DeltaYaw = FMath::Clamp(CurrentRotInput.Yaw * RotationalAcceleration.Yaw * DeltaTime, MinDeltaYawVel, MaxDeltaYawVel); CurrentRotationalVelocity.Yaw += DeltaYaw; } // update pitch if (CurrentRotInput.Pitch == 0) { // decelerate to 0 if (CurrentRotationalVelocity.Pitch > 0.f) { CurrentRotationalVelocity.Pitch -= RotationalDeceleration.Pitch * DeltaTime; CurrentRotationalVelocity.Pitch = FMath::Max(CurrentRotationalVelocity.Pitch, 0.f); // don't go below 0 because that would be reaccelerating the other way } else { CurrentRotationalVelocity.Pitch += RotationalDeceleration.Pitch * DeltaTime; CurrentRotationalVelocity.Pitch = FMath::Min(CurrentRotationalVelocity.Pitch, 0.f); // don't go above 0 because that would be reaccelerating the other way } } else { float const MaxPitchVelMag = FMath::Min(1.f, FMath::Abs(CurrentRotInput.Pitch)) * MaxRotationalVelocity.Pitch; float const MaxDeltaPitchVel = FMath::Max(0.f, MaxPitchVelMag - CurrentRotationalVelocity.Pitch); float const MinDeltaPitchVel = FMath::Min(0.f, -(CurrentRotationalVelocity.Pitch + MaxPitchVelMag)); float const DeltaPitch = FMath::Clamp(CurrentRotInput.Pitch * RotationalAcceleration.Pitch * DeltaTime, MinDeltaPitchVel, MaxDeltaPitchVel); CurrentRotationalVelocity.Pitch += DeltaPitch; } // apply rotation FRotator RotDelta = CurrentRotationalVelocity * DeltaTime; if (!RotDelta.IsNearlyZero()) { FRotator const CurrentComponentRot = UpdatedComponent->GetComponentRotation(); // enforce pitch limits float const CurrentPitch = CurrentComponentRot.Pitch; float const MinDeltaPitch = MinPitch - CurrentPitch; float const MaxDeltaPitch = MaxPitch - CurrentPitch; float const OldPitch = RotDelta.Pitch; RotDelta.Pitch = FMath::Clamp(RotDelta.Pitch, MinDeltaPitch, MaxDeltaPitch); if (OldPitch != RotDelta.Pitch) { // if we got clamped, zero the pitch velocity CurrentRotationalVelocity.Pitch = 0.f; } FRotator const NewRot = CurrentComponentRot + RotDelta; FHitResult Hit(1.f); SafeMoveUpdatedComponent(FVector::ZeroVector, NewRot, false, Hit); } // consume input CurrentRotInput = FRotator::ZeroRotator; }