第一次提交
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
// 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));
|
||||
}
|
||||
Reference in New Issue
Block a user