krazwer

AuraProjectileMovementComponent.cpp

Sep 4th, 2025
151
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.24 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.  
  120.   FVector TotalOffset;
  121.  
  122.   // --- Bezier full override ---
  123.   if (ActiveMotion == EProjectileMotionType::Bezier)
  124.   {
  125.     float T = ElapsedTime / EffectiveBezierDuration;
  126.  
  127.     if (MotionShiftModes.Contains(EMotionShiftMode::Phase))
  128.     {
  129.       T += RandomPhaseShift / (2.f * PI);
  130.     }
  131.  
  132.     T = FMath::Clamp(T, 0.f, 1.f);
  133.  
  134.     const FVector NewPos = ComputeBezierPosition(T);
  135.     const FVector PrevPos = ComputeBezierPosition(FMath::Clamp(T - DeltaTime / BezierDuration, 0.f, 1.f));
  136.  
  137.     TotalOffset = NewPos - PrevPos;
  138.  
  139.     UpdatedComponent->AddWorldOffset(TotalOffset, false);
  140.     Velocity = TotalOffset / DeltaTime;
  141.     return;
  142.   }
  143.  
  144.   // Base forward move
  145.   const FVector ForwardMove = StartVelocity * DeltaTime;
  146.  
  147.   // Additional motions
  148.   const FVector Offset = ComputeMotionOffset(DeltaTime);
  149.  
  150.   TotalOffset = ForwardMove + Offset;
  151.  
  152.   UpdatedComponent->AddWorldOffset(TotalOffset, false);
  153.  
  154.   Velocity = TotalOffset / DeltaTime;
  155. }
  156.  
  157. void UAuraProjectileMovementComponent::SetActiveMotion(EProjectileMotionType Motion)
  158. {
  159.   ActiveMotion = Motion;
  160. }
  161.  
  162. float UAuraProjectileMovementComponent::ComputeEffectiveAmplitude()
  163. {
  164.   float EffectiveAmplitude = Amplitude;
  165.   if (MotionShiftModes.Contains(EMotionShiftMode::Growth))
  166.   {
  167.     EffectiveAmplitude += GrowthRate * ElapsedTime * EffectiveFrequency;
  168.   }
  169.  
  170.   return EffectiveAmplitude;
  171. }
  172.  
  173. void UAuraProjectileMovementComponent::SetupYoYoMotion()
  174. {
  175.   // ---- Forward phase ----
  176.   FTimerHandle ForwardPhaseTimer;
  177.   GetOwner()->GetWorldTimerManager().SetTimer(
  178.     ForwardPhaseTimer,
  179.     FTimerDelegate::CreateLambda([this]()
  180.     {
  181.       if (EffectiveYoYoIdleDuration > 0)
  182.       {
  183.         // ---- Idle phase ----
  184.         Velocity = FVector::ZeroVector;
  185.         FTimerHandle IdlePhaseTimer;
  186.         GetOwner()->GetWorldTimerManager().SetTimer(
  187.           IdlePhaseTimer,
  188.           FTimerDelegate::CreateLambda([this]()
  189.           {
  190.             // ---- Return phase ----
  191.             ReturnYoYo();
  192.           }),
  193.           EffectiveYoYoIdleDuration,
  194.           false
  195.         );
  196.  
  197.         return;
  198.       }
  199.  
  200.       // ---- Return phase ----
  201.       ReturnYoYo();
  202.     }),
  203.     EffectiveYoYoForwardDuration,
  204.     false
  205.   );
  206. }
  207.  
  208. FVector UAuraProjectileMovementComponent::ComputeMotionOffset(float DeltaTime)
  209. {
  210.   FVector Offset = FVector::ZeroVector;
  211.  
  212.   switch (ActiveMotion)
  213.   {
  214.   case EProjectileMotionType::Helix:
  215.     {
  216.       const float EffectiveAmplitude = ComputeEffectiveAmplitude();
  217.       const FVector Forward = StartVelocity.GetSafeNormal();
  218.       FVector Right, Up;
  219.       Forward.FindBestAxisVectors(Right, Up);
  220.  
  221.       const float Angle = ElapsedTime * EffectiveFrequency + RandomPhaseShift;
  222.       const FVector CircleOffset =
  223.         (FMath::Sin(Angle) * Right + FMath::Cos(Angle) * Up) *
  224.         EffectiveAmplitude * DirectionMultiplier;
  225.  
  226.       Offset += CircleOffset;
  227.  
  228.       break;
  229.     }
  230.   case EProjectileMotionType::Snake:
  231.     {
  232.       const float EffectiveAmplitude = ComputeEffectiveAmplitude();
  233.  
  234.       Offset += FVector(
  235.         0.f,
  236.         FMath::Sin(ElapsedTime * EffectiveFrequency + RandomPhaseShift - PI / 2) * EffectiveAmplitude *
  237.         DirectionMultiplier,
  238.         0.f
  239.       );
  240.       break;
  241.     }
  242.  
  243.   case EProjectileMotionType::Noise:
  244.     {
  245.       NoiseTimer += DeltaTime;
  246.  
  247.       const float EffectiveAmplitude = ComputeEffectiveAmplitude();
  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.       const float EffectiveAmplitude = ComputeEffectiveAmplitude();
  274.  
  275.       Offset += FVector(
  276.         FMath::Cos(ElapsedTime * EffectiveFrequency + RandomPhaseShift) * EffectiveAmplitude * DirectionMultiplier,
  277.         0.f,
  278.         0.f
  279.       );
  280.       break;
  281.     }
  282.  
  283.   case EProjectileMotionType::ZigZag:
  284.     {
  285.       const float EffectiveAmplitude = ComputeEffectiveAmplitude();
  286.  
  287.       const float Phase = FMath::Sin(ElapsedTime * EffectiveFrequency + RandomPhaseShift - PI / 2);
  288.       Offset += FVector(
  289.         0.f,
  290.         FMath::Sign(Phase) * EffectiveAmplitude * DirectionMultiplier,
  291.         0.f
  292.       );
  293.       break;
  294.     }
  295.   default:
  296.     {
  297.       break;
  298.     }
  299.   }
  300.  
  301.   return StartTransform.TransformVectorNoScale(Offset);
  302. }
  303.  
  304. FVector UAuraProjectileMovementComponent::ComputeBezierPosition(float T) const
  305. {
  306.   const FVector P0 = StartLocation;
  307.   FVector P1 = StartLocation + StartTransform.TransformVectorNoScale(BezierP1);
  308.   FVector P2 = StartLocation + StartTransform.TransformVectorNoScale(BezierP2);
  309.   FVector P3 = StartLocation + StartTransform.TransformVectorNoScale(EffectiveBezierEnd);
  310.  
  311.   if (MotionShiftModes.Contains(EMotionShiftMode::ControlPoint))
  312.   {
  313.     P1 = StartLocation + StartTransform.TransformVectorNoScale(
  314.       RandomBezierRotation.RotateVector(BezierP1)
  315.     );
  316.     P2 = StartLocation + StartTransform.TransformVectorNoScale(
  317.       RandomBezierRotation.RotateVector(BezierP2)
  318.     );
  319.     P3 = StartLocation + StartTransform.TransformVectorNoScale(
  320.       RandomBezierRotation.RotateVector(EffectiveBezierEnd)
  321.     );
  322.   }
  323.  
  324.   const float U = 1 - T;
  325.   const float TT = T * T;
  326.   const float UU = U * U;
  327.   const float UUU = UU * U;
  328.   const float TTT = TT * T;
  329.  
  330.   return UUU * P0 +
  331.     3 * UU * T * P1 +
  332.     3 * U * TT * P2 +
  333.     TTT * P3;
  334. }
  335.  
  336. void UAuraProjectileMovementComponent::ReturnYoYo()
  337. {
  338.   Velocity = -StartVelocity * EffectiveYoYoReturnSpeedFactor;
  339.   bYoYoReturning = true;
  340.  
  341.   if (bYoYoReturnToAvatar && AvatarActor.IsValid())
  342.   {
  343.     bIsHomingProjectile = true;
  344.     HomingTargetComponent = AvatarActor->GetRootComponent();
  345.   }
  346. }
  347.  
Advertisement
Add Comment
Please, Sign In to add comment