void UEnvQueryGenerator_ActorsOfClass::GenerateItems(FEnvQueryInstance& QueryInstance) const { float RadiusValue = 0.0f; float DensityValue = 0.0f; UWorld* World = GEngine->GetWorldFromContextObject(QueryInstance.Owner.Get()); // @TODO add some logging here if (World == NULL || QueryInstance.GetParamValue(Radius, RadiusValue, TEXT("Radius")) == false || SearchedActorClass == NULL) { return; } const float RadiusSq = FMath::Square(RadiusValue); TArray<FVector> ContextLocations; QueryInstance.PrepareContext(SearchCenter, ContextLocations); for (TActorIterator<AActor> ItActor = TActorIterator<AActor>(World, SearchedActorClass); ItActor; ++ItActor) { for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ++ContextIndex) { if (FVector::DistSquared(ContextLocations[ContextIndex], ItActor->GetActorLocation()) < RadiusSq) { QueryInstance.AddItemData<UEnvQueryItemType_Actor>(*ItActor); break; } } } }
void UEnvQueryGenerator_OnCircle::GenerateItems(FEnvQueryInstance& QueryInstance) const { float AngleDegree = 360.f; float RadiusValue = 0.f; float ItemSpace = 1.0f; if (QueryInstance.GetParamValue(Angle, AngleDegree, TEXT("Angle")) == false || QueryInstance.GetParamValue(Radius, RadiusValue, TEXT("Radius")) == false || QueryInstance.GetParamValue(ItemSpacing, ItemSpace, TEXT("ItemSpacing")) == false || AngleDegree <= 0.f || AngleDegree > 360.f || RadiusValue <= 0.f) { return; } AngleRadians = FMath::DegreesToRadians(Angle.Value); // first generate points on a circle const float CircumferenceLength = 2.f * PI * RadiusValue; const float ArcAnglePercentage = Angle.Value / 360.f; const float ArcLength = CircumferenceLength * ArcAnglePercentage; const int32 StepsCount = FMath::CeilToInt(ArcLength / ItemSpace) + 1; const float AngleStep = AngleDegree / (StepsCount - 1); FVector StartDirection = CalcDirection(QueryInstance); TArray<FVector> CenterLocationCandidates; QueryInstance.PrepareContext(CircleCenter, CenterLocationCandidates); StartDirection = StartDirection.RotateAngleAxis(-AngleDegree/2, FVector::UpVector) * RadiusValue; int NumCenterLocations = CenterLocationCandidates.Num(); if (NumCenterLocations > 0) { for (int i = 0; i < NumCenterLocations; ++i) { GenerateItemsForCircle(CenterLocationCandidates[i], StartDirection, StepsCount, AngleStep, QueryInstance); } } else { FVector CenterLocation(0); AActor* Querier = Cast<AActor>(QueryInstance.Owner.Get()); if (Querier) { CenterLocation = Querier->GetActorLocation(); } GenerateItemsForCircle(CenterLocation, StartDirection, StepsCount, AngleStep, QueryInstance); } }
void UEnvQueryTest::NormalizeItemScores(FEnvQueryInstance& QueryInstance) { if (!IsScoring()) { return; } float WeightValue = 0.0f; if (!QueryInstance.GetParamValue(Weight, WeightValue, TEXT("Weight"))) { return; } float MinScore = 0; float MaxScore = -BIG_NUMBER; if (ClampMinType == EEnvQueryTestClamping::FilterThreshold) { bool bSuccess = QueryInstance.GetParamValue(FloatFilterMin, MinScore, TEXT("FloatFilterMin")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get FloatFilterMin parameter value from EnvQueryInstance %s"), FloatFilterMin.IsNamedParam() ? *FloatFilterMin.ParamName.ToString() : TEXT("<No name specified>")); } } else if (ClampMinType == EEnvQueryTestClamping::SpecifiedValue) { bool bSuccess = QueryInstance.GetParamValue(ScoreClampingMin, MinScore, TEXT("ScoreClampingMin")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get ClampMinType parameter value from EnvQueryInstance %s"), ScoreClampingMin.IsNamedParam() ? *ScoreClampingMin.ParamName.ToString() : TEXT("<No name specified>")); } } if (ClampMaxType == EEnvQueryTestClamping::FilterThreshold) { bool bSuccess = QueryInstance.GetParamValue(FloatFilterMax, MaxScore, TEXT("FloatFilterMax")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get FloatFilterMax parameter value from EnvQueryInstance %s"), FloatFilterMax.IsNamedParam() ? *FloatFilterMax.ParamName.ToString() : TEXT("<No name specified>")); } } else if (ClampMaxType == EEnvQueryTestClamping::SpecifiedValue) { bool bSuccess = QueryInstance.GetParamValue(ScoreClampingMax, MaxScore, TEXT("ScoreClampingMax")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get ScoreClampingMax parameter value from EnvQueryInstance %s"), ScoreClampingMax.IsNamedParam() ? *ScoreClampingMax.ParamName.ToString() : TEXT("<No name specified>")); } } FEnvQueryItemDetails* DetailInfo = QueryInstance.ItemDetails.GetData(); if ((ClampMinType == EEnvQueryTestClamping::None) || (ClampMaxType == EEnvQueryTestClamping::None) ) { for (int32 ItemIndex = 0; ItemIndex < QueryInstance.Items.Num(); ItemIndex++, DetailInfo++) { if (!QueryInstance.Items[ItemIndex].IsValid()) { continue; } float TestValue = DetailInfo->TestResults[QueryInstance.CurrentTest]; if (TestValue != UEnvQueryTypes::SkippedItemValue) { if (ClampMinType == EEnvQueryTestClamping::None) { MinScore = FMath::Min(MinScore, TestValue); } if (ClampMaxType == EEnvQueryTestClamping::None) { MaxScore = FMath::Max(MaxScore, TestValue); } } } } DetailInfo = QueryInstance.ItemDetails.GetData(); if (bNormalizeFromZero && MinScore > 0.0f) { MinScore = 0.0f; } if (MinScore != MaxScore) { for (int32 ItemIndex = 0; ItemIndex < QueryInstance.ItemDetails.Num(); ItemIndex++, DetailInfo++) { if (QueryInstance.Items[ItemIndex].IsValid() == false) { continue; } float WeightedScore = 0.0f; float& TestValue = DetailInfo->TestResults[QueryInstance.CurrentTest]; if (TestValue != UEnvQueryTypes::SkippedItemValue) { const float ClampedScore = FMath::Clamp(TestValue, MinScore, MaxScore); const float NormalizedScore = (ClampedScore - MinScore) / (MaxScore - MinScore); // TODO? Add an option to invert the normalized score before applying an equation. const float NormalizedScoreForEquation = /*bMirrorNormalizedScore ? (1.0f - NormalizedScore) :*/ NormalizedScore; switch (ScoringEquation) { case EEnvTestScoreEquation::Linear: WeightedScore = WeightValue * NormalizedScoreForEquation; break; case EEnvTestScoreEquation::InverseLinear: { // For now, we're avoiding having a separate flag for flipping the direction of the curve // because we don't have usage cases yet and want to avoid too complex UI. If we decide // to add that flag later, we'll need to remove this option, since it should just be "mirror // curve" plus "Linear". float InverseNormalizedScore = (1.0f - NormalizedScoreForEquation); WeightedScore = WeightValue * InverseNormalizedScore; break; } case EEnvTestScoreEquation::Square: WeightedScore = WeightValue * (NormalizedScoreForEquation * NormalizedScoreForEquation); break; case EEnvTestScoreEquation::Constant: // I know, it's not "constant". It's "Constant, or zero". The tooltip should explain that. WeightedScore = (NormalizedScoreForEquation > 0) ? WeightValue : 0.0f; break; default: break; } } else { TestValue = 0.0f; WeightedScore = 0.0f; } #if USE_EQS_DEBUGGER DetailInfo->TestWeightedScores[QueryInstance.CurrentTest] = WeightedScore; #endif QueryInstance.Items[ItemIndex].Score += WeightedScore; } } }
void UEnvQueryGenerator_PathingGrid::GenerateItems(FEnvQueryInstance& QueryInstance) const { #if WITH_RECAST const ARecastNavMesh* NavMesh = FEQSHelpers::FindNavMeshForQuery(QueryInstance); if (NavMesh == NULL) { return; } float PathDistanceValue = 0.0f; float DensityValue = 0.0f; bool bFromContextValue = true; if (!QueryInstance.GetParamValue(MaxPathDistance, PathDistanceValue, TEXT("MaxPathDistance")) || !QueryInstance.GetParamValue(Density, DensityValue, TEXT("Density")) || !QueryInstance.GetParamValue(PathFromContext, bFromContextValue, TEXT("PathFromContext"))) { return; } const int32 ItemCount = FPlatformMath::TruncToInt((PathDistanceValue * 2.0f / DensityValue) + 1); const int32 ItemCountHalf = ItemCount / 2; TArray<FVector> ContextLocations; QueryInstance.PrepareContext(GenerateAround, ContextLocations); QueryInstance.ReserveItemData(ItemCountHalf * ItemCountHalf * ContextLocations.Num()); TArray<NavNodeRef> NavNodeRefs; NavMesh->BeginBatchQuery(); int32 DataOffset = 0; for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { // find all node refs in pathing distance FBox AllowedBounds; NavNodeRefs.Reset(); FindNodeRefsInPathDistance(NavMesh, ContextLocations[ContextIndex], PathDistanceValue, bFromContextValue, NavNodeRefs, AllowedBounds); // cast 2D grid on generated node refs for (int32 IndexX = 0; IndexX < ItemCount; ++IndexX) { for (int32 IndexY = 0; IndexY < ItemCount; ++IndexY) { const FVector TestPoint = ContextLocations[ContextIndex] - FVector(DensityValue * (IndexX - ItemCountHalf), DensityValue * (IndexY - ItemCountHalf), 0); if (!AllowedBounds.IsInsideXY(TestPoint)) { continue; } // trace line on navmesh, and process all hits with collected node refs TArray<FNavLocation> Hits; NavMesh->ProjectPointMulti(TestPoint, Hits, FVector::ZeroVector, AllowedBounds.Min.Z, AllowedBounds.Max.Z); for (int32 HitIndex = 0; HitIndex < Hits.Num(); HitIndex++) { if (IsNavLocationInPathDistance(NavMesh, Hits[HitIndex], NavNodeRefs)) { // store generated point QueryInstance.AddItemData<UEnvQueryItemType_Point>(Hits[HitIndex].Location); } } } } } NavMesh->FinishBatchQuery(); #endif // WITH_RECAST }