Files
PHY/Plugins/GCS/Source/GenericCombatSystem/Private/Targeting/GCS_TargetingSystemComponent.cpp
2026-03-03 01:23:02 +08:00

413 lines
11 KiB
C++

// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/GCS_TargetingSystemComponent.h"
#include "GCS_LogChannels.h"
#include "Kismet/KismetMathLibrary.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Character.h"
#include "Camera/PlayerCameraManager.h"
#include "Net/UnrealNetwork.h"
#include "TargetingSystem/TargetingSubsystem.h"
UGCS_TargetingSystemComponent::UGCS_TargetingSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
SetIsReplicatedByDefault(true);
}
UGCS_TargetingSystemComponent* UGCS_TargetingSystemComponent::GetTargetingSystemComponent(const AActor* Actor)
{
return Actor ? Actor->FindComponentByClass<UGCS_TargetingSystemComponent>() : nullptr;
}
void UGCS_TargetingSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(ThisClass, TargetedActor, COND_OwnerOnly);
}
// Called when the game starts
void UGCS_TargetingSystemComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UGCS_TargetingSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
RefreshTargeting(DeltaTime);
}
void UGCS_TargetingSystemComponent::RefreshTargeting(float DeltaTime)
{
if (GetOwnerRole() >= ROLE_AutonomousProxy)
{
if (bAutoUpdatePotentialTargets || IsValid(TargetedActor))
{
RefreshPotentialTargets();
}
bool LocalBool = PotentialTargets.Contains(TargetedActor);
// Current TargetedActor no longer consider valid.
if (TargetedActor && !LocalBool)
{
OnLockOff();
SetTargetedActor(nullptr);
}
}
}
void UGCS_TargetingSystemComponent::SearchForActorToTarget()
{
RefreshPotentialTargets();
SelectFromPotentialTargets();
//No target found/unlocked.
if (!IsValid(TargetedActor) && !bAutoUpdatePotentialTargets)
{
PotentialTargets.Empty();
}
}
AActor* UGCS_TargetingSystemComponent::SelectClosestActorFromPotentialTargets(float Radius) const
{
// 检查 Owner 是否有效
if (!GetOwner())
{
UE_LOG(LogTemp, Warning, TEXT("SelectClosestActorFromPotentialTargets: Owner is null"));
return nullptr;
}
// 检查 PotentialTargets 是否为空
if (PotentialTargets.Num() == 0)
{
return nullptr;
}
// 过滤有效 Actor 并在范围内
TArray<AActor*> FilteredPotentialTargets;
const FVector OwnerLocation = GetOwner()->GetActorLocation();
for (AActor* Actor : PotentialTargets)
{
// 检查 Actor 是否有效且未被销毁
if (IsValid(Actor))
{
float Distance = FVector::Dist(OwnerLocation, Actor->GetActorLocation());
if (Distance <= Radius)
{
FilteredPotentialTargets.Add(Actor);
}
}
}
// 如果过滤后没有有效目标
if (FilteredPotentialTargets.Num() == 0)
{
return nullptr;
}
// 寻找最近的 Actor
AActor* ClosestActor = FilteredPotentialTargets[0];
float MinDistance = FVector::Dist(OwnerLocation, ClosestActor->GetActorLocation());
for (int32 i = 1; i < FilteredPotentialTargets.Num(); ++i)
{
AActor* CurrentActor = FilteredPotentialTargets[i];
if (IsValid(CurrentActor))
{
float Distance = FVector::Dist(OwnerLocation, CurrentActor->GetActorLocation());
if (Distance < MinDistance)
{
MinDistance = Distance;
ClosestActor = CurrentActor;
}
}
}
return ClosestActor;
}
bool UGCS_TargetingSystemComponent::FilterActorsWithPreset(UTargetingPreset* InTargetingPreset, const TArray<AActor*> InTargets, TArray<AActor*>& OutActors)
{
if (InTargetingPreset == nullptr)
{
return false;
}
if (UTargetingSubsystem* TargetingSubsystem = UTargetingSubsystem::Get(GetWorld()))
{
FTargetingSourceContext SourceContext;
SourceContext.SourceActor = GetOwner();
FTargetingRequestHandle TargetingHandle = UTargetingSubsystem::MakeTargetRequestHandle(TargetingPreset, SourceContext);
if (TargetingHandle.IsValid() && InTargets.Num() > 0)
{
FTargetingDefaultResultsSet& TargetingResults = FTargetingDefaultResultsSet::FindOrAdd(TargetingHandle);
for (AActor* Target : InTargets)
{
if (!Target)
{
continue;
}
bool bAddResult = !TargetingResults.TargetResults.ContainsByPredicate([Target](const FTargetingDefaultResultData& Data) -> bool
{
return (Data.HitResult.GetActor() == Target);
});
if (bAddResult)
{
FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData();
ResultData->HitResult.HitObjectHandle = FActorInstanceHandle(Target);
ResultData->HitResult.Location = Target->GetActorLocation();
}
}
}
FTargetingRequestDelegate Delegate = FTargetingRequestDelegate::CreateWeakLambda(this, [&](FTargetingRequestHandle InTargetingHandle)
{
TargetingSubsystem->GetTargetingResultsActors(InTargetingHandle, OutActors);
});
FTargetingImmediateTaskData& ImmeidateTaskData = FTargetingImmediateTaskData::FindOrAdd(TargetingHandle);
ImmeidateTaskData.bReleaseOnCompletion = true;
TargetingSubsystem->ExecuteTargetingRequestWithHandle(TargetingHandle, Delegate);
}
return !OutActors.IsEmpty();
}
void UGCS_TargetingSystemComponent::SetTargetedActor(AActor* NewActor)
{
SetTargetedActor(NewActor, true);
}
void UGCS_TargetingSystemComponent::SetTargetedActor(AActor* NewActor, bool bSendRpc)
{
if (GetOwnerRole() < ROLE_AutonomousProxy)
{
return;
}
if (bSendRpc)
{
if (GetOwnerRole() >= ROLE_Authority)
{
ClientSetTargetedActor(NewActor);
}
else
{
ServerSetTargetedActor(NewActor);
}
}
TargetedActor = NewActor;
}
void UGCS_TargetingSystemComponent::ClientSetTargetedActor_Implementation(AActor* NewActor)
{
SetTargetedActor(NewActor, false);
}
void UGCS_TargetingSystemComponent::ServerSetTargetedActor_Implementation(AActor* NewActor)
{
SetTargetedActor(NewActor, false);
}
void UGCS_TargetingSystemComponent::OnLockOff_Implementation()
{
OnTargetLockOffEvent.Broadcast(TargetedActor);
}
void UGCS_TargetingSystemComponent::OnLockOn_Implementation()
{
OnTargetLockOnEvent.Broadcast(TargetedActor);
}
void UGCS_TargetingSystemComponent::SelectFromPotentialTargets()
{
if (!IsValid(TargetedActor))
{
TMap<AActor*, float> LocalPotentialTargets;
for (TObjectPtr<AActor>& Elem : PotentialTargets)
{
if (CanBeTargeted(Elem))
{
LocalPotentialTargets.Add(Elem, UKismetMathLibrary::Abs(CalculateViewAngle(Elem)));
}
}
if (LocalPotentialTargets.Num() > 0)
{
TArray<AActor*> LocalTargets;
TArray<float> LocalAngles;
LocalPotentialTargets.GenerateKeyArray(LocalTargets);
LocalPotentialTargets.GenerateValueArray(LocalAngles);
int32 MinIndex;
const float MaxValue = FMath::Min<float>(LocalAngles, &MinIndex);
SetTargetedActor(LocalTargets[MinIndex]);
OnLockOn();
}
}
else
{
OnLockOff();
SetTargetedActor(nullptr);
}
}
void UGCS_TargetingSystemComponent::RefreshPotentialTargets()
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGCS_TargetingSystemComponent::RefreshPotentialTargets"), UGCS_TargetingSystemComponent_RefreshPotentialTargets, STATGROUP_GCS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
if (TargetingPreset == nullptr)
{
return;
}
if (UTargetingSubsystem* TargetingSubsystem = UTargetingSubsystem::Get(GetWorld()))
{
FTargetingSourceContext SourceContext;
SourceContext.SourceActor = GetOwner();
SourceContext.SourceObject = this;
FTargetingRequestHandle TargetingHandle = UTargetingSubsystem::MakeTargetRequestHandle(TargetingPreset, SourceContext);
FTargetingRequestDelegate Delegate = FTargetingRequestDelegate::CreateWeakLambda(this, [this,TargetingSubsystem](FTargetingRequestHandle InTargetingHandle)
{
TArray<AActor*> Results;
TargetingSubsystem->GetTargetingResultsActors(InTargetingHandle, Results);
PotentialTargets.Empty();
PotentialTargets = Results;
});
if (bUseAsyncTargeting)
{
FTargetingAsyncTaskData& AsyncTaskData = FTargetingAsyncTaskData::FindOrAdd(TargetingHandle);
AsyncTaskData.bReleaseOnCompletion = true;
TargetingSubsystem->StartAsyncTargetingRequestWithHandle(TargetingHandle, Delegate);
}
else
{
FTargetingImmediateTaskData& ImmeidateTaskData = FTargetingImmediateTaskData::FindOrAdd(TargetingHandle);
ImmeidateTaskData.bReleaseOnCompletion = true;
TargetingSubsystem->ExecuteTargetingRequestWithHandle(TargetingHandle, Delegate);
}
}
}
bool UGCS_TargetingSystemComponent::CanBeTargeted_Implementation(AActor* ActorToTarget)
{
return true;
}
void UGCS_TargetingSystemComponent::StaticSwitchToNewTarget(bool RightDirection)
{
if (TargetedActor)
{
TMap<AActor*, float> LocalPotentialTargets;
for (TObjectPtr<AActor>& Elem : PotentialTargets)
{
if (Elem != TargetedActor)
{
float LocalDistance = UKismetMathLibrary::Vector_Distance(GetOwner()->GetActorLocation(), Elem->GetActorLocation());
FRotator RequiredRotation = UKismetMathLibrary::FindLookAtRotation(GetOwner()->GetActorLocation(), Elem->GetActorLocation());
FRotator DeltaRotation;
ACharacter* LocalOwnerCharacter = Cast<ACharacter>(GetOwner());
DeltaRotation = UKismetMathLibrary::NormalizedDeltaRotator(LocalOwnerCharacter->GetControlRotation(), RequiredRotation);
if (RightDirection == 1)
{
if (DeltaRotation.Yaw < 0.f && DeltaRotation.Yaw > -100.f)
{
LocalPotentialTargets.Add(Elem, DeltaRotation.Yaw);
}
else
{
LocalPotentialTargets.Add(Elem, -10000.f);
}
}
else
{
if (DeltaRotation.Yaw > 0.f && DeltaRotation.Yaw < 100.f)
{
LocalPotentialTargets.Add(Elem, DeltaRotation.Yaw);
}
else
{
LocalPotentialTargets.Add(Elem, 10000.f);
}
}
}
}
if (LocalPotentialTargets.Num() > 0)
{
TArray<AActor*> LocalTargets;
TArray<float> LocalAngles;
LocalPotentialTargets.GenerateKeyArray(LocalTargets);
LocalPotentialTargets.GenerateValueArray(LocalAngles);
if (RightDirection == 1)
{
int32 FoundIndex;
const float MaxValue = FMath::Max<float>(LocalAngles, &FoundIndex);
if (MaxValue > -10000.f)
{
OnLockOff();
SetTargetedActor(LocalTargets[FoundIndex]);
OnLockOn();
}
}
else
{
int32 FoundIndex;
const float MinValue = FMath::Min<float>(LocalAngles, &FoundIndex);
if (MinValue < 10000.f)
{
OnLockOff();
SetTargetedActor(LocalTargets[FoundIndex]);
OnLockOn();
}
}
}
}
}
float UGCS_TargetingSystemComponent::CalculateViewAngle(const AActor* TargetActor)
{
APawn* OwningPawn = GetPawn<APawn>();
FRotator FinalRotation = FRotator::ZeroRotator;
if (TargetActor && OwningPawn)
{
FRotator RequiredRotation = UKismetMathLibrary::FindLookAtRotation(OwningPawn->GetPawnViewLocation(), TargetActor->GetActorLocation());
FRotator DeltaRotation = UKismetMathLibrary::NormalizedDeltaRotator(RequiredRotation, OwningPawn->GetViewRotation());
FinalRotation = DeltaRotation;
}
return UKismetMathLibrary::Abs(UKismetMathLibrary::Abs(FinalRotation.Yaw) + UKismetMathLibrary::Abs(FinalRotation.Pitch));
}