Files
UnrealEngineUWP/Engine/Source/Developer/FunctionalTesting/Private/FunctionalAITest.cpp
Mieszko Zielinski 7cb8d165ef Functional testing improvements merged over from Fortnite branch #UE4
- AI functional test got an option to decfine time delay between spawns in a spawnset

[CL 2097018 by Mieszko Zielinski in Main branch]
2014-06-06 06:27:44 -04:00

270 lines
6.5 KiB
C++

// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#include "FunctionalTestingPrivatePCH.h"
#include "ObjectEditorUtils.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "BehaviorTree/BehaviorTree.h"
#include "AIController.h"
AFunctionalAITest::AFunctionalAITest( const class FPostConstructInitializeProperties& PCIP )
: Super(PCIP)
, CurrentSpawnSetIndex(INDEX_NONE)
{
/*struct FConstructorStatics
{
ConstructorHelpers::FObjectFinderOptional<UTexture2D> Texture;
FConstructorStatics()
: Texture(TEXT("/Engine/EditorResources/S_FTest"))
{
}
};*/
}
bool AFunctionalAITest::IsOneOfSpawnedPawns(AActor* Actor)
{
APawn* Pawn = Cast<APawn>(Actor);
return Pawn != NULL && SpawnedPawns.Contains(Pawn);
}
void AFunctionalAITest::BeginPlay()
{
// do a post-load step and remove all disabled spawn sets
for(int32 Index = SpawnSets.Num()-1; Index >= 0; --Index)
{
FAITestSpawnSet& SpawnSet = SpawnSets[Index];
if (SpawnSet.bEnabled == false)
{
UE_LOG(LogFunctionalTest, Log, TEXT("Removing disabled spawn set \'%s\'."), *SpawnSets[Index].Name.ToString());
SpawnSets.RemoveAt(Index, 1, false);
}
else
{
// update all spawn info that doesn't have spawn location set, and set spawn set name
for (int32 SpawnIndex = 0; SpawnIndex < SpawnSet.SpawnInfoContainer.Num(); ++SpawnIndex)
{
FAITestSpawnInfo& SpawnInfo = SpawnSet.SpawnInfoContainer[SpawnIndex];
SpawnInfo.SpawnSetName = SpawnSet.Name;
if (SpawnInfo.SpawnLocation == NULL)
{
SpawnInfo.SpawnLocation = SpawnSet.FallbackSpawnLocation;
}
}
}
}
SpawnSets.Shrink();
Super::BeginPlay();
}
bool AFunctionalAITest::StartTest()
{
KillOffSpawnedPawns();
ClearPendingDelayedSpawns();
if (++CurrentSpawnSetIndex < SpawnSets.Num())
{
UWorld* World = GetWorld();
check(World);
const FAITestSpawnSet& SpawnSet = SpawnSets[CurrentSpawnSetIndex];
bool bSuccessfullySpawnedAll = true;
// NOTE: even if some pawns fail to spawn we don't stop spawning to find all spawns that will fails.
// all spawned pawns get filled off in case of failure.
CurrentSpawnSetName = SpawnSet.Name.ToString();
for (int32 SpawnIndex = 0; SpawnIndex < SpawnSet.SpawnInfoContainer.Num(); ++SpawnIndex)
{
const FAITestSpawnInfo& SpawnInfo = SpawnSet.SpawnInfoContainer[SpawnIndex];
if (SpawnInfo.IsValid())
{
if (SpawnInfo.SpawnDelay == 0.0)
{
for (int32 SpawnedCount = 0; SpawnedCount < SpawnInfo.NumberToSpawn; ++SpawnedCount)
{
bSuccessfullySpawnedAll &= SpawnInfo.Spawn(this);
}
}
else
{
bSuccessfullySpawnedAll &= SpawnInfo.Spawn(this);
if (SpawnInfo.NumberToSpawn > 1)
{
PendingDelayedSpawns.Add(SpawnInfo);
}
}
}
else
{
FString FailureMessage = FString::Printf(TEXT("Spawn set \'%s\' contains invalid entry at index %d")
, *SpawnSet.Name.ToString()
, SpawnIndex);
UE_LOG(LogFunctionalTest, Warning, TEXT("%s"), *FailureMessage);
bSuccessfullySpawnedAll = false;
}
}
if (bSuccessfullySpawnedAll == false)
{
KillOffSpawnedPawns();
}
else
{
if (PendingDelayedSpawns.Num() > 0)
{
SetActorTickEnabled(true);
}
return Super::StartTest();
}
}
return false;
}
void AFunctionalAITest::FinishTest(TEnumAsByte<EFunctionalTestResult::Type> TestResult, const FString& Message)
{
Super::FinishTest(TestResult, Message);
}
bool AFunctionalAITest::WantsToRunAgain() const
{
return CurrentSpawnSetIndex + 1 < SpawnSets.Num();
}
void AFunctionalAITest::CleanUp()
{
Super::CleanUp();
CurrentSpawnSetIndex = INDEX_NONE;
KillOffSpawnedPawns();
ClearPendingDelayedSpawns();
}
FString AFunctionalAITest::GetAdditionalTestFinishedMessage(EFunctionalTestResult::Type TestResult) const
{
FString Result;
if (SpawnedPawns.Num() > 0)
{
if (CurrentSpawnSetName.Len() > 0 && CurrentSpawnSetName != TEXT("None"))
{
Result = FString::Printf(TEXT("set %s, pawns: "), *CurrentSpawnSetName);
}
else
{
Result = TEXT("pawns: ");
}
for (int32 PawnIndex = 0; PawnIndex < SpawnedPawns.Num(); ++PawnIndex)
{
Result += FString::Printf(TEXT("%s, "), *GetNameSafe(SpawnedPawns[PawnIndex]));
}
}
return Result;
}
void AFunctionalAITest::KillOffSpawnedPawns()
{
for (int32 PawnIndex = 0; PawnIndex < SpawnedPawns.Num(); ++PawnIndex)
{
if (SpawnedPawns[PawnIndex])
{
SpawnedPawns[PawnIndex]->Destroy();
}
}
SpawnedPawns.Reset();
}
void AFunctionalAITest::ClearPendingDelayedSpawns()
{
SetActorTickEnabled(false);
PendingDelayedSpawns.Reset();
}
void AFunctionalAITest::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
for (auto& DelayedSpawn : PendingDelayedSpawns)
{
DelayedSpawn.Tick(DeltaSeconds, this);
}
}
//----------------------------------------------------------------------//
// FAITestSpawnInfo
//----------------------------------------------------------------------//
bool FAITestSpawnInfo::Spawn(AFunctionalAITest* AITest) const
{
check(AITest);
bool bSuccessfullySpawned = false;
APawn* SpawnedPawn = UAIBlueprintHelperLibrary::SpawnAIFromClass(AITest->GetWorld(), PawnClass, BehaviorTree
, SpawnLocation->GetActorLocation()
, SpawnLocation->GetActorRotation()
, /*bNoCollisionFail=*/true);
if (SpawnedPawn == NULL)
{
FString FailureMessage = FString::Printf(TEXT("Failed to spawn \'%s\' pawn (\'%s\' set) ")
, *GetNameSafe(PawnClass)
, *SpawnSetName.ToString());
UE_LOG(LogFunctionalTest, Warning, TEXT("%s"), *FailureMessage);
}
else if (SpawnedPawn->GetController() == NULL)
{
FString FailureMessage = FString::Printf(TEXT("Spawned Pawn %s (\'%s\' set) has no controller ")
, *GetNameSafe(SpawnedPawn)
, *SpawnSetName.ToString());
UE_LOG(LogFunctionalTest, Warning, TEXT("%s"), *FailureMessage);
}
else
{
AITest->SpawnedPawns.Add(SpawnedPawn);
AITest->OnAISpawned.Broadcast(Cast<AAIController>(SpawnedPawn->GetController()), SpawnedPawn);
bSuccessfullySpawned = true;
}
return bSuccessfullySpawned;
}
//----------------------------------------------------------------------//
//
//----------------------------------------------------------------------//
FPendingDelayedSpawn::FPendingDelayedSpawn(const FAITestSpawnInfo& Source)
: NumberToSpawnLeft(0), bFinished(false)
{
*((FAITestSpawnInfo*)this) = Source;
TimeToNextSpawn = Source.SpawnDelay;
NumberToSpawnLeft = Source.NumberToSpawn - 1;
}
void FPendingDelayedSpawn::Tick(float TimeDelta, AFunctionalAITest* AITest)
{
if (bFinished)
{
return;
}
TimeToNextSpawn -= TimeDelta;
if (TimeToNextSpawn <= 0)
{
Spawn(AITest);
TimeToNextSpawn = SpawnDelay;
bFinished = (--NumberToSpawnLeft <= 0);
}
}