// 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() : nullptr; } void UGCS_TargetingSystemComponent::GetLifetimeReplicatedProps(TArray& 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 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 InTargets, TArray& 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 LocalPotentialTargets; for (TObjectPtr& Elem : PotentialTargets) { if (CanBeTargeted(Elem)) { LocalPotentialTargets.Add(Elem, UKismetMathLibrary::Abs(CalculateViewAngle(Elem))); } } if (LocalPotentialTargets.Num() > 0) { TArray LocalTargets; TArray LocalAngles; LocalPotentialTargets.GenerateKeyArray(LocalTargets); LocalPotentialTargets.GenerateValueArray(LocalAngles); int32 MinIndex; const float MaxValue = FMath::Min(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 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 LocalPotentialTargets; for (TObjectPtr& 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(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 LocalTargets; TArray LocalAngles; LocalPotentialTargets.GenerateKeyArray(LocalTargets); LocalPotentialTargets.GenerateValueArray(LocalAngles); if (RightDirection == 1) { int32 FoundIndex; const float MaxValue = FMath::Max(LocalAngles, &FoundIndex); if (MaxValue > -10000.f) { OnLockOff(); SetTargetedActor(LocalTargets[FoundIndex]); OnLockOn(); } } else { int32 FoundIndex; const float MinValue = FMath::Min(LocalAngles, &FoundIndex); if (MinValue < 10000.f) { OnLockOff(); SetTargetedActor(LocalTargets[FoundIndex]); OnLockOn(); } } } } } float UGCS_TargetingSystemComponent::CalculateViewAngle(const AActor* TargetActor) { APawn* OwningPawn = GetPawn(); 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)); }