Advertisement
Guest User

UE4 - ProjectileMovementComponent.cpp

a guest
Jan 31st, 2020
947
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 27.25 KB | None | 0 0
  1. // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
  2.  
  3. #include "GameFramework/ProjectileMovementComponent.h"
  4. #include "EngineDefines.h"
  5. #include "GameFramework/DamageType.h"
  6. #include "Engine/World.h"
  7. #include "Components/PrimitiveComponent.h"
  8. #include "GameFramework/WorldSettings.h"
  9. #include "ProfilingDebugging/CsvProfiler.h"
  10.  
  11. CSV_DECLARE_CATEGORY_MODULE_EXTERN(CORE_API, Basic);
  12. DEFINE_LOG_CATEGORY_STATIC(LogProjectileMovement, Log, All);
  13.  
  14. const float UProjectileMovementComponent::MIN_TICK_TIME = 1e-6f;
  15.  
  16. UProjectileMovementComponent::UProjectileMovementComponent(const FObjectInitializer& ObjectInitializer)
  17.     : Super(ObjectInitializer)
  18. {
  19.     bUpdateOnlyIfRendered = false;
  20.     bInitialVelocityInLocalSpace = true;
  21.     bForceSubStepping = false;
  22.     bSimulationEnabled = true;
  23.     bSweepCollision = true;
  24.     bInterpMovement = false;
  25.     bInterpRotation = false;
  26.     bInterpolationComplete = true;
  27.     InterpLocationTime = 0.100f;
  28.     InterpRotationTime = 0.050f;
  29.     InterpLocationMaxLagDistance = 300.0f;
  30.     InterpLocationSnapToTargetDistance = 500.0f;
  31.  
  32.     Velocity = FVector(1.f,0.f,0.f);
  33.  
  34.     ProjectileGravityScale = 1.f;
  35.  
  36.     Bounciness = 0.6f;
  37.     Friction = 0.2f;
  38.     BounceVelocityStopSimulatingThreshold = 5.f;
  39.     MinFrictionFraction = 0.0f;
  40.  
  41.     HomingAccelerationMagnitude = 0.f;
  42.  
  43.     bWantsInitializeComponent = true;
  44.     bComponentShouldUpdatePhysicsVolume = false;
  45.  
  46.     MaxSimulationTimeStep = 0.05f;
  47.     MaxSimulationIterations = 4;
  48.     BounceAdditionalIterations = 1;
  49.  
  50.     bBounceAngleAffectsFriction = false;
  51.     bIsSliding = false;
  52.     PreviousHitTime = 1.f;
  53.     PreviousHitNormal = FVector::UpVector;
  54. }
  55.  
  56. void UProjectileMovementComponent::PostLoad()
  57. {
  58.     Super::PostLoad();
  59.  
  60.     const int32 LinkerUE4Ver = GetLinkerUE4Version();
  61.  
  62.     if (LinkerUE4Ver < VER_UE4_REFACTOR_PROJECTILE_MOVEMENT)
  63.     {
  64.         // Old code used to treat Bounciness as Friction as well.
  65.         Friction = FMath::Clamp(1.f - Bounciness, 0.f, 1.f);
  66.  
  67.         // Old projectiles probably don't want to use this behavior by default.
  68.         bInitialVelocityInLocalSpace = false;
  69.     }
  70. }
  71.  
  72.  
  73. void UProjectileMovementComponent::InitializeComponent()
  74. {
  75.     Super::InitializeComponent();
  76.  
  77.     if (Velocity.SizeSquared() > 0.f)
  78.     {
  79.         // InitialSpeed > 0 overrides initial velocity magnitude.
  80.         if (InitialSpeed > 0.f)
  81.         {
  82.             Velocity = Velocity.GetSafeNormal() * InitialSpeed;
  83.         }
  84.  
  85.         if (bInitialVelocityInLocalSpace)
  86.         {
  87.             SetVelocityInLocalSpace(Velocity);
  88.         }
  89.  
  90.         if (bRotationFollowsVelocity)
  91.         {
  92.             if (UpdatedComponent)
  93.             {
  94.                 FRotator DesiredRotation = Velocity.Rotation();
  95.                 if (bRotationRemainsVertical)
  96.                 {
  97.                     DesiredRotation.Pitch = 0.0f;
  98.                     DesiredRotation.Yaw = FRotator::NormalizeAxis(DesiredRotation.Yaw);
  99.                     DesiredRotation.Roll = 0.0f;
  100.                 }
  101.  
  102.                 UpdatedComponent->SetWorldRotation(DesiredRotation);
  103.             }
  104.         }
  105.  
  106.         UpdateComponentVelocity();
  107.        
  108.         if (UpdatedPrimitive && UpdatedPrimitive->IsSimulatingPhysics())
  109.         {
  110.             UpdatedPrimitive->SetPhysicsLinearVelocity(Velocity);
  111.         }
  112.     }
  113. }
  114.  
  115.  
  116. void UProjectileMovementComponent::UpdateTickRegistration()
  117. {
  118.     if (bAutoUpdateTickRegistration)
  119.     {
  120.         if (!bInterpolationComplete)
  121.         {
  122.             SetComponentTickEnabled(true);
  123.         }
  124.         else
  125.         {
  126.             Super::UpdateTickRegistration();
  127.         }
  128.     }
  129. }
  130.  
  131. void UProjectileMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
  132. {
  133.     QUICK_SCOPE_CYCLE_COUNTER( STAT_ProjectileMovementComponent_TickComponent );
  134.     CSV_SCOPED_TIMING_STAT_EXCLUSIVE(ProjectileMovement);
  135.  
  136.     // Still need to finish interpolating after we've stopped simulating, so do that first.
  137.     if (bInterpMovement && !bInterpolationComplete)
  138.     {
  139.         QUICK_SCOPE_CYCLE_COUNTER(STAT_ProjectileMovementComponent_TickInterpolation);
  140.         TickInterpolation(DeltaTime);
  141.     }
  142.  
  143.     // Consume PendingForce and reset to zero.
  144.     // At this point, any calls to AddForce() will apply to the next frame.
  145.     PendingForceThisUpdate = PendingForce;
  146.     ClearPendingForce();
  147.  
  148.     // skip if don't want component updated when not rendered or updated component can't move
  149.     if (HasStoppedSimulation() || ShouldSkipUpdate(DeltaTime))
  150.     {
  151.         return;
  152.     }
  153.  
  154.     Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
  155.  
  156.     if (!IsValid(UpdatedComponent) || !bSimulationEnabled)
  157.     {
  158.         return;
  159.     }
  160.  
  161.     AActor* ActorOwner = UpdatedComponent->GetOwner();
  162.     if ( !ActorOwner || !CheckStillInWorld() )
  163.     {
  164.         return;
  165.     }
  166.  
  167.     if (UpdatedComponent->IsSimulatingPhysics())
  168.     {
  169.         return;
  170.     }
  171.  
  172.     float RemainingTime = DeltaTime;
  173.     int32 NumImpacts = 0;
  174.     int32 NumBounces = 0;
  175.     int32 LoopCount = 0;
  176.     int32 Iterations = 0;
  177.     FHitResult Hit(1.f);
  178.    
  179.     while (bSimulationEnabled && RemainingTime >= MIN_TICK_TIME && (Iterations < MaxSimulationIterations) && !ActorOwner->IsPendingKill() && !HasStoppedSimulation())
  180.     {
  181.         LoopCount++;
  182.         Iterations++;
  183.  
  184.         // subdivide long ticks to more closely follow parabolic trajectory
  185.         const float InitialTimeRemaining = RemainingTime;
  186.         const float TimeTick = ShouldUseSubStepping() ? GetSimulationTimeStep(RemainingTime, Iterations) : RemainingTime;
  187.         RemainingTime -= TimeTick;
  188.        
  189.         // Logging
  190.         UE_LOG(LogProjectileMovement, Verbose, TEXT("Projectile %s: (Role: %d, Iteration %d, step %.3f, [%.3f / %.3f] cur/total) sim (Pos %s, Vel %s)"),
  191.             *GetNameSafe(ActorOwner), (int32)ActorOwner->GetLocalRole(), LoopCount, TimeTick, FMath::Max(0.f, DeltaTime - InitialTimeRemaining), DeltaTime,
  192.             *UpdatedComponent->GetComponentLocation().ToString(), *Velocity.ToString());
  193.  
  194.         // Initial move state
  195.         Hit.Time = 1.f;
  196.         const FVector OldVelocity = Velocity;
  197.         const FVector MoveDelta = ComputeMoveDelta(OldVelocity, TimeTick);
  198.         FQuat NewRotation = (bRotationFollowsVelocity && !OldVelocity.IsNearlyZero(0.01f)) ? OldVelocity.ToOrientationQuat() : UpdatedComponent->GetComponentQuat();
  199.  
  200.         if (bRotationFollowsVelocity && bRotationRemainsVertical)
  201.         {
  202.             FRotator DesiredRotation = NewRotation.Rotator();
  203.             DesiredRotation.Pitch = 0.0f;
  204.             DesiredRotation.Yaw = FRotator::NormalizeAxis(DesiredRotation.Yaw);
  205.             DesiredRotation.Roll = 0.0f;
  206.             NewRotation = DesiredRotation.Quaternion();
  207.         }
  208.  
  209.         // Move the component
  210.         if (bShouldBounce)
  211.         {
  212.             // If we can bounce, we are allowed to move out of penetrations, so use SafeMoveUpdatedComponent which does that automatically.
  213.             SafeMoveUpdatedComponent( MoveDelta, NewRotation, bSweepCollision, Hit );
  214.         }
  215.         else
  216.         {
  217.             // 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.
  218.             TGuardValue<EMoveComponentFlags> ScopedFlagRestore(MoveComponentFlags, MoveComponentFlags | MOVECOMP_NeverIgnoreBlockingOverlaps);
  219.             MoveUpdatedComponent(MoveDelta, NewRotation, bSweepCollision, &Hit );
  220.         }
  221.        
  222.         // If we hit a trigger that destroyed us, abort.
  223.         if( ActorOwner->IsPendingKill() || HasStoppedSimulation() )
  224.         {
  225.             return;
  226.         }
  227.  
  228.         // Handle hit result after movement
  229.         if( !Hit.bBlockingHit )
  230.         {
  231.             PreviousHitTime = 1.f;
  232.             bIsSliding = false;
  233.  
  234.             // Only calculate new velocity if events didn't change it during the movement update.
  235.             if (Velocity == OldVelocity)
  236.             {
  237.                 Velocity = ComputeVelocity(Velocity, TimeTick);            
  238.             }
  239.  
  240.             // Logging
  241.             UE_LOG(LogProjectileMovement, VeryVerbose, TEXT("Projectile %s: (Role: %d, Iteration %d, step %.3f) no hit (Pos %s, Vel %s)"),
  242.                 *GetNameSafe(ActorOwner), (int32)ActorOwner->GetLocalRole(), LoopCount, TimeTick, *UpdatedComponent->GetComponentLocation().ToString(), *Velocity.ToString());
  243.         }
  244.         else
  245.         {
  246.             // Only calculate new velocity if events didn't change it during the movement update.
  247.             if (Velocity == OldVelocity)
  248.             {
  249.                 // re-calculate end velocity for partial time
  250.                 Velocity = (Hit.Time > KINDA_SMALL_NUMBER) ? ComputeVelocity(OldVelocity, TimeTick * Hit.Time) : OldVelocity;
  251.             }
  252.  
  253.             // Logging
  254.             UE_CLOG(UpdatedComponent != nullptr, LogProjectileMovement, VeryVerbose, TEXT("Projectile %s: (Role: %d, Iteration %d, step %.3f) new hit at t=%.3f: (Pos %s, Vel %s)"),
  255.                 *GetNameSafe(ActorOwner), (int32)ActorOwner->GetLocalRole(), LoopCount, TimeTick, Hit.Time, *UpdatedComponent->GetComponentLocation().ToString(), *Velocity.ToString());
  256.  
  257.             // Handle blocking hit
  258.             NumImpacts++;
  259.             float SubTickTimeRemaining = TimeTick * (1.f - Hit.Time);
  260.             const EHandleBlockingHitResult HandleBlockingResult = HandleBlockingHit(Hit, TimeTick, MoveDelta, SubTickTimeRemaining);
  261.             if (HandleBlockingResult == EHandleBlockingHitResult::Abort || HasStoppedSimulation())
  262.             {
  263.                 break;
  264.             }
  265.             else if (HandleBlockingResult == EHandleBlockingHitResult::Deflect)
  266.             {
  267.                 NumBounces++;
  268.                 HandleDeflection(Hit, OldVelocity, NumBounces, SubTickTimeRemaining);
  269.                 PreviousHitTime = Hit.Time;
  270.                 PreviousHitNormal = ConstrainNormalToPlane(Hit.Normal);
  271.             }
  272.             else if (HandleBlockingResult == EHandleBlockingHitResult::AdvanceNextSubstep)
  273.             {
  274.                 // Reset deflection logic to ignore this hit
  275.                 PreviousHitTime = 1.f;
  276.             }
  277.             else
  278.             {
  279.                 // Unhandled EHandleBlockingHitResult
  280.                 checkNoEntry();
  281.             }
  282.            
  283.             // Logging
  284.             UE_CLOG(UpdatedComponent != nullptr, LogProjectileMovement, VeryVerbose, TEXT("Projectile %s: (Role: %d, Iteration %d, step %.3f) deflect at t=%.3f: (Pos %s, Vel %s)"),
  285.                 *GetNameSafe(ActorOwner), (int32)ActorOwner->GetLocalRole(), Iterations, TimeTick, Hit.Time, *UpdatedComponent->GetComponentLocation().ToString(), *Velocity.ToString());
  286.            
  287.             // Add unprocessed time after impact
  288.             if (SubTickTimeRemaining >= MIN_TICK_TIME)
  289.             {
  290.                 RemainingTime += SubTickTimeRemaining;
  291.  
  292.                 // A few initial impacts should possibly allow more iterations to complete more of the simulation.
  293.                 if (NumImpacts <= BounceAdditionalIterations)
  294.                 {
  295.                     Iterations--;
  296.  
  297.                     // Logging
  298.                     UE_LOG(LogProjectileMovement, Verbose, TEXT("Projectile %s: (Role: %d, Iteration %d, step %.3f) allowing extra iteration after bounce %u (t=%.3f, adding %.3f secs)"),
  299.                         *GetNameSafe(ActorOwner), (int32)ActorOwner->GetLocalRole(), LoopCount, TimeTick, NumBounces, Hit.Time, SubTickTimeRemaining);
  300.                 }
  301.             }
  302.         }
  303.     }
  304.  
  305.     UpdateComponentVelocity();
  306. }
  307.  
  308.  
  309. bool UProjectileMovementComponent::HandleDeflection(FHitResult& Hit, const FVector& OldVelocity, const uint32 NumBounces, float& SubTickTimeRemaining)
  310. {
  311.     const FVector Normal = ConstrainNormalToPlane(Hit.Normal);
  312.  
  313.     // Multiple hits within very short time period?
  314.     const bool bMultiHit = (PreviousHitTime < 1.f && Hit.Time <= KINDA_SMALL_NUMBER);
  315.  
  316.     // if velocity still into wall (after HandleBlockingHit() had a chance to adjust), slide along wall
  317.     const float DotTolerance = 0.01f;
  318.     bIsSliding = (bMultiHit && FVector::Coincident(PreviousHitNormal, Normal)) ||
  319.                     ((Velocity.GetSafeNormal() | Normal) <= DotTolerance);
  320.  
  321.     if (bIsSliding)
  322.     {
  323.         if (bMultiHit && (PreviousHitNormal | Normal) <= 0.f)
  324.         {
  325.             //90 degree or less corner, so use cross product for direction
  326.             FVector NewDir = (Normal ^ PreviousHitNormal);
  327.             NewDir = NewDir.GetSafeNormal();
  328.             Velocity = Velocity.ProjectOnToNormal(NewDir);
  329.             if ((OldVelocity | Velocity) < 0.f)
  330.             {
  331.                 Velocity *= -1.f;
  332.             }
  333.             Velocity = ConstrainDirectionToPlane(Velocity);
  334.         }
  335.         else
  336.         {
  337.             //adjust to move along new wall
  338.             Velocity = ComputeSlideVector(Velocity, 1.f, Normal, Hit);
  339.         }
  340.  
  341.         // Check min velocity.
  342.         if (IsVelocityUnderSimulationThreshold())
  343.         {
  344.             StopSimulating(Hit);
  345.             return false;
  346.         }
  347.  
  348.         // Velocity is now parallel to the impact surface.
  349.         if (SubTickTimeRemaining > KINDA_SMALL_NUMBER)
  350.         {
  351.             if (!HandleSliding(Hit, SubTickTimeRemaining))
  352.             {
  353.                 return false;
  354.             }
  355.         }
  356.     }
  357.  
  358.     return true;
  359. }
  360.  
  361.  
  362. bool UProjectileMovementComponent::HandleSliding(FHitResult& Hit, float& SubTickTimeRemaining)
  363. {
  364.     FHitResult InitialHit(Hit);
  365.     const FVector OldHitNormal = ConstrainDirectionToPlane(Hit.Normal);
  366.  
  367.     // Velocity is now parallel to the impact surface.
  368.     // Perform the move now, before adding gravity/accel again, so we don't just keep hitting the surface.
  369.     SafeMoveUpdatedComponent(Velocity * SubTickTimeRemaining, UpdatedComponent->GetComponentQuat(), bSweepCollision, Hit);
  370.  
  371.     if (HasStoppedSimulation())
  372.     {
  373.         return false;
  374.     }
  375.  
  376.     // A second hit can deflect the velocity (through the normal bounce code), for the next iteration.
  377.     if (Hit.bBlockingHit)
  378.     {
  379.         const float TimeTick = SubTickTimeRemaining;
  380.         SubTickTimeRemaining = TimeTick * (1.f - Hit.Time);
  381.        
  382.         if (HandleBlockingHit(Hit, TimeTick, Velocity * TimeTick, SubTickTimeRemaining) == EHandleBlockingHitResult::Abort ||
  383.             HasStoppedSimulation())
  384.         {
  385.             return false;
  386.         }
  387.     }
  388.     else
  389.     {
  390.         // Find velocity after elapsed time
  391.         const FVector PostTickVelocity = ComputeVelocity(Velocity, SubTickTimeRemaining);
  392.  
  393.         // If pointing back into surface, apply friction and acceleration.
  394.         const FVector Force = (PostTickVelocity - Velocity);
  395.         const float ForceDotN = (Force | OldHitNormal);
  396.         if (ForceDotN < 0.f)
  397.         {
  398.             const FVector ProjectedForce = FVector::VectorPlaneProject(Force, OldHitNormal);
  399.             const FVector NewVelocity = Velocity + ProjectedForce;
  400.  
  401.             const FVector FrictionForce = -NewVelocity.GetSafeNormal() * FMath::Min(-ForceDotN * Friction, NewVelocity.Size());
  402.             Velocity = ConstrainDirectionToPlane(NewVelocity + FrictionForce);
  403.         }
  404.         else
  405.         {
  406.             Velocity = PostTickVelocity;
  407.         }
  408.  
  409.         // Check min velocity
  410.         if (IsVelocityUnderSimulationThreshold())
  411.         {
  412.             StopSimulating(InitialHit);
  413.             return false;
  414.         }
  415.  
  416.         SubTickTimeRemaining = 0.f;
  417.     }
  418.  
  419.     return true;
  420. }
  421.  
  422.  
  423. void UProjectileMovementComponent::SetVelocityInLocalSpace(FVector NewVelocity)
  424. {
  425.     if (UpdatedComponent)
  426.     {
  427.         Velocity = UpdatedComponent->GetComponentToWorld().TransformVectorNoScale(NewVelocity);
  428.     }
  429. }
  430.  
  431.  
  432. FVector UProjectileMovementComponent::ComputeVelocity(FVector InitialVelocity, float DeltaTime) const
  433. {
  434.     // v = v0 + a*t
  435.     const FVector Acceleration = ComputeAcceleration(InitialVelocity, DeltaTime);
  436.     FVector NewVelocity = InitialVelocity + (Acceleration * DeltaTime);
  437.  
  438.     return LimitVelocity(NewVelocity);
  439. }
  440.  
  441.  
  442. FVector UProjectileMovementComponent::LimitVelocity(FVector NewVelocity) const
  443. {
  444.     const float CurrentMaxSpeed = GetMaxSpeed();
  445.     if (CurrentMaxSpeed > 0.f)
  446.     {
  447.         NewVelocity = NewVelocity.GetClampedToMaxSize(CurrentMaxSpeed);
  448.     }
  449.  
  450.     return ConstrainDirectionToPlane(NewVelocity);
  451. }
  452.  
  453. FVector UProjectileMovementComponent::ComputeMoveDelta(const FVector& InVelocity, float DeltaTime) const
  454. {
  455.     // Velocity Verlet integration (http://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet)
  456.     // The addition of p0 is done outside this method, we are just computing the delta.
  457.     // p = p0 + v0*t + 1/2*a*t^2
  458.  
  459.     // We use ComputeVelocity() here to infer the acceleration, to make it easier to apply custom velocities.
  460.     // p = p0 + v0*t + 1/2*((v1-v0)/t)*t^2
  461.     // p = p0 + v0*t + 1/2*((v1-v0))*t
  462.  
  463.     const FVector NewVelocity = ComputeVelocity(InVelocity, DeltaTime);
  464.     const FVector Delta = (InVelocity * DeltaTime) + (NewVelocity - InVelocity) * (0.5f * DeltaTime);
  465.     return Delta;
  466. }
  467.  
  468. FVector UProjectileMovementComponent::ComputeAcceleration(const FVector& InVelocity, float DeltaTime) const
  469. {
  470.     FVector Acceleration(FVector::ZeroVector);
  471.  
  472.     Acceleration.Z += GetGravityZ();
  473.  
  474.     Acceleration += PendingForceThisUpdate;
  475.  
  476.     if (bIsHomingProjectile && HomingTargetComponent.IsValid())
  477.     {
  478.         Acceleration += ComputeHomingAcceleration(InVelocity, DeltaTime);
  479.     }
  480.  
  481.     return Acceleration;
  482. }
  483.  
  484. // Allow the projectile to track towards its homing target.
  485. FVector UProjectileMovementComponent::ComputeHomingAcceleration(const FVector& InVelocity, float DeltaTime) const
  486. {
  487.     FVector HomingAcceleration = ((HomingTargetComponent->GetComponentLocation() - UpdatedComponent->GetComponentLocation()).GetSafeNormal() * HomingAccelerationMagnitude);
  488.     return HomingAcceleration;
  489. }
  490.  
  491.  
  492. void UProjectileMovementComponent::AddForce(FVector Force)
  493. {
  494.     PendingForce += Force;
  495. }
  496.  
  497. void UProjectileMovementComponent::ClearPendingForce(bool bClearImmediateForce)
  498. {
  499.     PendingForce = FVector::ZeroVector;
  500.     if (bClearImmediateForce)
  501.     {
  502.         PendingForceThisUpdate = FVector::ZeroVector;
  503.     }
  504. }
  505.  
  506. float UProjectileMovementComponent::GetGravityZ() const
  507. {
  508.     // TODO: apply buoyancy if in water
  509.     return ShouldApplyGravity() ? Super::GetGravityZ() * ProjectileGravityScale : 0.f;
  510. }
  511.  
  512.  
  513. void UProjectileMovementComponent::StopSimulating(const FHitResult& HitResult)
  514. {
  515.     Velocity = FVector::ZeroVector;
  516.     PendingForce = FVector::ZeroVector;
  517.     PendingForceThisUpdate = FVector::ZeroVector;
  518.     UpdateComponentVelocity();
  519.     SetUpdatedComponent(NULL);
  520.     OnProjectileStop.Broadcast(HitResult);
  521. }
  522.  
  523.  
  524. UProjectileMovementComponent::EHandleBlockingHitResult UProjectileMovementComponent::HandleBlockingHit(const FHitResult& Hit, float TimeTick, const FVector& MoveDelta, float& SubTickTimeRemaining)
  525. {
  526.     AActor* ActorOwner = UpdatedComponent ? UpdatedComponent->GetOwner() : NULL;
  527.     if (!CheckStillInWorld() || !ActorOwner || ActorOwner->IsPendingKill())
  528.     {
  529.         return EHandleBlockingHitResult::Abort;
  530.     }
  531.    
  532.     HandleImpact(Hit, TimeTick, MoveDelta);
  533.    
  534.     if (ActorOwner->IsPendingKill() || HasStoppedSimulation())
  535.     {
  536.         return EHandleBlockingHitResult::Abort;
  537.     }
  538.  
  539.     if (Hit.bStartPenetrating)
  540.     {
  541.         UE_LOG(LogProjectileMovement, Verbose, TEXT("Projectile %s is stuck inside %s.%s!"), *GetNameSafe(ActorOwner), *GetNameSafe(Hit.GetActor()), *GetNameSafe(Hit.GetComponent()));
  542.         return EHandleBlockingHitResult::Abort;
  543.     }
  544.  
  545.     SubTickTimeRemaining = TimeTick * (1.f - Hit.Time);
  546.     return EHandleBlockingHitResult::Deflect;
  547. }
  548.  
  549. FVector UProjectileMovementComponent::ComputeBounceResult(const FHitResult& Hit, float TimeSlice, const FVector& MoveDelta)
  550. {
  551.     FVector TempVelocity = Velocity;
  552.     const FVector Normal = ConstrainNormalToPlane(Hit.Normal);
  553.     const float VDotNormal = (TempVelocity | Normal);
  554.  
  555.     // Only if velocity is opposed by normal or parallel
  556.     if (VDotNormal <= 0.f)
  557.     {
  558.         // Project velocity onto normal in reflected direction.
  559.         const FVector ProjectedNormal = Normal * -VDotNormal;
  560.  
  561.         // Point velocity in direction parallel to surface
  562.         TempVelocity += ProjectedNormal;
  563.  
  564.         // Only tangential velocity should be affected by friction.
  565.         const float ScaledFriction = (bBounceAngleAffectsFriction || bIsSliding) ? FMath::Clamp(-VDotNormal / TempVelocity.Size(), MinFrictionFraction, 1.f) * Friction : Friction;
  566.         TempVelocity *= FMath::Clamp(1.f - ScaledFriction, 0.f, 1.f);
  567.  
  568.         // Coefficient of restitution only applies perpendicular to impact.
  569.         TempVelocity += (ProjectedNormal * FMath::Max(Bounciness, 0.f));
  570.  
  571.         // Bounciness could cause us to exceed max speed.
  572.         TempVelocity = LimitVelocity(TempVelocity);
  573.     }
  574.  
  575.     return TempVelocity;
  576. }
  577.  
  578. void UProjectileMovementComponent::HandleImpact(const FHitResult& Hit, float TimeSlice, const FVector& MoveDelta)
  579. {
  580.     bool bStopSimulating = false;
  581.  
  582.     if (bShouldBounce)
  583.     {
  584.         const FVector OldVelocity = Velocity;
  585.         Velocity = ComputeBounceResult(Hit, TimeSlice, MoveDelta);
  586.  
  587.         // Trigger bounce events
  588.         OnProjectileBounce.Broadcast(Hit, OldVelocity);
  589.  
  590.         // Event may modify velocity or threshold, so check velocity threshold now.
  591.         Velocity = LimitVelocity(Velocity);
  592.         if (IsVelocityUnderSimulationThreshold())
  593.         {
  594.             bStopSimulating = true;
  595.         }
  596.     }
  597.     else
  598.     {
  599.         bStopSimulating = true;
  600.     }
  601.  
  602.  
  603.     if (bStopSimulating)
  604.     {
  605.         StopSimulating(Hit);
  606.     }
  607. }
  608.  
  609. bool UProjectileMovementComponent::CheckStillInWorld()
  610. {
  611.     if ( !UpdatedComponent )
  612.     {
  613.         return false;
  614.     }
  615.  
  616.     const UWorld* MyWorld = GetWorld();
  617.     if (!MyWorld)
  618.     {
  619.         return false;
  620.     }
  621.  
  622.     // check the variations of KillZ
  623.     AWorldSettings* WorldSettings = MyWorld->GetWorldSettings( true );
  624.     if (!WorldSettings->bEnableWorldBoundsChecks)
  625.     {
  626.         return true;
  627.     }
  628.     AActor* ActorOwner = UpdatedComponent->GetOwner();
  629.     if (!IsValid(ActorOwner))
  630.     {
  631.         return false;
  632.     }
  633.     if( ActorOwner->GetActorLocation().Z < WorldSettings->KillZ )
  634.     {
  635.         UDamageType const* DmgType = WorldSettings->KillZDamageType ? WorldSettings->KillZDamageType->GetDefaultObject<UDamageType>() : GetDefault<UDamageType>();
  636.         ActorOwner->FellOutOfWorld(*DmgType);
  637.         return false;
  638.     }
  639.     // Check if box has poked outside the world
  640.     else if( UpdatedComponent && UpdatedComponent->IsRegistered() )
  641.     {
  642.         const FBox& Box = UpdatedComponent->Bounds.GetBox();
  643.         if( Box.Min.X < -HALF_WORLD_MAX || Box.Max.X > HALF_WORLD_MAX ||
  644.             Box.Min.Y < -HALF_WORLD_MAX || Box.Max.Y > HALF_WORLD_MAX ||
  645.             Box.Min.Z < -HALF_WORLD_MAX || Box.Max.Z > HALF_WORLD_MAX )
  646.         {
  647.             UE_LOG(LogProjectileMovement, Warning, TEXT("%s is outside the world bounds!"), *ActorOwner->GetName());
  648.             ActorOwner->OutsideWorldBounds();
  649.             // not safe to use physics or collision at this point
  650.             ActorOwner->SetActorEnableCollision(false);
  651.             FHitResult Hit(1.f);
  652.             StopSimulating(Hit);
  653.             return false;
  654.         }
  655.     }
  656.     return true;
  657. }
  658.  
  659.  
  660. bool UProjectileMovementComponent::ShouldUseSubStepping() const
  661. {
  662.     return bForceSubStepping || (GetGravityZ() != 0.f) || (bIsHomingProjectile && HomingTargetComponent.IsValid());
  663. }
  664.  
  665.  
  666. float UProjectileMovementComponent::GetSimulationTimeStep(float RemainingTime, int32 Iterations) const
  667. {
  668.     if (RemainingTime > MaxSimulationTimeStep)
  669.     {
  670.         if (Iterations < MaxSimulationIterations)
  671.         {
  672.             // Subdivide moves to be no longer than MaxSimulationTimeStep seconds
  673.             RemainingTime = FMath::Min(MaxSimulationTimeStep, RemainingTime * 0.5f);
  674.         }
  675.         else
  676.         {
  677.             // If this is the last iteration, just use all the remaining time. This is better than cutting things short, as the simulation won't move far enough otherwise.
  678.             // Print a throttled warning.
  679. #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
  680.             if (const UWorld* const World = GetWorld())
  681.             {
  682.                 // Don't report during long hitches, we're more concerned about normal behavior just to make sure we have reasonable simulation settings.
  683.                 if (World->DeltaTimeSeconds < 0.20f)
  684.                 {
  685.                     static uint32 s_WarningCount = 0;
  686.                     if ((s_WarningCount++ < 100) || (GFrameCounter & 15) == 0)
  687.                     {
  688.                         UE_LOG(LogProjectileMovement, Warning, TEXT("GetSimulationTimeStep() - Max iterations %d hit while remaining time %.6f > MaxSimulationTimeStep (%.3f) for '%s'"), MaxSimulationIterations, RemainingTime, MaxSimulationTimeStep, *GetPathNameSafe(UpdatedComponent));
  689.                     }
  690.                 }
  691.             }
  692. #endif
  693.         }
  694.     }
  695.  
  696.     // no less than MIN_TICK_TIME (to avoid potential divide-by-zero during simulation).
  697.     return FMath::Max(MIN_TICK_TIME, RemainingTime);
  698. }
  699.  
  700. void UProjectileMovementComponent::SetInterpolatedComponent(USceneComponent* Component)
  701. {
  702.     if (Component == GetInterpolatedComponent())
  703.     {
  704.         return;
  705.     }
  706.  
  707.     if (Component)
  708.     {
  709.         ResetInterpolation();
  710.         InterpolatedComponentPtr = Component;
  711.         InterpInitialLocationOffset = Component->GetRelativeLocation();
  712.         InterpInitialRotationOffset = Component->GetRelativeRotation().Quaternion();
  713.         bInterpolationComplete = false;
  714.     }
  715.     else
  716.     {
  717.         ResetInterpolation();
  718.         InterpolatedComponentPtr = nullptr;
  719.         InterpInitialLocationOffset = FVector::ZeroVector;
  720.         InterpInitialRotationOffset = FQuat::Identity;
  721.         bInterpolationComplete = true;
  722.     }
  723. }
  724.  
  725. USceneComponent* UProjectileMovementComponent::GetInterpolatedComponent() const
  726. {
  727.     return InterpolatedComponentPtr.Get();
  728. }
  729.  
  730. void UProjectileMovementComponent::MoveInterpolationTarget(const FVector& NewLocation, const FRotator& NewRotation)
  731. {
  732.     if (!UpdatedComponent)
  733.     {
  734.         return;
  735.     }
  736.  
  737.     bool bHandledMovement = false;
  738.     if (bInterpMovement)
  739.     {
  740.         if (USceneComponent* InterpComponent = GetInterpolatedComponent())
  741.         {
  742.             // Avoid moving the child, it will interpolate later
  743.             const FRotator InterpRelativeRotation = InterpComponent->GetRelativeRotation();
  744.             FScopedPreventAttachedComponentMove ScopedChildNoMove(InterpComponent);
  745.            
  746.             // Update interp offset
  747.             const FVector OldLocation = UpdatedComponent->GetComponentLocation();
  748.             const FVector NewToOldVector = (OldLocation - NewLocation);
  749.             InterpLocationOffset += NewToOldVector;
  750.  
  751.             // Enforce distance limits
  752.             if (NewToOldVector.SizeSquared() > FMath::Square(InterpLocationSnapToTargetDistance))
  753.             {
  754.                 InterpLocationOffset = FVector::ZeroVector;
  755.             }
  756.             else if (InterpLocationOffset.SizeSquared() > FMath::Square(InterpLocationMaxLagDistance))
  757.             {
  758.                 InterpLocationOffset = InterpLocationMaxLagDistance * InterpLocationOffset.GetSafeNormal();
  759.             }
  760.  
  761.             // Handle rotation
  762.             if (bInterpRotation)
  763.             {
  764.                 const FQuat OldRotation = UpdatedComponent->GetComponentQuat();
  765.                 InterpRotationOffset = (NewRotation.Quaternion().Inverse() * OldRotation) * InterpRotationOffset;
  766.             }
  767.             else
  768.             {
  769.                 // If not interpolating rotation, we should allow the component to rotate.
  770.                 // The absolute flag will get restored by the scoped move.
  771.                 InterpComponent->SetUsingAbsoluteRotation(false);
  772.                 InterpComponent->SetRelativeRotation_Direct(InterpRelativeRotation);
  773.                 InterpRotationOffset = FQuat::Identity;
  774.             }
  775.  
  776.             // Move the root
  777.             UpdatedComponent->SetRelativeLocationAndRotation(NewLocation, NewRotation);
  778.             bHandledMovement = true;
  779.             bInterpolationComplete = false;
  780.         }
  781.         else
  782.         {
  783.             ResetInterpolation();
  784.             bInterpolationComplete = true;
  785.         }
  786.     }
  787.    
  788.     if (!bHandledMovement)
  789.     {
  790.         UpdatedComponent->SetRelativeLocationAndRotation(NewLocation, NewRotation);
  791.     }
  792. }
  793.  
  794. void UProjectileMovementComponent::ResetInterpolation()
  795. {
  796.     if (USceneComponent* InterpComponent = GetInterpolatedComponent())
  797.     {
  798.         InterpComponent->SetRelativeLocationAndRotation(InterpInitialLocationOffset, InterpInitialRotationOffset);
  799.     }
  800.  
  801.     InterpLocationOffset = FVector::ZeroVector;
  802.     InterpRotationOffset = FQuat::Identity;
  803.     bInterpolationComplete = true;
  804. }
  805.  
  806. void UProjectileMovementComponent::TickInterpolation(float DeltaTime)
  807. {
  808.     if (!bInterpolationComplete)
  809.     {
  810.         if (USceneComponent* InterpComponent = GetInterpolatedComponent())
  811.         {
  812.             // Smooth location. Interp faster when stopping.
  813.             const float ActualInterpLocationTime = Velocity.IsZero() ? 0.5f * InterpLocationTime : InterpLocationTime;
  814.             if (DeltaTime < ActualInterpLocationTime)
  815.             {
  816.                 // Slowly decay translation offset (lagged exponential smoothing)
  817.                 InterpLocationOffset = (InterpLocationOffset * (1.f - DeltaTime / ActualInterpLocationTime));
  818.             }
  819.             else
  820.             {
  821.                 InterpLocationOffset = FVector::ZeroVector;
  822.             }
  823.  
  824.             // Smooth rotation
  825.             if (DeltaTime < InterpRotationTime && bInterpRotation)
  826.             {
  827.                 // Slowly decay rotation offset
  828.                 InterpRotationOffset = FQuat::FastLerp(InterpRotationOffset, FQuat::Identity, DeltaTime / InterpRotationTime).GetNormalized();
  829.             }
  830.             else
  831.             {
  832.                 InterpRotationOffset = FQuat::Identity;
  833.             }
  834.  
  835.             // Test for reaching the end
  836.             if (InterpLocationOffset.IsNearlyZero(1e-2f) && InterpRotationOffset.Equals(FQuat::Identity, 1e-5f))
  837.             {
  838.                 InterpLocationOffset = FVector::ZeroVector;
  839.                 InterpRotationOffset = FQuat::Identity;
  840.                 bInterpolationComplete = true;
  841.             }
  842.  
  843.             // Apply result
  844.             if (UpdatedComponent)
  845.             {
  846.                 const FVector NewRelTranslation = UpdatedComponent->GetComponentToWorld().InverseTransformVectorNoScale(InterpLocationOffset) + InterpInitialLocationOffset;
  847.                 if (bInterpRotation)
  848.                 {
  849.                     const FQuat NewRelRotation = InterpRotationOffset * InterpInitialRotationOffset;
  850.                     InterpComponent->SetRelativeLocationAndRotation(NewRelTranslation, NewRelRotation);
  851.                 }
  852.                 else
  853.                 {
  854.                     InterpComponent->SetRelativeLocation(NewRelTranslation);
  855.                 }
  856.             }
  857.         }
  858.         else
  859.         {
  860.             ResetInterpolation();
  861.             bInterpolationComplete = true;
  862.         }
  863.     }
  864.  
  865.     // Might be done interpolating and want to disable tick
  866.     if (bInterpolationComplete && bAutoUpdateTickRegistration && (UpdatedComponent == nullptr))
  867.     {
  868.         UpdateTickRegistration();
  869.     }
  870. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement