Fix EQS firing position: invalid BB vector check, AlreadyAtGoal fallback

- LastKnownTargetPosition check now filters FLT_MAX/InvalidLocation (not just zero)
- EQS result with AlreadyAtGoal triggers fallback advance instead of no-op
- bProjectGoal enabled for EQS move to handle NavMesh projection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
j.foucher 2026-03-29 11:14:04 +02:00
parent 59a23e61db
commit 25abd59512
8 changed files with 34 additions and 16 deletions

View File

@ -227,8 +227,12 @@ void UPS_AI_Behavior_BTTask_Attack::TickTask(
{ {
// No LOS and idle → find a firing position // No LOS and idle → find a firing position
// Check if investigation (last known position) is active // Check if investigation (last known position) is active
// ClearValue sets vectors to FAISystem::InvalidLocation, not zero — must check both
const FVector LastKnownPos = BB->GetValueAsVector(PS_AI_Behavior_BB::LastKnownTargetPosition); const FVector LastKnownPos = BB->GetValueAsVector(PS_AI_Behavior_BB::LastKnownTargetPosition);
if (!LastKnownPos.IsZero()) const bool bHasLastKnown = !LastKnownPos.IsZero() &&
!LastKnownPos.ContainsNaN() &&
LastKnownPos.X < 1e30f; // Filter out FLT_MAX / InvalidLocation
if (bHasLastKnown)
{ {
// Investigation mode: go to last known position // Investigation mode: go to last known position
AIC->MoveToLocation( AIC->MoveToLocation(
@ -336,6 +340,7 @@ void UPS_AI_Behavior_BTTask_Attack::TickTask(
*AIC->GetName()); *AIC->GetName());
} }
} }
} }
EBTNodeResult::Type UPS_AI_Behavior_BTTask_Attack::AbortTask( EBTNodeResult::Type UPS_AI_Behavior_BTTask_Attack::AbortTask(
@ -427,20 +432,9 @@ void UPS_AI_Behavior_BTTask_Attack::OnFiringPositionQueryFinished(
AActor* Target = WeakTarget.Get(); AActor* Target = WeakTarget.Get();
if (Result.IsValid() && Result->IsSuccessful()) // Fallback lambda: advance directly toward target
auto FallbackAdvance = [&](const TCHAR* Reason)
{ {
const FVector FiringPos = Result->GetItemAsLocation(0);
AIC->MoveToLocation(
FiringPos, 50.0f, /*bStopOnOverlap=*/true,
/*bUsePathfinding=*/true, /*bProjectGoal=*/false, /*bCanStrafe=*/false);
Memory->bSeekingFiringPos = true;
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: EQS found firing position %s"),
*AIC->GetName(), *FiringPos.ToString());
}
else
{
// EQS found nothing — fallback: advance toward target
if (Target) if (Target)
{ {
const float MidRange = (Memory->MinRange + Memory->MaxRange) * 0.5f; const float MidRange = (Memory->MinRange + Memory->MaxRange) * 0.5f;
@ -449,9 +443,32 @@ void UPS_AI_Behavior_BTTask_Attack::OnFiringPositionQueryFinished(
/*bUsePathfinding=*/true, /*bProjectGoal=*/true, /*bCanStrafe=*/false); /*bUsePathfinding=*/true, /*bProjectGoal=*/true, /*bCanStrafe=*/false);
Memory->bSeekingFiringPos = true; Memory->bSeekingFiringPos = true;
} }
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: %s, fallback advance toward target"),
*AIC->GetName(), Reason);
};
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: EQS no result, fallback advance"), if (Result.IsValid() && Result->IsSuccessful())
*AIC->GetName()); {
const FVector FiringPos = Result->GetItemAsLocation(0);
const EPathFollowingRequestResult::Type MoveResult = AIC->MoveToLocation(
FiringPos, 50.0f, /*bStopOnOverlap=*/true,
/*bUsePathfinding=*/true, /*bProjectGoal=*/true, /*bCanStrafe=*/false);
if (MoveResult == EPathFollowingRequestResult::RequestSuccessful)
{
Memory->bSeekingFiringPos = true;
UE_LOG(LogPS_AI_Behavior, Log, TEXT("[%s] Attack: EQS moving to firing position %s"),
*AIC->GetName(), *FiringPos.ToString());
}
else
{
// AlreadyAtGoal or Failed — EQS position is too close or unreachable
FallbackAdvance(TEXT("EQS position unreachable or too close"));
}
}
else
{
FallbackAdvance(TEXT("EQS no result"));
} }
} }

View File

@ -48,6 +48,7 @@ public:
UPROPERTY(EditAnywhere, Category = "Attack|LOS", meta = (ClampMin = "0.1", ClampMax = "1.0")) UPROPERTY(EditAnywhere, Category = "Attack|LOS", meta = (ClampMin = "0.1", ClampMax = "1.0"))
float LOSCheckInterval = 0.2f; float LOSCheckInterval = 0.2f;
/** /**
* EQS query asset for finding a firing position with clear LOS. * EQS query asset for finding a firing position with clear LOS.
* Compose in editor: OnCircle generator + LineOfSight filter + Distance tests. * Compose in editor: OnCircle generator + LineOfSight filter + Distance tests.