第一次提交
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/Filters/GCS_TargetingFilterTask_Affiliation.h"
|
||||
#include "GCS_CombatSystemSettings.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/Controller.h"
|
||||
#include "Team/GCS_CombatTeamAgentInterface.h"
|
||||
#include "Utility/GCS_CombatFunctionLibrary.h"
|
||||
|
||||
bool UGCS_TargetingFilterTask_Affiliation::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
if (const UGCS_CombatSystemSettings* Settings = UGCS_CombatSystemSettings::Get())
|
||||
{
|
||||
if (Settings->bDisableAffiliationCheck)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FGenericTeamId SourceTeamId = GetSourceTeamId(TargetingHandle, TargetData);
|
||||
FGenericTeamId TargetTeamId = GetTargetTeamId(TargetingHandle, TargetData);
|
||||
|
||||
if (TargetTeamId == FGenericTeamId::NoTeam && bIgnoreTargetWithNoTeam)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return FAISenseAffiliationFilter::ShouldSenseTeam(SourceTeamId, TargetTeamId, DetectionByAffiliation.GetAsFlags()) == false;
|
||||
}
|
||||
|
||||
FGenericTeamId UGCS_TargetingFilterTask_Affiliation::GetSourceTeamId(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
AActor* Actor = SourceContext->InstigatorActor ? SourceContext->InstigatorActor : SourceContext->SourceActor;
|
||||
if (IsValid(Actor))
|
||||
{
|
||||
return UGCS_CombatFunctionLibrary::QueryCombatTeamId(Actor, true, true);;
|
||||
}
|
||||
}
|
||||
return FGenericTeamId::NoTeam;
|
||||
}
|
||||
|
||||
FGenericTeamId UGCS_TargetingFilterTask_Affiliation::GetTargetTeamId(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
if (const AActor* TargetActor = TargetData.HitResult.GetActor())
|
||||
{
|
||||
return UGCS_CombatFunctionLibrary::QueryCombatTeamId(TargetActor, true, true);;
|
||||
}
|
||||
return FGenericTeamId::NoTeam;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/Filters/GCS_TargetingFilterTask_IsDead.h"
|
||||
|
||||
#include "GCS_CombatEntityInterface.h"
|
||||
#include "Utility/GCS_CombatFunctionLibrary.h"
|
||||
|
||||
bool UGCS_TargetingFilterTask_IsDead::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
AActor* TargetActor = TargetData.HitResult.GetActor();
|
||||
|
||||
if (UObject* Implementer = UGCS_CombatFunctionLibrary::GetCombatEntity(TargetActor))
|
||||
{
|
||||
return IGCS_CombatEntityInterface::Execute_IsDead(Implementer);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/Filters/GCS_TargetingFilterTask_TagsRequirements.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
|
||||
bool UGCS_TargetingFilterTask_TagsRequirements::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
const AActor* TargetActor = TargetData.HitResult.GetActor();
|
||||
|
||||
if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(TargetActor))
|
||||
{
|
||||
FGameplayTagContainer ActorTags;
|
||||
ASC->GetOwnedGameplayTags(ActorTags);
|
||||
|
||||
return bInvert ? !TagQuery.Matches(ActorTags) : TagQuery.Matches(ActorTags);
|
||||
}
|
||||
|
||||
if (bLookingForTagAssetInterface)
|
||||
{
|
||||
const IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(TargetActor);
|
||||
if (!TagAssetInterface)
|
||||
{
|
||||
const TArray<UActorComponent*> Components = TargetActor->GetComponentsByInterface(UGameplayTagAssetInterface::StaticClass());
|
||||
TagAssetInterface = Components.IsValidIndex(0) ? Cast<IGameplayTagAssetInterface>(Components[0]) : nullptr;
|
||||
}
|
||||
|
||||
if (TagAssetInterface)
|
||||
{
|
||||
FGameplayTagContainer ActorTags;
|
||||
TagAssetInterface->GetOwnedGameplayTags(ActorTags);
|
||||
|
||||
return bInvert ? !TagQuery.Matches(ActorTags) : TagQuery.Matches(ActorTags);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/Filters/GCS_TargetingFilterTask_TraceInstance.h"
|
||||
|
||||
#include "Collision/DEPRECATED_GCS_CollisionTraceInstance.h"
|
||||
|
||||
bool UGCS_TargetingFilterTask_TraceInstance::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
|
||||
{
|
||||
const AActor* TargetActor = TargetData.HitResult.GetActor();
|
||||
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (const UDEPRECATED_GCS_CollisionTraceInstance* TraceInstance = Cast<UDEPRECATED_GCS_CollisionTraceInstance>(SourceContext->SourceObject))
|
||||
{
|
||||
return !TraceInstance->CanHitActor(TargetActor);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/GCS_TargetingFunctionLibrary.h"
|
||||
#include "Components/MeshComponent.h"
|
||||
|
||||
FTargetingSourceContext UGCS_TargetingFunctionLibrary::GetTargetingSourceContext(FTargetingRequestHandle TargetingHandle)
|
||||
{
|
||||
if (TargetingHandle.IsValid())
|
||||
{
|
||||
if (FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
return *SourceContext;
|
||||
}
|
||||
}
|
||||
|
||||
return FTargetingSourceContext();
|
||||
}
|
||||
|
||||
FString UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(FTargetingRequestHandle TargetingHandle)
|
||||
{
|
||||
if (TargetingHandle.IsValid())
|
||||
{
|
||||
if (FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
return FString::Format(TEXT("ctx(actor:{0},instigator:{1},source:{2})"), {
|
||||
*GetNameSafe(SourceContext->SourceActor), *GetNameSafe(SourceContext->InstigatorActor), *GetNameSafe(SourceContext->SourceObject)
|
||||
});
|
||||
}
|
||||
}
|
||||
return TEXT("None");
|
||||
}
|
||||
|
||||
void UGCS_TargetingFunctionLibrary::GetTargetingResultsActors(FTargetingRequestHandle TargetingHandle, TArray<AActor*>& Targets)
|
||||
{
|
||||
if (TargetingHandle.IsValid())
|
||||
{
|
||||
if (FTargetingDefaultResultsSet* Results = FTargetingDefaultResultsSet::Find(TargetingHandle))
|
||||
{
|
||||
for (const FTargetingDefaultResultData& ResultData : Results->TargetResults)
|
||||
{
|
||||
if (AActor* Target = ResultData.HitResult.GetActor())
|
||||
{
|
||||
Targets.Add(Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCS_TargetingFunctionLibrary::GetTargetingResults(FTargetingRequestHandle TargetingHandle, TArray<FHitResult>& OutTargets)
|
||||
{
|
||||
if (TargetingHandle.IsValid())
|
||||
{
|
||||
if (FTargetingDefaultResultsSet* Results = FTargetingDefaultResultsSet::Find(TargetingHandle))
|
||||
{
|
||||
for (const FTargetingDefaultResultData& ResultData : Results->TargetResults)
|
||||
{
|
||||
OutTargets.Add(ResultData.HitResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FTargetingSourceContext UGCS_TargetingFunctionLibrary::ConvertTargetingLocationInfoToSourceContext(FGameplayAbilityTargetingLocationInfo LocationInfo)
|
||||
{
|
||||
FTargetingSourceContext Context = FTargetingSourceContext();
|
||||
|
||||
//Return or calculate based on LocationType.
|
||||
switch (LocationInfo.LocationType)
|
||||
{
|
||||
case EGameplayAbilityTargetingLocationType::ActorTransform:
|
||||
if (LocationInfo.SourceActor)
|
||||
{
|
||||
Context.SourceActor = LocationInfo.SourceActor;
|
||||
}
|
||||
break;
|
||||
case EGameplayAbilityTargetingLocationType::SocketTransform:
|
||||
if (LocationInfo.SourceComponent)
|
||||
{
|
||||
// Bad socket name will just return component transform anyway, so we're safe
|
||||
Context.SourceLocation = LocationInfo.SourceComponent->GetSocketTransform(LocationInfo.SourceSocketName).GetLocation();
|
||||
}
|
||||
break;
|
||||
case EGameplayAbilityTargetingLocationType::LiteralTransform:
|
||||
Context.SourceLocation = LocationInfo.LiteralTransform.GetLocation();
|
||||
default:
|
||||
check(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return Context;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/GCS_TargetingSourceInterface.h"
|
||||
|
||||
|
||||
// Add default functionality here for any IGCS_TargetingSourceInterface functions that are not pure virtual.
|
||||
@@ -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));
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Targeting/Selections/GCS_TargetingSelectionTask_LineTrace.h"
|
||||
|
||||
#include "CollisionQueryParams.h"
|
||||
#include "KismetTraceUtils.h"
|
||||
#include "Components/PrimitiveComponent.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "TargetingSystem/TargetingSubsystem.h"
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
#include "Engine/Canvas.h"
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
|
||||
|
||||
UGCS_TargetingSelectionTask_LineTrace::UGCS_TargetingSelectionTask_LineTrace(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
bComplexTrace = false;
|
||||
bIgnoreSourceActor = false;
|
||||
bIgnoreInstigatorActor = false;
|
||||
bGenerateDefaultHitResult = true;
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::Execute(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
Super::Execute(TargetingHandle);
|
||||
|
||||
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Executing);
|
||||
|
||||
if (IsAsyncTargetingRequest(TargetingHandle))
|
||||
{
|
||||
ExecuteAsyncTrace(TargetingHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteImmediateTrace(TargetingHandle);
|
||||
}
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_LineTrace::GetSourceLocation_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceActor)
|
||||
{
|
||||
return SourceContext->SourceActor->GetActorLocation();
|
||||
}
|
||||
|
||||
return SourceContext->SourceLocation;
|
||||
}
|
||||
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_LineTrace::GetSourceOffset_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
return DefaultSourceOffset;
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_LineTrace::GetTraceDirection_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceActor)
|
||||
{
|
||||
if (APawn* Pawn = Cast<APawn>(SourceContext->SourceActor))
|
||||
{
|
||||
return Pawn->GetControlRotation().Vector();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SourceContext->SourceActor->GetActorForwardVector();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_LineTrace::GetTraceLength_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
return DefaultTraceLength.GetValueAtLevel(GetTraceLevel(TargetingHandle));
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::GetAdditionalActorsToIgnore_Implementation(const FTargetingRequestHandle& TargetingHandle, TArray<AActor*>& OutAdditionalActorsToIgnore) const
|
||||
{
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_LineTrace::GetTraceLevel_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::ExecuteImmediateTrace(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (UWorld* World = GetSourceContextWorld(TargetingHandle))
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
ResetTraceResultsDebugString(TargetingHandle);
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
|
||||
const FVector Direction = GetTraceDirection(TargetingHandle).GetSafeNormal();
|
||||
const FVector Start = (GetSourceLocation(TargetingHandle) + GetSourceOffset(TargetingHandle));
|
||||
const FVector End = Start + (Direction * GetTraceLength(TargetingHandle));
|
||||
|
||||
FCollisionQueryParams Params(SCENE_QUERY_STAT(ExecuteImmediateTrace), bComplexTrace);
|
||||
InitCollisionParams(TargetingHandle, Params);
|
||||
|
||||
bool bHasBlockingHit = false;
|
||||
TArray<FHitResult> Hits;
|
||||
if (CollisionProfileName.Name != TEXT("NoCollision"))
|
||||
{
|
||||
bHasBlockingHit = World->LineTraceMultiByProfile(Hits, Start, End, CollisionProfileName.Name, Params);
|
||||
}
|
||||
else
|
||||
{
|
||||
const ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
|
||||
bHasBlockingHit = World->LineTraceMultiByChannel(Hits, Start, End, CollisionChannel, Params);
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
DrawDebugTrace(TargetingHandle, Start, End, bHasBlockingHit, Hits);
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
|
||||
ProcessHitResults(TargetingHandle, Hits);
|
||||
}
|
||||
|
||||
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::ExecuteAsyncTrace(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (UWorld* World = GetSourceContextWorld(TargetingHandle))
|
||||
{
|
||||
AActor* SourceActor = nullptr;
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
SourceActor = SourceContext->SourceActor;
|
||||
}
|
||||
const FVector Direction = GetTraceDirection(TargetingHandle).GetSafeNormal();
|
||||
const FVector Start = (GetSourceLocation(TargetingHandle) + GetSourceOffset(TargetingHandle));
|
||||
const FVector End = Start + (Direction * GetTraceLength(TargetingHandle));
|
||||
|
||||
FCollisionQueryParams Params(SCENE_QUERY_STAT(ExecuteAsyncTrace), bComplexTrace);
|
||||
InitCollisionParams(TargetingHandle, Params);
|
||||
|
||||
FTraceDelegate Delegate = FTraceDelegate::CreateUObject(this, &UGCS_TargetingSelectionTask_LineTrace::HandleAsyncTraceComplete, TargetingHandle);
|
||||
if (CollisionProfileName.Name != TEXT("NoCollision"))
|
||||
{
|
||||
World->AsyncLineTraceByProfile(EAsyncTraceType::Multi, Start, End, CollisionProfileName.Name, Params, &Delegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
const ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
|
||||
World->AsyncLineTraceByChannel(EAsyncTraceType::Multi, Start, End, CollisionChannel, Params, FCollisionResponseParams::DefaultResponseParam, &Delegate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
|
||||
}
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::HandleAsyncTraceComplete(const FTraceHandle& InTraceHandle, FTraceDatum& InTraceDatum, FTargetingRequestHandle TargetingHandle) const
|
||||
{
|
||||
if (TargetingHandle.IsValid())
|
||||
{
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
ResetTraceResultsDebugString(TargetingHandle);
|
||||
|
||||
// We have to manually find if there is a blocking hit.
|
||||
bool bHasBlockingHit = false;
|
||||
for (const FHitResult& HitResult : InTraceDatum.OutHits)
|
||||
{
|
||||
if (HitResult.bBlockingHit)
|
||||
{
|
||||
bHasBlockingHit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DrawDebugTrace(TargetingHandle, InTraceDatum.Start, InTraceDatum.End, bHasBlockingHit, InTraceDatum.OutHits);
|
||||
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
|
||||
ProcessHitResults(TargetingHandle, InTraceDatum.OutHits);
|
||||
}
|
||||
|
||||
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::ProcessHitResults(const FTargetingRequestHandle& TargetingHandle, const TArray<FHitResult>& Hits) const
|
||||
{
|
||||
if (!TargetingHandle.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
FTargetingDefaultResultsSet& TargetingResults = FTargetingDefaultResultsSet::FindOrAdd(TargetingHandle);
|
||||
|
||||
if (Hits.Num() > 0)
|
||||
{
|
||||
for (const FHitResult& HitResult : Hits)
|
||||
{
|
||||
if (!HitResult.GetActor())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bAddResult = true;
|
||||
for (const FTargetingDefaultResultData& ResultData : TargetingResults.TargetResults)
|
||||
{
|
||||
if (ResultData.HitResult.GetActor() == HitResult.GetActor())
|
||||
{
|
||||
bAddResult = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddResult)
|
||||
{
|
||||
FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData();
|
||||
ResultData->HitResult = HitResult;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
BuildTraceResultsDebugString(TargetingHandle, TargetingResults.TargetResults);
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
}
|
||||
else if (bGenerateDefaultHitResult)
|
||||
{
|
||||
// If there were no hits, add a default HitResult at the end of the trace
|
||||
FHitResult HitResult;
|
||||
const FVector Start = (GetSourceLocation(TargetingHandle) + GetSourceOffset(TargetingHandle));
|
||||
const FVector End = Start + (GetTraceDirection(TargetingHandle) * GetTraceLength(TargetingHandle));
|
||||
// Start param could be player ViewPoint. We want HitResult to always display the StartLocation.
|
||||
HitResult.TraceStart = Start;
|
||||
HitResult.TraceEnd = End;
|
||||
HitResult.Location = End;
|
||||
HitResult.ImpactPoint = End;
|
||||
FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData();
|
||||
ResultData->HitResult = HitResult;
|
||||
}
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::InitCollisionParams(const FTargetingRequestHandle& TargetingHandle, FCollisionQueryParams& OutParams) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (bIgnoreSourceActor && SourceContext->SourceActor)
|
||||
{
|
||||
OutParams.AddIgnoredActor(SourceContext->SourceActor);
|
||||
}
|
||||
|
||||
if (bIgnoreInstigatorActor && SourceContext->InstigatorActor)
|
||||
{
|
||||
OutParams.AddIgnoredActor(SourceContext->InstigatorActor);
|
||||
}
|
||||
|
||||
TArray<AActor*> AdditionalActorsToIgnoreArray;
|
||||
GetAdditionalActorsToIgnore(TargetingHandle, AdditionalActorsToIgnoreArray);
|
||||
|
||||
if (AdditionalActorsToIgnoreArray.Num() > 0)
|
||||
{
|
||||
OutParams.AddIgnoredActors(AdditionalActorsToIgnoreArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
bool UGCS_TargetingSelectionTask_LineTrace::CanEditChange(const FProperty* InProperty) const
|
||||
{
|
||||
bool bCanEdit = Super::CanEditChange(InProperty);
|
||||
|
||||
if (bCanEdit && InProperty)
|
||||
{
|
||||
const FName PropertyName = InProperty->GetFName();
|
||||
|
||||
if (PropertyName == GET_MEMBER_NAME_CHECKED(UGCS_TargetingSelectionTask_LineTrace, TraceChannel))
|
||||
{
|
||||
return (CollisionProfileName.Name == TEXT("NoCollision"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
|
||||
#if ENABLE_DRAW_DEBUG
|
||||
void UGCS_TargetingSelectionTask_LineTrace::DrawDebug(UTargetingSubsystem* TargetingSubsystem, FTargetingDebugInfo& Info, const FTargetingRequestHandle& TargetingHandle, float XOffset, float YOffset,
|
||||
int32 MinTextRowsToAdvance) const
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
if (UTargetingSubsystem::IsTargetingDebugEnabled())
|
||||
{
|
||||
FTargetingDebugData& DebugData = FTargetingDebugData::FindOrAdd(TargetingHandle);
|
||||
FString& ScratchPadString = DebugData.DebugScratchPadStrings.FindOrAdd(GetNameSafe(this));
|
||||
if (!ScratchPadString.IsEmpty())
|
||||
{
|
||||
if (Info.Canvas)
|
||||
{
|
||||
Info.Canvas->SetDrawColor(FColor::Yellow);
|
||||
}
|
||||
|
||||
FString TaskString = FString::Printf(TEXT("Results : %s"), *ScratchPadString);
|
||||
TargetingSubsystem->DebugLine(Info, TaskString, XOffset, YOffset, MinTextRowsToAdvance);
|
||||
}
|
||||
}
|
||||
#endif // WITH_EDITORONLY_DATA
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::DrawDebugTrace(const FTargetingRequestHandle TargetingHandle, const FVector& StartLocation, const FVector& EndLocation, const bool bHit,
|
||||
const TArray<FHitResult>& Hits) const
|
||||
{
|
||||
if (UTargetingSubsystem::IsTargetingDebugEnabled())
|
||||
{
|
||||
if (UWorld* World = GetSourceContextWorld(TargetingHandle))
|
||||
{
|
||||
const float DrawTime = UTargetingSubsystem::GetOverrideTargetingLifeTime();
|
||||
const EDrawDebugTrace::Type DrawDebugType = DrawTime <= 0.0f ? EDrawDebugTrace::Type::ForOneFrame : EDrawDebugTrace::Type::ForDuration;
|
||||
const FLinearColor TraceColor = FLinearColor::Red;
|
||||
const FLinearColor TraceHitColor = FLinearColor::Green;
|
||||
DrawDebugLineTraceMulti(World, StartLocation, EndLocation, DrawDebugType, bHit, Hits, TraceColor, TraceHitColor, DrawTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::BuildTraceResultsDebugString(const FTargetingRequestHandle& TargetingHandle, const TArray<FTargetingDefaultResultData>& TargetResults) const
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
if (UTargetingSubsystem::IsTargetingDebugEnabled())
|
||||
{
|
||||
FTargetingDebugData& DebugData = FTargetingDebugData::FindOrAdd(TargetingHandle);
|
||||
FString& ScratchPadString = DebugData.DebugScratchPadStrings.FindOrAdd(GetNameSafe(this));
|
||||
|
||||
for (const FTargetingDefaultResultData& TargetData : TargetResults)
|
||||
{
|
||||
if (const AActor* Target = TargetData.HitResult.GetActor())
|
||||
{
|
||||
if (ScratchPadString.IsEmpty())
|
||||
{
|
||||
ScratchPadString = FString::Printf(TEXT("%s"), *GetNameSafe(Target));
|
||||
}
|
||||
else
|
||||
{
|
||||
ScratchPadString += FString::Printf(TEXT(", %s"), *GetNameSafe(Target));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // WITH_EDITORONLY_DATA
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_LineTrace::ResetTraceResultsDebugString(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
FTargetingDebugData& DebugData = FTargetingDebugData::FindOrAdd(TargetingHandle);
|
||||
FString& ScratchPadString = DebugData.DebugScratchPadStrings.FindOrAdd(GetNameSafe(this));
|
||||
ScratchPadString.Reset();
|
||||
#endif // WITH_EDITORONLY_DATA
|
||||
}
|
||||
|
||||
#endif // ENABLE_DRAW_DEBUG
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/Selections/GCS_TargetingSelectionTask_TraceExt.h"
|
||||
|
||||
#include "GCS_LogChannels.h"
|
||||
#include "Collision/DEPRECATED_GCS_CollisionTraceInstance.h"
|
||||
#include "Targeting/GCS_TargetingSourceInterface.h"
|
||||
|
||||
UDEPRECATED_GCS_CollisionTraceInstance* UGCS_TargetingSelectionTask_TraceExt::GetSourceTraceInstance_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceObject)
|
||||
{
|
||||
return Cast<UDEPRECATED_GCS_CollisionTraceInstance>(SourceContext->SourceObject);
|
||||
}
|
||||
}
|
||||
UE_LOG(LogGCS, Error, TEXT("No valid CollisionTraceInstance passed in as SourceObject! TargetingPreset:%s"), *GetOuter()->GetName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_TraceExt::Execute(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
Super::Execute(TargetingHandle);
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_TraceExt::GetSourceLocation_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (bUseContextLocationAsSourceLocation)
|
||||
{
|
||||
return SourceContext->SourceLocation;
|
||||
}
|
||||
if (SourceContext->SourceActor)
|
||||
{
|
||||
return SourceContext->SourceActor->GetActorLocation();
|
||||
}
|
||||
|
||||
return SourceContext->SourceLocation;
|
||||
}
|
||||
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_TraceExt::GetTraceDirection_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceObject && SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
|
||||
{
|
||||
FVector TraceDirection;
|
||||
if (IGCS_TargetingSourceInterface::Execute_GetTraceDirection(SourceContext->SourceObject, TraceDirection))
|
||||
{
|
||||
return TraceDirection;
|
||||
}
|
||||
}
|
||||
|
||||
return SourceContext->SourceLocation;
|
||||
}
|
||||
|
||||
return Super::GetTraceDirection_Implementation(TargetingHandle);
|
||||
}
|
||||
|
||||
void UGCS_TargetingSelectionTask_TraceExt::GetAdditionalActorsToIgnore_Implementation(const FTargetingRequestHandle& TargetingHandle, TArray<AActor*>& OutAdditionalActorsToIgnore) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceObject && SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
|
||||
{
|
||||
OutAdditionalActorsToIgnore.Append(IGCS_TargetingSourceInterface::Execute_GetAdditionalActorsToIgnore(SourceContext->SourceObject));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_TraceExt::GetTraceLevel_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (!IsValid(SourceContext->SourceObject))
|
||||
{
|
||||
UE_LOG(LogGCS, Error, TEXT("No valid Context Source Object found! TargetingPreset:%s"), *GetOuter()->GetName());
|
||||
return 0;
|
||||
}
|
||||
if (!SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
|
||||
{
|
||||
UE_LOG(LogGCS, Error, TEXT("Source Object(%s) doesn't implements GCS_TargetingSourceInterface.! TargetingPreset:%s"),
|
||||
*SourceContext->SourceObject->GetName(), *GetOuter()->GetName());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_TraceExt::GetTraceLength_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
return bTraceLengthLevel ? DefaultTraceLength.GetValueAtLevel(GetTraceLevel(TargetingHandle)) : DefaultTraceLength.GetValue();
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_TraceExt::GetSweptTraceRadius_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
return bSweptTraceRadiusLevel ? DefaultSweptTraceRadius.GetValueAtLevel(GetTraceLevel(TargetingHandle)) : DefaultSweptTraceRadius.GetValue();
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_TraceExt::GetSweptTraceCapsuleHalfHeight_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
return bSweptTraceCapsuleHalfHeightLevel ? DefaultSweptTraceCapsuleHalfHeight.GetValueAtLevel(GetTraceLevel(TargetingHandle)) : DefaultSweptTraceCapsuleHalfHeight.GetValue();
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_TraceExt::GetSweptTraceBoxHalfExtents_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (bSweptTraceBoxHalfExtentLevel)
|
||||
{
|
||||
float Level = GetTraceLevel(TargetingHandle);
|
||||
return FVector(DefaultSweptTraceBoxHalfExtentX.GetValueAtLevel(Level), DefaultSweptTraceBoxHalfExtentY.GetValueAtLevel(Level), DefaultSweptTraceBoxHalfExtentZ.GetValueAtLevel(Level));
|
||||
}
|
||||
|
||||
return Super::GetSweptTraceBoxHalfExtents_Implementation(TargetingHandle);
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Targeting/Selections/GCS_TargetingSelectionTask_TraceExt_BindShape.h"
|
||||
|
||||
#include "GCS_LogChannels.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Components/SphereComponent.h"
|
||||
#include "Components/ShapeComponent.h"
|
||||
#include "Misc/DataValidation.h"
|
||||
#include "Targeting/GCS_TargetingFunctionLibrary.h"
|
||||
#include "Targeting/GCS_TargetingSourceInterface.h"
|
||||
|
||||
void UGCS_TargetingSelectionTask_TraceExt_BindShape::Execute(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
bool bMatchShapeType = true;
|
||||
UShapeComponent* Shape = GetTraceShape(TargetingHandle);
|
||||
|
||||
if (!IsValid(Shape))
|
||||
{
|
||||
GCS_LOG(Warning, "")
|
||||
bMatchShapeType = false;
|
||||
}
|
||||
if (TraceType == ETargetingTraceType::Box && !Shape->IsA<UBoxComponent>())
|
||||
{
|
||||
GCS_LOG(Warning, "%s: Trace type mismatched! want Box, got %s. %s",
|
||||
*GetNameSafe(GetOuter()),
|
||||
*GetNameSafe(Shape->GetClass()),
|
||||
*UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(TargetingHandle)
|
||||
)
|
||||
bMatchShapeType = false;
|
||||
}
|
||||
if (TraceType == ETargetingTraceType::Capsule && !Shape->IsA<UCapsuleComponent>())
|
||||
{
|
||||
GCS_LOG(Warning, "%s: Trace type mismatched! want Capsule, got %s. %s",
|
||||
*GetNameSafe(GetOuter()),
|
||||
*GetNameSafe(Shape->GetClass()),
|
||||
*UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(TargetingHandle)
|
||||
)
|
||||
bMatchShapeType = false;
|
||||
}
|
||||
if (TraceType == ETargetingTraceType::Sphere && !Shape->IsA<USphereComponent>())
|
||||
{
|
||||
GCS_LOG(Warning, "%s: Trace type mismatched! want Capsule, got %s. %s",
|
||||
*GetNameSafe(GetOuter()),
|
||||
*GetNameSafe(Shape->GetClass()),
|
||||
*UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(TargetingHandle)
|
||||
)
|
||||
bMatchShapeType = false;
|
||||
}
|
||||
|
||||
if (bMatchShapeType)
|
||||
{
|
||||
Super::Execute(TargetingHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
|
||||
}
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceRadius_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
float BaseValue = -1;
|
||||
|
||||
if (USphereComponent* Shape = Cast<USphereComponent>(GetTraceShape(TargetingHandle)))
|
||||
{
|
||||
BaseValue = Shape->GetScaledSphereRadius();
|
||||
}
|
||||
if (const UCapsuleComponent* Shape = Cast<UCapsuleComponent>(GetTraceShape(TargetingHandle)))
|
||||
{
|
||||
BaseValue = Shape->GetScaledCapsuleRadius();
|
||||
}
|
||||
|
||||
float Value = Super::GetSweptTraceRadius_Implementation(TargetingHandle);
|
||||
|
||||
if (BaseValue < 0)
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
if (SweptTraceRadiusModType == EGCS_TraceDataModifyType::Add)
|
||||
{
|
||||
return BaseValue + Value;
|
||||
}
|
||||
|
||||
if (SweptTraceRadiusModType == EGCS_TraceDataModifyType::Multiply)
|
||||
{
|
||||
return BaseValue * Value;
|
||||
}
|
||||
return BaseValue;
|
||||
}
|
||||
|
||||
float UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceCapsuleHalfHeight_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
float BaseValue = -1;
|
||||
|
||||
if (const UCapsuleComponent* Capsule = Cast<UCapsuleComponent>(GetTraceShape(TargetingHandle)))
|
||||
{
|
||||
BaseValue = Capsule->GetScaledCapsuleHalfHeight();
|
||||
}
|
||||
|
||||
float Value = Super::GetSweptTraceCapsuleHalfHeight_Implementation(TargetingHandle);
|
||||
|
||||
if (BaseValue < 0)
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
if (SweptTraceCapsuleHalfHeightModType == EGCS_TraceDataModifyType::Add)
|
||||
{
|
||||
return BaseValue + Value;
|
||||
}
|
||||
|
||||
if (SweptTraceCapsuleHalfHeightModType == EGCS_TraceDataModifyType::Multiply)
|
||||
{
|
||||
return BaseValue * Value;
|
||||
}
|
||||
return BaseValue;
|
||||
}
|
||||
|
||||
FVector UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceBoxHalfExtents_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
FVector BaseValue = FVector::ZeroVector;
|
||||
|
||||
if (const UBoxComponent* Shape = Cast<UBoxComponent>(GetTraceShape(TargetingHandle)))
|
||||
{
|
||||
BaseValue = Shape->GetScaledBoxExtent();
|
||||
}
|
||||
|
||||
FVector Value = Super::GetSweptTraceBoxHalfExtents_Implementation(TargetingHandle);
|
||||
|
||||
if (BaseValue == FVector::ZeroVector)
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
if (SweptTraceBoxHalfExtentModType == EGCS_TraceDataModifyType::Add)
|
||||
{
|
||||
return BaseValue + Value;
|
||||
}
|
||||
|
||||
if (SweptTraceBoxHalfExtentModType == EGCS_TraceDataModifyType::Multiply)
|
||||
{
|
||||
return BaseValue * Value;
|
||||
}
|
||||
return BaseValue;
|
||||
}
|
||||
|
||||
UShapeComponent* UGCS_TargetingSelectionTask_TraceExt_BindShape::GetTraceShape(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
UShapeComponent* ShapeComponent = nullptr;
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceObject)
|
||||
{
|
||||
if (SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
|
||||
{
|
||||
if (IGCS_TargetingSourceInterface::Execute_GetTraceShape(SourceContext->SourceObject, ShapeComponent))
|
||||
{
|
||||
return ShapeComponent;
|
||||
}
|
||||
UE_LOG(LogGCS, VeryVerbose, TEXT("Source Object(%s) doesn't provide valid ShapeComponent! TargetingPreset:%s"),
|
||||
*SourceContext->SourceObject->GetName(), *GetOuter()->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogGCS, VeryVerbose, TEXT("Source Object(%s) doesn't implements GCS_TargetingSourceInterface.! TargetingPreset:%s"),
|
||||
*SourceContext->SourceObject->GetName(), *GetOuter()->GetName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogGCS, Error, TEXT("No valid Context Source Object found! TargetingPreset:%s"), *GetOuter()->GetName());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FRotator UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceRotation_Implementation(const FTargetingRequestHandle& TargetingHandle) const
|
||||
{
|
||||
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
|
||||
{
|
||||
if (SourceContext->SourceObject && SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
|
||||
{
|
||||
FRotator TraceRotation;
|
||||
if (IGCS_TargetingSourceInterface::Execute_GetSweptTraceRotation(SourceContext->SourceObject, TraceRotation))
|
||||
{
|
||||
return TraceRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Super::GetSweptTraceRotation_Implementation(TargetingHandle);
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
EDataValidationResult UGCS_TargetingSelectionTask_TraceExt_BindShape::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
if (TraceType == ETargetingTraceType::Line)
|
||||
{
|
||||
FString Txt = FString::Format(TEXT("TraceType == Line is not allowed in this type of task:{0} "), {GetClass()->GetName()});
|
||||
Context.AddError(FText::FromString(Txt));
|
||||
}
|
||||
if (bUseContextLocationAsSourceLocation)
|
||||
{
|
||||
FString Txt = FString::Format(TEXT("bUseContextLocationAsSourceLocation is not allowed in this type of task:{0} "), {GetClass()->GetName()});
|
||||
Context.AddError(FText::FromString(Txt));
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user