krazwer

AuraProjectileMovementComponent.cpp

Sep 4th, 2025 (edited)
380
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.17 KB | None | 0 0
  1. // Copyright Lucas Rossi
  2.  
  3.  
  4. #include "Components/AuraProjectileMovementComponent.h"
  5.  
  6.  
  7. UAuraProjectileMovementComponent::UAuraProjectileMovementComponent()
  8. {
  9.   PrimaryComponentTick.bCanEverTick = true;
  10. }
  11.  
  12. void UAuraProjectileMovementComponent::BeginPlay()
  13. {
  14.   Super::BeginPlay();
  15.  
  16.   StartVelocity = Velocity;
  17.   if (UpdatedComponent)
  18.   {
  19.     StartLocation = UpdatedComponent->GetComponentLocation();
  20.     StartTransform = UpdatedComponent->GetComponentTransform();
  21.   }
  22.  
  23.   if (!bDefinedDirections && MotionShiftModes.Contains(EMotionShiftMode::Direction))
  24.   {
  25.     DirectionMultiplier = FMath::RandBool() ? 1.f : -1.f;
  26.   }
  27.   // To ensure those values if setting it from outside
  28.   DirectionMultiplier = FMath::Clamp(DirectionMultiplier, -1.f, 1.f);
  29.  
  30.   if (MotionShiftModes.Contains(EMotionShiftMode::Phase))
  31.   {
  32.     RandomPhaseShift = FMath::FRandRange(0.f, 2.f * PI);
  33.   }
  34.  
  35.   if (MotionShiftModes.Contains(EMotionShiftMode::Speed))
  36.   {
  37.     const float RandomSpeedScale = FMath::FRandRange(1.f - MaxSpeedShiftAmplitude, 1.f + MaxSpeedShiftAmplitude);
  38.  
  39.     switch (ActiveMotion)
  40.     {
  41.     case EProjectileMotionType::Bezier:
  42.       {
  43.         EffectiveBezierDuration = BezierDuration / RandomSpeedScale;
  44.         break;
  45.       }
  46.     case EProjectileMotionType::Noise:
  47.       {
  48.         EffectiveNoiseInterval = NoiseInterval / RandomSpeedScale;
  49.         break;
  50.       }
  51.     case EProjectileMotionType::YoYo:
  52.       {
  53.         Velocity *= RandomSpeedScale;
  54.         StartVelocity = Velocity;
  55.         EffectiveYoYoForwardDuration = YoYoForwardDuration / RandomSpeedScale;
  56.         EffectiveYoYoIdleDuration = YoYoIdleDuration / RandomSpeedScale;
  57.         break;
  58.       }
  59.     default:
  60.       {
  61.         EffectiveFrequency = Frequency * RandomSpeedScale;
  62.       }
  63.     }
  64.   }
  65.  
  66.   if (MotionShiftModes.Contains(EMotionShiftMode::ControlPoint))
  67.   {
  68.     const float Angle = FMath::FRandRange(-MaxRandomBezierRotation / 2, MaxRandomBezierRotation / 2);
  69.     RandomBezierRotation = FRotator(0.f, Angle, 0.f);
  70.   }
  71.  
  72.   if (MotionShiftModes.Contains(EMotionShiftMode::YoYoReturnSpeed))
  73.   {
  74.     EffectiveYoYoReturnSpeedFactor = FMath::FRandRange(1.f - MaxSpeedShiftAmplitude, 1.f + MaxSpeedShiftAmplitude);
  75.   }
  76.  
  77.   if (ActiveMotion == EProjectileMotionType::YoYo)
  78.   {
  79.     SetupYoYoMotion();
  80.   }
  81.  
  82.   if (bBezierOverride)
  83.   {
  84.     EffectiveBezierEnd = BezierEndOverride;
  85.   }
  86. }
  87.  
  88. void UAuraProjectileMovementComponent::InitializeComponent()
  89. {
  90.   Super::InitializeComponent();
  91.  
  92.   EffectiveFrequency = Frequency;
  93.   EffectiveNoiseInterval = NoiseInterval;
  94.   EffectiveBezierDuration = BezierDuration;
  95.   EffectiveBezierEnd = BezierEnd;
  96.   EffectiveYoYoForwardDuration = YoYoForwardDuration;
  97.   EffectiveYoYoIdleDuration = YoYoIdleDuration;
  98.   EffectiveYoYoReturnSpeedFactor = YoYoReturnSpeedFactor;
  99. }
  100.  
  101. void UAuraProjectileMovementComponent::TickComponent(
  102.   float DeltaTime,
  103.   ELevelTick TickType,
  104.   FActorComponentTickFunction* ThisTickFunction
  105. )
  106. {
  107.   Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
  108.  
  109.   if (
  110.     ActiveMotion == EProjectileMotionType::Default ||
  111.     ActiveMotion == EProjectileMotionType::YoYo ||
  112.     bIsHomingProjectile ||
  113.     !UpdatedComponent ||
  114.     ShouldSkipUpdate(DeltaTime)
  115.   )
  116.     return;
  117.  
  118.   ElapsedTime += DeltaTime;
  119.   MotionPhase = ElapsedTime * EffectiveFrequency;
  120.  
  121.   // --- Bezier full override ---
  122.   if (ActiveMotion == EProjectileMotionType::Bezier)
  123.   {
  124.     float T = ElapsedTime / EffectiveBezierDuration;
  125.  
  126.     if (MotionShiftModes.Contains(EMotionShiftMode::Phase))
  127.     {
  128.       T += RandomPhaseShift / (2.f * PI);
  129.     }
  130.  
  131.     T = FMath::Clamp(T, 0.f, 1.f);
  132.  
  133.     const FVector NewPos = ComputeBezierPosition(T);
  134.     const FVector PrevPos = ComputeBezierPosition(FMath::Clamp(T - DeltaTime / BezierDuration, 0.f, 1.f));
  135.  
  136.     const FVector TotalOffset = NewPos - PrevPos;
  137.  
  138.     UpdatedComponent->AddWorldOffset(TotalOffset, false);
  139.     Velocity = TotalOffset / DeltaTime;
  140.     return;
  141.   }
  142.  
  143.   // Additional motions
  144.   const FVector CurrentOffset = ComputeMotionOffset(DeltaTime);
  145.   const FVector DeltaOffset = CurrentOffset - LastOffset;
  146.  
  147.   UpdatedComponent->AddWorldOffset(DeltaOffset, false);
  148.  
  149.   if (bRotationFollowsVelocity)
  150.   {
  151.     const FVector ActualDelta = DeltaOffset + Velocity * DeltaTime;
  152.     if (!ActualDelta.IsNearlyZero())
  153.     {
  154.       const FRotator NewRotation = ActualDelta.GetSafeNormal().Rotation();
  155.       UpdatedComponent->SetWorldRotation(NewRotation);
  156.     }
  157.   }
  158.  
  159.   LastOffset = CurrentOffset;
  160. }
  161.  
  162. void UAuraProjectileMovementComponent::SetActiveMotion(EProjectileMotionType Motion)
  163. {
  164.   ActiveMotion = Motion;
  165. }
  166.  
  167. float UAuraProjectileMovementComponent::ComputeEffectiveAmplitude()
  168. {
  169.   float EffectiveAmplitude = Amplitude;
  170.   if (MotionShiftModes.Contains(EMotionShiftMode::Growth))
  171.   {
  172.     EffectiveAmplitude += GrowthRate * ElapsedTime * EffectiveFrequency;
  173.   }
  174.  
  175.   return EffectiveAmplitude;
  176. }
  177.  
  178. void UAuraProjectileMovementComponent::SetupYoYoMotion()
  179. {
  180.   // ---- Forward phase ----
  181.   FTimerHandle ForwardPhaseTimer;
  182.   GetOwner()->GetWorldTimerManager().SetTimer(
  183.     ForwardPhaseTimer,
  184.     FTimerDelegate::CreateLambda([this]()
  185.     {
  186.       if (EffectiveYoYoIdleDuration > 0)
  187.       {
  188.         // ---- Idle phase ----
  189.         Velocity = FVector::ZeroVector;
  190.         FTimerHandle IdlePhaseTimer;
  191.         GetOwner()->GetWorldTimerManager().SetTimer(
  192.           IdlePhaseTimer,
  193.           FTimerDelegate::CreateLambda([this]()
  194.           {
  195.             // ---- Return phase ----
  196.             ReturnYoYo();
  197.           }),
  198.           EffectiveYoYoIdleDuration,
  199.           false
  200.         );
  201.  
  202.         return;
  203.       }
  204.  
  205.       // ---- Return phase ----
  206.       ReturnYoYo();
  207.     }),
  208.     EffectiveYoYoForwardDuration,
  209.     false
  210.   );
  211. }
  212.  
  213. FVector UAuraProjectileMovementComponent::ComputeMotionOffset(float DeltaTime)
  214. {
  215.   FVector Offset = FVector::ZeroVector;
  216.   const float EffectiveAmplitude = ComputeEffectiveAmplitude();
  217.  
  218.   switch (ActiveMotion)
  219.   {
  220.   case EProjectileMotionType::Helix:
  221.     {
  222.       const FVector Forward = StartVelocity.GetSafeNormal();
  223.       FVector Right, Up;
  224.       Forward.FindBestAxisVectors(Right, Up);
  225.  
  226.       const float Angle = MotionPhase + RandomPhaseShift;
  227.       const FVector CircleOffset =
  228.         (FMath::Sin(Angle) * Right + FMath::Cos(Angle) * Up) *
  229.         EffectiveAmplitude * DirectionMultiplier;
  230.  
  231.       Offset += CircleOffset;
  232.       break;
  233.     }
  234.   case EProjectileMotionType::Snake:
  235.     {
  236.       Offset += FVector(
  237.         0.f,
  238.         FMath::Sin(MotionPhase + RandomPhaseShift - PI / 2) * EffectiveAmplitude *
  239.         DirectionMultiplier,
  240.         0.f
  241.       );
  242.       break;
  243.     }
  244.  
  245.   case EProjectileMotionType::Noise:
  246.     {
  247.       NoiseTimer += DeltaTime;
  248.  
  249.       if (NoiseTimer >= EffectiveNoiseInterval)
  250.       {
  251.         // Time to pick a new target offset
  252.         TargetNoiseOffset = FVector(
  253.           FMath::FRandRange(-1.f, 1.f) * EffectiveAmplitude,
  254.           FMath::FRandRange(-1.f, 1.f) * EffectiveAmplitude,
  255.           0.f
  256.         );
  257.         NoiseTimer = 0.f;
  258.       }
  259.  
  260.       CurrentNoiseOffset = FMath::VInterpTo(
  261.         CurrentNoiseOffset,
  262.         TargetNoiseOffset,
  263.         DeltaTime,
  264.         NoiseInterpSpeed
  265.       );
  266.  
  267.       Offset += CurrentNoiseOffset;
  268.       break;
  269.     }
  270.  
  271.   case EProjectileMotionType::Pulse:
  272.     {
  273.       Offset += FVector(
  274.         FMath::Cos(MotionPhase + RandomPhaseShift) * EffectiveAmplitude * DirectionMultiplier,
  275.         0.f,
  276.         0.f
  277.       );
  278.       break;
  279.     }
  280.  
  281.   case EProjectileMotionType::ZigZag:
  282.     {
  283.       const float Phase = FMath::Sin(MotionPhase + RandomPhaseShift - PI / 2);
  284.       Offset += FVector(
  285.         0.f,
  286.         FMath::Sign(Phase) * EffectiveAmplitude * DirectionMultiplier,
  287.         0.f
  288.       );
  289.       break;
  290.     }
  291.   default:
  292.     {
  293.       break;
  294.     }
  295.   }
  296.  
  297.   return StartTransform.TransformVectorNoScale(Offset);
  298. }
  299.  
  300. FVector UAuraProjectileMovementComponent::ComputeBezierPosition(float T) const
  301. {
  302.   const FVector P0 = StartLocation;
  303.   FVector P1 = StartLocation + StartTransform.TransformVectorNoScale(BezierP1);
  304.   FVector P2 = StartLocation + StartTransform.TransformVectorNoScale(BezierP2);
  305.   FVector P3 = StartLocation + StartTransform.TransformVectorNoScale(EffectiveBezierEnd);
  306.  
  307.   if (MotionShiftModes.Contains(EMotionShiftMode::ControlPoint))
  308.   {
  309.     P1 = StartLocation + StartTransform.TransformVectorNoScale(
  310.       RandomBezierRotation.RotateVector(BezierP1)
  311.     );
  312.     P2 = StartLocation + StartTransform.TransformVectorNoScale(
  313.       RandomBezierRotation.RotateVector(BezierP2)
  314.     );
  315.     P3 = StartLocation + StartTransform.TransformVectorNoScale(
  316.       RandomBezierRotation.RotateVector(EffectiveBezierEnd)
  317.     );
  318.   }
  319.  
  320.   const float U = 1 - T;
  321.   const float TT = T * T;
  322.   const float UU = U * U;
  323.   const float UUU = UU * U;
  324.   const float TTT = TT * T;
  325.  
  326.   return UUU * P0 +
  327.     3 * UU * T * P1 +
  328.     3 * U * TT * P2 +
  329.     TTT * P3;
  330. }
  331.  
  332. void UAuraProjectileMovementComponent::ReturnYoYo()
  333. {
  334.   Velocity = -StartVelocity * EffectiveYoYoReturnSpeedFactor;
  335.   bYoYoReturning = true;
  336.   OnYoYoReturnDelegate.Broadcast();
  337.  
  338.   if (bYoYoReturnToAvatar && AvatarActor.IsValid())
  339.   {
  340.     bIsHomingProjectile = true;
  341.     HomingTargetComponent = AvatarActor->GetRootComponent();
  342.   }
  343. }
  344.  
Advertisement
Add Comment
Please, Sign In to add comment