//---------------------------------------- double CBratAlgorithmGeosVelAtp::Run(CVectorBratAlgorithmParam& args) { int32_t iRecord = m_callerProduct->GetCurrentRecordNumber(); if (iRecord == m_callerProductRecordPrev) { // Do nothing: data have been already computed return m_velocity; } // Gets the next record regarding to the current product record. // and save previous values. this->GetNextData(); SetParamValues(args); SetGap(); SetEquatorTransition(); ComputeCoriolis(); // Compute geostrophic velocity. ComputeVelocity(); m_callerProductRecordPrev = iRecord; return m_velocity; }
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; }
FVector UProjectileMovementComponent::ComputeMoveDelta(const FVector& InVelocity, float DeltaTime) const { // Velocity Verlet integration (http://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet) // The addition of p0 is done outside this method, we are just computing the delta. // p = p0 + v0*t + 1/2*a*t^2 // We use ComputeVelocity() here to infer the acceleration, to make it easier to apply custom velocities. // p = p0 + v0*t + 1/2*((v1-v0)/t)*t^2 // p = p0 + v0*t + 1/2*((v1-v0))*t const FVector NewVelocity = ComputeVelocity(InVelocity, DeltaTime); const FVector Delta = (InVelocity * DeltaTime) + (NewVelocity - InVelocity) * (0.5f * DeltaTime); return Delta; }
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(); }
// Deprecated FVector UProjectileMovementComponent::CalculateVelocity(FVector OldVelocity, float DeltaTime, bool bGravityEnabled_UNUSED) const { return ComputeVelocity(OldVelocity, DeltaTime); }