void UProjectileMovementComponent::InitializeComponent() { Super::InitializeComponent(); if (Velocity.SizeSquared() > 0.f) { // InitialSpeed > 0 overrides initial velocity magnitude. if (InitialSpeed > 0.f) { Velocity = Velocity.GetSafeNormal() * InitialSpeed; } if (bInitialVelocityInLocalSpace) { SetVelocityInLocalSpace(Velocity); } if (bRotationFollowsVelocity) { if (UpdatedComponent) { UpdatedComponent->SetWorldRotation(Velocity.Rotation()); } } UpdateComponentVelocity(); if (UpdatedPrimitive && UpdatedPrimitive->IsSimulatingPhysics()) { UpdatedPrimitive->SetPhysicsLinearVelocity(Velocity); } } }
void UObstacleMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (!UpdatedComponent) { return; } AActor* ActorOwner = UpdatedComponent->GetOwner(); if (!ActorOwner || ActorOwner->IsPendingKill()) { return; } FVector CurrentLocation = UpdatedComponent->GetComponentLocation(); FRotator CurrentRotation = UpdatedComponent->GetComponentRotation(); if (MovingRight) { CurrentLocation.Y += MovementSpeed * DeltaTime; if (CurrentLocation.Y >= RightMovementTarget) { MovingRight = !MovingRight; CurrentLocation.Y = RightMovementTarget; } } else { CurrentLocation.Y -= MovementSpeed * DeltaTime; if (CurrentLocation.Y <= LeftMovementTarget) { MovingRight = !MovingRight; CurrentLocation.Y = LeftMovementTarget; } } FHitResult Hit(1.0f); MoveUpdatedComponent(CurrentLocation - UpdatedComponent->GetComponentLocation(), CurrentRotation, true, &Hit); // If we hit a trigger that destroyed us, abort. if (ActorOwner->IsPendingKill() || !UpdatedComponent) { return; } UpdateComponentVelocity(); }
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 UGridMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (Moving) { /* Find the next location */ Distance = FMath::Min(Spline->GetSplineLength(), Distance + (MaxSpeed * DeltaTime)); /* Grab our current transform so we can find the velocity if we need it later */ AActor *Owner = GetOwner(); FTransform OldTransform = Owner->GetTransform(); /* Find the next loaction from the spline*/ FTransform NewTransform = Spline->GetTransformAtDistanceAlongSpline(Distance, ESplineCoordinateSpace::Local); /* Restrain rotation axis */ FRotator Rotation = NewTransform.Rotator(); Rotation.Roll = LockRoll ? 0 : Rotation.Roll; Rotation.Pitch = LockPitch ? 0 : Rotation.Pitch; Rotation.Yaw = LockYaw ? 0 : Rotation.Yaw; NewTransform.SetRotation(Rotation.Quaternion()); Owner->SetActorTransform(NewTransform); /* Check if we're reached our destination*/ if (Distance >= Spline->GetSplineLength()) { Moving = false; Distance = 0; Velocity = FVector::ZeroVector; OnMovementEndEvent.Broadcast(); } else { Velocity = (NewTransform.GetLocation() - OldTransform.GetLocation()) * (1 / DeltaTime); } // update velocity so it can be fetched by the pawn 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(); }
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(); }