Files
UnrealEngine/Engine/Plugins/Runtime/MassGameplay/Source/MassSpawner/Private/MassEntityEQSSpawnPointsGenerator.cpp
2025-05-18 13:04:45 +08:00

126 lines
4.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MassEntityEQSSpawnPointsGenerator.h"
#include "MassSpawnerTypes.h"
#include "MassSpawnLocationProcessor.h"
#include "VisualLogger/VisualLogger.h"
#include "MassGameplaySettings.h"
#include "MassCommonUtils.h"
UMassEntityEQSSpawnPointsGenerator::UMassEntityEQSSpawnPointsGenerator()
{
EQSRequest.RunMode = EEnvQueryRunMode::AllMatching;
}
void UMassEntityEQSSpawnPointsGenerator::Generate(UObject& QueryOwner, TConstArrayView<FMassSpawnedEntityType> EntityTypes, const int32 Count, FFinishedGeneratingSpawnDataSignature& FinishedGeneratingSpawnPointsDelegate) const
{
if (Count <= 0)
{
FinishedGeneratingSpawnPointsDelegate.Execute(TArray<FMassEntitySpawnDataGeneratorResult>());
return;
}
// Need to copy the request as it is called inside a CDO and CDO states cannot be changed.
FEQSParametrizedQueryExecutionRequest EQSRequestInstanced = EQSRequest;
if (EQSRequestInstanced.IsValid() == false)
{
EQSRequestInstanced.InitForOwnerAndBlackboard(QueryOwner, /*BBAsset=*/nullptr);
if (!ensureMsgf(EQSRequestInstanced.IsValid(), TEXT("Query request initialization can fail due to missing parameters. See the runtime log for details")))
{
return;
}
}
// Build array of entity types to spawn.
// @todo: I dont like that this get's passed by value to OnEQSQueryFinished, but seemed like the cleanest solution.
TArray<FMassEntitySpawnDataGeneratorResult> Results;
BuildResultsFromEntityTypes(Count, EntityTypes, Results);
FQueryFinishedSignature Delegate = FQueryFinishedSignature::CreateUObject(this, &UMassEntityEQSSpawnPointsGenerator::OnEQSQueryFinished, Results, FinishedGeneratingSpawnPointsDelegate);
EQSRequestInstanced.Execute(QueryOwner, /*BlackboardComponent=*/nullptr, Delegate);
}
void UMassEntityEQSSpawnPointsGenerator::OnEQSQueryFinished(TSharedPtr<FEnvQueryResult> EQSResult, TArray<FMassEntitySpawnDataGeneratorResult> Results,
FFinishedGeneratingSpawnDataSignature FinishedGeneratingSpawnPointsDelegate) const
{
if (EQSResult.IsValid() == false || EQSResult->IsSuccessful() == false)
{
UE_VLOG_UELOG(this, LogMassSpawner, Error, TEXT("EQS query failed or result is invalid"));
// Return empty result.
Results.Reset();
FinishedGeneratingSpawnPointsDelegate.Execute(Results);
return;
}
TArray<FVector> Locations;
EQSResult->GetAllAsLocations(Locations);
// Randomize them
FRandomStream RandomStream(UE::Mass::Utils::OverrideRandomSeedForTesting(GetRandomSelectionSeed()));
for (int32 I = 0; I < Locations.Num(); ++I)
{
const int32 J = RandomStream.RandHelper(Locations.Num());
Locations.Swap(I, J);
}
const int32 LocationCount = Locations.Num();
int32 LocationIndex = 0;
// Distribute points amongst the entities to spawn.
for (FMassEntitySpawnDataGeneratorResult& Result : Results)
{
Result.SpawnDataProcessor = UMassSpawnLocationProcessor::StaticClass();
Result.SpawnData.InitializeAs<FMassTransformsSpawnData>();
FMassTransformsSpawnData& Transforms = Result.SpawnData.GetMutable<FMassTransformsSpawnData>();
Transforms.Transforms.Reserve(Result.NumEntities);
for (int i = 0; i < Result.NumEntities; i++)
{
FTransform& Transform = Transforms.Transforms.AddDefaulted_GetRef();
Transform.SetLocation(Locations[LocationIndex % LocationCount]);
LocationIndex++;
}
}
#if ENABLE_VISUAL_LOG
UE_VLOG(this, LogMassSpawner, Log, TEXT("Spawning at %d locations"), LocationIndex);
if (GetDefault<UMassGameplaySettings>()->bLogSpawnLocations)
{
FVisualLogger::Get().ExecuteOnLastEntryForObject(this, [LocationIndex, &Results](FVisualLogEntry& LogEntry)
{
FVisualLogShapeElement Element(TEXT(""), FColor::Orange, /*Thickness*/20, LogMassSpawner.GetCategoryName());
Element.Points.Reserve(LocationIndex);
for (const FMassEntitySpawnDataGeneratorResult& Result : Results)
{
const FMassTransformsSpawnData& Transforms = Result.SpawnData.Get<FMassTransformsSpawnData>();
for (int i = 0; i < Result.NumEntities; i++)
{
Element.Points.Add(Transforms.Transforms[i].GetLocation());
}
}
Element.Type = EVisualLoggerShapeElement::SinglePoint;
Element.Verbosity = ELogVerbosity::Display;
LogEntry.AddElement(Element);
});
}
#endif // ENABLE_VISUAL_LOG
FinishedGeneratingSpawnPointsDelegate.Execute(Results);
}
#if WITH_EDITOR
void UMassEntityEQSSpawnPointsGenerator::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
static const FName EQSRequestName = GET_MEMBER_NAME_CHECKED(UMassEntityEQSSpawnPointsGenerator, EQSRequest);
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.MemberProperty && PropertyChangedEvent.MemberProperty->GetFName() == EQSRequestName)
{
EQSRequest.PostEditChangeProperty(*this, PropertyChangedEvent);
}
}
#endif