Advertisement
TheRedPixel

Untitled

Apr 4th, 2024 (edited)
276
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.25 KB | None | 0 0
  1. // by RedPixel Studio
  2.  
  3.  
  4. #include "EnemyState_MovingAroundBlockade.h"
  5.  
  6. #include "Components/ShapeComponent.h"
  7. #include "FiniteStateMachine/FiniteStateMachine.h"
  8. //Seekinng
  9. #include "EnemyGlobalMachineState.h"
  10. #include "EnemyState_Seeking.h"
  11. #include "REnemyAIController.h"
  12. #include "Kismet/KismetMathLibrary.h"
  13. #include "NativeGameplayTags.h"
  14. #include "GameFramework/Character.h"
  15. #include "Kismet/GameplayStatics.h"
  16. #include "Kismet/KismetSystemLibrary.h"
  17. #include "Navigation/PathFollowingComponent.h"
  18. //#include "Physics/MTD_CollisionChannels.h"
  19. //#include "Utilities/MTD_GameplayStatics.h"
  20. //#include "Utilities/MTD_Log.h"
  21.  
  22. UE_DEFINE_GAMEPLAY_TAG(TAG_StateMachine_Label_MoveAround, "StateMachine.Label.MoveAround");
  23.  
  24.  
  25. #define DRAW_DEBUG
  26. #define DRAW_TIME 2.f
  27. #define DRAW_THICKNESS 1.f
  28. #ifdef DRAW_DEBUG
  29.     #define DRAW_DEBUG_TYPE EDrawDebugTrace::ForDuration
  30. #elif
  31.     #define DRAW_DEBUG_TYPE EDrawDebugTrace::None
  32. #endif
  33.  
  34. UEnemyState_MovingAroundBlockade::UEnemyState_MovingAroundBlockade()
  35. {
  36.     StateDataClass = UEnemyStateData_MovingAroundBlockade::StaticClass();
  37.  
  38.    /* InitialLabelsToRegister.Add(TAG_StateMachine_Label_MoveAround,
  39.         FLabelSignature::CreateUObject(this, &ThisClass::MoveAroundBlockade));*/
  40. }
  41.  
  42.  
  43. float getTime(const UObject* ContextObject)
  44. {
  45.     const UWorld* World = GEngine->GetWorldFromContextObject(ContextObject, EGetWorldErrorMode::LogAndReturnNull);
  46.     const float Time = World ? World->GetTimeSeconds() : 0.0;
  47.     return Time;
  48. }
  49.  
  50. float timeSince(const UObject* ContextObject, float Time)
  51. {
  52.     const float TimeSince = getTime(ContextObject) - Time;
  53.     return TimeSince;
  54. }
  55.  
  56. bool UEnemyState_MovingAroundBlockade::MoveAroundBlockade(UFiniteStateMachine* StateMachineContext,
  57.     AActor* BlockadeActor, float BlockadeWidth, FVector HitNormal, bool bSkipBlockingCheck /*false*/)
  58. {
  59.     if (!IsValid(BlockadeActor))
  60.     {
  61.         return false;
  62.     }
  63.  
  64.     auto* StateData = StateMachineContext->GetStateDataChecked<UEnemyStateData_MovingAroundBlockade>(StaticClass());
  65.     const AActor* Avatar = StateMachineContext->GetAvatar();
  66.     if (!IsValid(Avatar))
  67.     {
  68.         return false;
  69.     }
  70.     const FVector Velocity = Avatar->GetVelocity();
  71.     const float SpeedSq = Velocity.SquaredLength();
  72.     const float MoveAroundSomethingMaximumSpeedSq = FMath::Square(StateData->MoveAroundSomethingMaximumSpeed);
  73.  
  74.    
  75.     if (SpeedSq > MoveAroundSomethingMaximumSpeedSq)
  76.     {
  77.         return false;
  78.     }
  79.     // prevent enemies from moving around each other again as they all try to move around each other
  80.     if (StateData->bIgnoreMoveAroundBlockade)
  81.     {
  82.         return false;
  83.     }
  84.  
  85.     // if (!bReachedInitialTarget)
  86.     // {
  87.     //  return false;
  88.     // }
  89.  
  90.     // bInvicible?
  91.  
  92.     // Only move-around-blockade when seeking or moving around a blockade
  93.     if (!StateMachineContext->IsInState(UEnemyState_Seeking::StaticClass()) &&
  94.         !StateMachineContext->IsInState(UEnemyState_MovingAroundBlockade::StaticClass()))
  95.     {
  96.         return false;
  97.     }
  98.  
  99.     if (timeSince(StateMachineContext, StateData->LastBlockadeCheckTime) < 0.5f)
  100.     {
  101.        
  102.         return false;
  103.     }
  104.  
  105.     const auto* GlobalStateData = StateMachineContext->GetStateDataChecked<UEnemyGlobalMachineStateData>(
  106.         UGlobalMachineState::StaticClass());
  107.  
  108.     if (GlobalStateData->Target == BlockadeActor)
  109.     {
  110.        
  111.         return false;
  112.     }
  113.  
  114.     if (BlockadeActor == StateData->LastMovingAroundBlockadeActor &&
  115.         timeSince(StateMachineContext, StateData->LastMovingAroundBlockadeTime) < 2.6f)
  116.     {
  117.         return false;
  118.     }
  119.  
  120.     StateData->LastBlockadeCheckTime = getTime(StateMachineContext);
  121.  
  122.     //guess based on its collision component
  123.     if (BlockadeWidth == 0.f)
  124.     {
  125.         const auto* CollisionComponent = BlockadeActor->FindComponentByTag<UShapeComponent>(UEnemyStateData_MovingAroundBlockade::CollisionComponentTag);
  126.         if (!IsValid(CollisionComponent))
  127.         {
  128.             return false;
  129.         }
  130.  
  131.         BlockadeWidth = CollisionComponent->Bounds.SphereRadius;
  132.     }
  133.    
  134.     const FRotator AvatarRotator = Avatar->GetActorRotation();
  135.     const FVector AvatarRotationVector = AvatarRotator.Vector();
  136.     const float Dot = FVector::DotProduct(HitNormal.GetSafeNormal2D(), AvatarRotationVector.GetSafeNormal2D());
  137.     if (Dot > 0.f)
  138.     {
  139.         return false;
  140.     }
  141.  
  142.     const FVector OurPosition = Avatar->GetActorLocation();
  143.     const float AngleBetween = 180.f - UKismetMathLibrary::DegAcos(Dot);
  144.     const FVector RotatedHitNormal = HitNormal.RotateAngleAxis(AngleBetween, FVector::UpVector);
  145.     const FRotator HitNormalRotator = RotatedHitNormal.Rotation();
  146.  
  147.     const UWorld* World = StateMachineContext->GetWorld();
  148.     check(IsValid(World));
  149.  
  150.     // Only run the blockade-validity if specified to (do by default). Switches Directions
  151.     if (!bSkipBlockingCheck)
  152.     {
  153.         // if this blockade actually is in the way of our destination
  154.  
  155.        
  156.         const FVector TargetDesinationPosition = FVector::ZeroVector; // @todo MoveToTarget state
  157.         FVector Delta = FVector::ZeroVector;
  158.         if (!TargetDesinationPosition.IsZero())
  159.         {
  160.             Delta = TargetDesinationPosition - OurPosition;
  161.         }
  162.         else if (GlobalStateData->Target.IsValid())
  163.         {
  164.             Delta = GlobalStateData->Target->GetActorLocation() - OurPosition;
  165.         }
  166.  
  167.         FVector TraceThroughBlockadePosition = FVector::ZeroVector;
  168.         if (!Delta.IsZero())
  169.         {
  170.             TraceThroughBlockadePosition = Delta.GetSafeNormal() * BlockadeWidth * 1.5f + OurPosition;
  171.         }
  172.  
  173.         bool bFoundAnyTraceCheck = false;
  174.  
  175.         // In case of Nn target destination
  176.         if (!TraceThroughBlockadePosition.IsZero())
  177.         {
  178. #ifdef DRAW_DEBUG
  179.             DrawDebugSphere(World, TraceThroughBlockadePosition, 30, 16, FColor::Red, false, DRAW_TIME, 0, DRAW_THICKNESS);
  180.             DrawDebugSphere(World, OurPosition, 30, 16, FColor::Green, false, DRAW_TIME, 0, DRAW_THICKNESS);
  181.             DrawDebugLine(World, OurPosition, TraceThroughBlockadePosition, FColor::White, false, DRAW_TIME, 0, DRAW_THICKNESS);
  182. #endif
  183.  
  184.             const auto* CollisionSettings = GetDefault<UCollisionProfile>();
  185.             const ETraceTypeQuery TraceChannel = CollisionSettings->ConvertToTraceType(ECC_Visibility);
  186.  
  187.             TArray<FHitResult> Hits;
  188.             UKismetSystemLibrary::BoxTraceMulti(Avatar, TraceThroughBlockadePosition, OurPosition,
  189.                 FVector(BlockadeWidth, BlockadeWidth, 0) / 2.f * 0.9f, FRotator::ZeroRotator, TraceChannel, false, {},
  190.                 DRAW_DEBUG_TYPE, OUT Hits, true, FLinearColor::Red, FLinearColor::Green, DRAW_TIME);
  191.  
  192.             // World->LineTraceMultiByObjectType(Hits, TraceThroughBlockadePosition, OurPosition, ObjectQueryParams,
  193.             //  CollisionQueryParams);
  194.  
  195.            
  196.             for (const FHitResult& Hit : Hits)
  197.             {
  198.                 const AActor* HitActor = Hit.GetActor();
  199.                 /*MTDC_VVERBOSE(MTD_LogFoe_MoveAroundBlockade, StateMachineContext, "Collided with blockade [%s] "
  200.                     "Trace checked [%s].", *BlockadeActor->GetName(), *HitActor->GetName());*/
  201.  
  202.                 if (HitActor == BlockadeActor)
  203.                 {
  204.                     bFoundAnyTraceCheck = true;
  205.                     break;
  206.                 }
  207.             }
  208.         }
  209.  
  210.         // If blockade does not appear to be in the way of our target destination, cancel moving around it
  211.        
  212.         if (!bFoundAnyTraceCheck)
  213.         {
  214.             /*MTDC_LOG(MTD_LogFoe_MoveAroundBlockade, StateMachineContext, "Ignoring... Collided with blockade [%s] but "
  215.                 "it wasn't in trace path towards our target.", *BlockadeActor->GetName());*/
  216.             return false;
  217.         }
  218.     }
  219.  
  220.     const UShapeComponent* CollisionComponent = Avatar->FindComponentByTag<UShapeComponent>(UEnemyStateData_MovingAroundBlockade::CollisionComponentTag);
  221.     if (!IsValid(CollisionComponent))
  222.     {
  223.         /*MTDC_WARN(MTD_LogFoe_MoveAroundBlockade, StateMachineContext, "Cancelling... Our pawn [%s] lacks a collision "
  224.             "component with tag [%s].", *Avatar->GetName(), *CollisionComponentTag.ToString());*/
  225.         return false;
  226.     }
  227.  
  228.     const float OurWidth = CollisionComponent->Bounds.SphereRadius;
  229.     FVector Offset;
  230.  
  231.     // Start with a navigation offset that is sideways, so that we will be moving to the left or right of the blockade.
  232.    
  233.     Offset.Y = StateData->MovingAroundBlockadeSide * (BlockadeWidth + OurWidth);
  234.  
  235.     // Push the offset a little backwards so we tend to navigate around the blockade
  236.     Offset.X = -50.f - OurWidth;
  237.  
  238.     const FVector BlockadePosition = BlockadeActor->GetActorLocation();
  239. #ifdef DRAW_DEBUG
  240.     DrawDebugLine(World, BlockadePosition, BlockadePosition + HitNormal * 150.f, FColor::Red, false, 2.f, 0, 1.f);
  241. #endif
  242.  
  243.     // Calculate the final move-around point
  244.     StateData->MovingAroundBlockadePoint = BlockadePosition + HitNormalRotator.RotateVector(Offset);
  245.  
  246.     // Ensure the point we're attempting to navigate to is at our height
  247.     StateData->MovingAroundBlockadePoint.Z = OurPosition.Z;
  248.  
  249. #ifdef DRAW_DEBUG
  250.     DrawDebugSphere(World, StateData->MovingAroundBlockadePoint, 15.f, 6, FColor::Cyan, false, 2.f, 0, 1.f);
  251. #endif
  252.  
  253.     // Check that the point we're going to try to navigate to around the blockade actually is a decent point --
  254.     // that it has ground underneath it; if it's not a valid point, then switch moving side
  255.     if (!CheckMoveAroundBlockadePoint(StateMachineContext, StateData->MovingAroundBlockadePoint))
  256.     {
  257.         // Store the new side so that subsequent blockade movements will also travel in it
  258.         StateData->MovingAroundBlockadeSide *= -1.f;
  259.  
  260.         Offset.Y *= -1.f;
  261.  
  262.         StateData->MovingAroundBlockadePoint = BlockadePosition + HitNormalRotator.RotateVector(Offset);
  263.  
  264.        
  265.         StateData->MovingAroundBlockadePoint.Z = OurPosition.Z;
  266.  
  267.         /*MTDC_LOG(MTD_LogFoe_MoveAroundBlockade, StateMachineContext, "Failed going [%s] side, trying to go the other "
  268.             "one...", StateData->MovingAroundBlockadeSide == 1.f ? TEXT("Right") : TEXT("Left"));*/
  269.  
  270.        
  271.         if(!CheckMoveAroundBlockadePoint(StateMachineContext, StateData->MovingAroundBlockadePoint))
  272.         {
  273.             // If we're here, then there were no valid points around the blocakde, so...
  274.             // This blockade is totally blocking us, so perhaps let's try to destroy it!
  275.             // @todo AggroEntries.Remove(0,AggroEntries.Length);
  276.             // @todo AddToAggro(DunDefTargetableInterface(blockadeActor),1.0);
  277.  
  278.             //MTDC_LOG(MTD_LogFoe_MoveAroundBlockade, StateMachineContext, "Failed final viability check.");
  279.             return false;
  280.         }
  281.     }
  282.  
  283.     //MTDC_LOG(MTD_LogFoe_MoveAroundBlockade, StateMachineContext, "Moving around blockade...");
  284.  
  285.    
  286.     StateData->LastMovingAroundBlockadeActor = BlockadeActor;
  287.  
  288.     // @todo    FindSpot(Pawn.CollisionComponent.Bounds.BoxExtent, MovingAroundBlockadePoint);
  289.  
  290.     StateData->LastBlockadeWidth = BlockadeWidth;
  291.     StateData->LastBlockadeHitNormal = HitNormal;
  292.  
  293.     // Ready to enter or restart the MovingAroundBlockade state and attempt to head to our point
  294.    
  295.     if (!bSkipBlockingCheck)
  296.     {
  297.         // If we did a full check, then begin a completely new blockade movement, starting at the beginning of the
  298.         // MovingAroundBlockade state code
  299.         StateData->LastMovingAroundBlockadeTime = getTime(StateMachineContext);
  300.         StateData->StartMovingAroundBlockadeLocation = OurPosition;
  301.  
  302.         // StateMachineContext->GotoState(StaticClass());
  303.         StateMachineContext->GotoState(StaticClass(), TAG_StateMachine_Label_MoveAround);
  304.     }
  305.     else
  306.     {
  307.        
  308.         StateMachineContext->GotoState(StaticClass(), TAG_StateMachine_Label_MoveAround);
  309.     }
  310.  
  311.     return true;
  312. }
  313.  
  314. bool UEnemyState_MovingAroundBlockade::CheckMoveAroundBlockadePoint(const UFiniteStateMachine* StateMachineContext,
  315.     const FVector& MoveToPoint)
  316. {
  317.     const AActor* Avatar = StateMachineContext->GetAvatar();
  318.     check(IsValid(Avatar));
  319.  
  320.     const UShapeComponent* CollisionComponent = Avatar->FindComponentByTag<UShapeComponent>(
  321.         UEnemyStateData_MovingAroundBlockade::CollisionComponentTag);
  322.     check(IsValid(CollisionComponent));
  323.  
  324.     const FVector Offset = FVector::ZAxisVector * CollisionComponent->Bounds.SphereRadius * -2.f;
  325.  
  326.     FHitResult Hit;
  327.     const bool bHasGround = Avatar->GetWorld()->LineTraceSingleByChannel(OUT Hit, MoveToPoint, MoveToPoint + Offset,
  328.         ECC_Visibility);
  329.  
  330.     return bHasGround;
  331. }
  332.  
  333. void UEnemyState_MovingAroundBlockade::Begin(TSubclassOf<UMachineState> PreviousState)
  334. {
  335.     Super::Begin(PreviousState);
  336.  
  337.     if (!StateData.IsValid())
  338.     {
  339.         StateData = StateMachine->GetStateDataChecked<UEnemyStateData_MovingAroundBlockade>(StaticClass());
  340.     }
  341.  
  342.     FTimerManager& TimerManager = GetWorld()->GetTimerManager();
  343.     TimerManager.SetTimer(CancelMoveAroundTimer, this, &ThisClass::CancelMoveAround, 4.f, false);
  344.     TimerManager.SetTimer(TryOtherBlockadeDirectionTimer, this, &ThisClass::TryOtherBlockadeDirection, 0.6f, true);
  345. }
  346.  
  347. void UEnemyState_MovingAroundBlockade::Tick(float DeltaSeconds)
  348. {
  349.     Super::Tick(DeltaSeconds);
  350.  
  351.     // Check if we don't appear to be moving or have not moved 1.5 seconds after entering this state, if not, cancel the move around
  352.     if (TimeSince(StateData->LastMovingAroundBlockadeTime) > 1.5f)
  353.     {
  354.         const FVector OurPosition = Character->GetActorLocation();
  355.         const FVector DistanceVector = OurPosition - StateData->StartMovingAroundBlockadeLocation;
  356.         const float DistanceSq = DistanceVector.SquaredLength();
  357.         if (DistanceSq < 0.01f)
  358.         {
  359.             CancelMoveAround();
  360.         }
  361.         else
  362.         {
  363.             const FVector Velocity = Character->GetVelocity();
  364.             const float SpeedSq = Velocity.SquaredLength();
  365.             if (SpeedSq < 1.f)
  366.             {
  367.                 CancelMoveAround();
  368.             }
  369.         }
  370.     }
  371. }
  372.  
  373. void UEnemyState_MovingAroundBlockade::End(TSubclassOf<UMachineState> NewState)
  374. {
  375.     CancelMoveAround();
  376.     Super::End(NewState);
  377. }
  378.  
  379. void UEnemyState_MovingAroundBlockade::MoveAroundBlockade(float DeltaSeconds)
  380. {
  381.     Controller->MoveToLocation(StateData->MovingAroundBlockadePoint, 50.f, false, false);
  382. }
  383.  
  384. void UEnemyState_MovingAroundBlockade::CancelMoveAround()
  385. {
  386.     FTimerManager& TimerManager = GetWorld()->GetTimerManager();
  387.     TimerManager.ClearTimer(CancelMoveAroundTimer);
  388.     TimerManager.ClearTimer(TryOtherBlockadeDirectionTimer);
  389.  
  390.     StateMachine->GotoState(UEnemyState_Seeking::StaticClass());
  391. }
  392.  
  393. void UEnemyState_MovingAroundBlockade::TryOtherBlockadeDirection()
  394. {
  395.     const FVector Velocity = Character->GetVelocity();
  396.     const float SpeedSq = Velocity.SquaredLength();
  397.     if (SpeedSq < 25.f * 25.f)
  398.     {
  399.        
  400.  
  401.         StateData->MovingAroundBlockadeSide *= -1.f;
  402.         AActor* BlockadeActor = StateData->LastMovingAroundBlockadeActor.Get();
  403.         StateData->LastMovingAroundBlockadeActor.Reset();
  404.         MoveAroundBlockade(StateMachine.Get(), BlockadeActor, StateData->LastBlockadeWidth,
  405.             StateData->LastBlockadeHitNormal, true);
  406.     }
  407. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement