Spawn walkers at begin play and add a black list for misbehaving walkers

This commit is contained in:
nsubiron 2017-05-24 11:31:14 +01:00
parent 54cd5fa775
commit 795ec2e12e
3 changed files with 121 additions and 43 deletions

View File

@ -14,9 +14,24 @@
// -- Static local methods -----------------------------------------------------
// =============================================================================
static bool WalkerIsValid(ACharacter *Walker)
{
return ((Walker != nullptr) && !Walker->IsPendingKill());
}
static AWalkerAIController *GetController(ACharacter *Walker)
{
return (Walker != nullptr ? Cast<AWalkerAIController>(Walker->GetController()) : nullptr);
return (WalkerIsValid(Walker) ? Cast<AWalkerAIController>(Walker->GetController()) : nullptr);
}
static float GetDistance(const FVector &Location0, const FVector &Location1)
{
return FMath::Abs((Location0 - Location1).Size());
}
static float GetDistance(const AActor &Actor0, const AActor &Actor1)
{
return GetDistance(Actor0.GetActorLocation(), Actor1.GetActorLocation());
}
// =============================================================================
@ -39,6 +54,8 @@ void AWalkerSpawnerBase::BeginPlay()
{
Super::BeginPlay();
NumberOfWalkers = FMath::Max(0, NumberOfWalkers);
// Allocate space for walkers.
Walkers.Reserve(NumberOfWalkers);
@ -50,14 +67,28 @@ void AWalkerSpawnerBase::BeginPlay()
}
// Find spawn points present in level.
for (TActorIterator<AWalkerSpawnPoint> It(GetWorld()); It; ++It) {
SpawnPoints.Add(*It);
TArray<AWalkerSpawnPointBase *> BeginSpawnPoints;
for (TActorIterator<AWalkerSpawnPointBase> It(GetWorld()); It; ++It) {
BeginSpawnPoints.Add(*It);
AWalkerSpawnPoint *SpawnPoint = Cast<AWalkerSpawnPoint>(*It);
if (SpawnPoint != nullptr) {
SpawnPoints.Add(SpawnPoint);
}
}
UE_LOG(LogCarla, Log, TEXT("Found %d positions for spawning walkers"), SpawnPoints.Num());
UE_LOG(LogCarla, Log, TEXT("Found %d positions for spawning walkers at begin play."), BeginSpawnPoints.Num());
UE_LOG(LogCarla, Log, TEXT("Found %d positions for spawning walkers during game play."), SpawnPoints.Num());
if (SpawnPoints.Num() < 2) {
bSpawnWalkers = false;
UE_LOG(LogCarla, Error, TEXT("We don't have enough spawn points for walkers!"));
} else if (BeginSpawnPoints.Num() < NumberOfWalkers) {
UE_LOG(LogCarla, Warning, TEXT("Requested %d walkers, but we only have %d spawn points. Some will fail to spawn."), NumberOfWalkers, BeginSpawnPoints.Num());
}
if (bSpawnWalkers) {
for (auto i = 0; i < NumberOfWalkers; ++i) {
TryToSpawnWalkerAt(*BeginSpawnPoints[i % BeginSpawnPoints.Num()]);
}
}
}
@ -65,21 +96,38 @@ void AWalkerSpawnerBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (bSpawnWalkers && (NumberOfWalkers > Walkers.Num())) {
if (bSpawnWalkers && (NumberOfWalkers > GetCurrentNumberOfWalkers())) {
// Try to spawn one walker.
TryToSpawnRandomWalker();
TryToSpawnWalkerAt(GetRandomSpawnPoint());
}
if (WalkersBlackList.Num() > 0) {
// If still stuck in the black list, just kill it.
const int32 Index = (++CurrentIndexToCheck % WalkersBlackList.Num());
auto Walker = WalkersBlackList[Index];
auto Controller = GetController(Walker);
if ((Controller == nullptr) ||
(Controller->GetMoveStatus() != EPathFollowingStatus::Moving)) {
WalkersBlackList.RemoveAtSwap(Index);
if (Walker != nullptr) {
Walker->Destroy();
}
}
}
if (Walkers.Num() > 0) {
// Check one walker and kill it if necessary.
// Check one walker, if fails black-list it or kill it.
const int32 Index = (++CurrentIndexToCheck % Walkers.Num());
auto Walker = Walkers[Index];
auto Controller = GetController(Walker);
if ((Controller == nullptr) || (Controller->GetMoveStatus() != EPathFollowingStatus::Moving)) {
if (Controller == nullptr) {
Walkers.RemoveAtSwap(Index);
if (Walker != nullptr) {
Walker->Destroy();
}
} else if (Controller->GetMoveStatus() != EPathFollowingStatus::Moving) {
WalkersBlackList.Add(Walker);
Walkers.RemoveAtSwap(Index);
}
}
}
@ -98,45 +146,64 @@ void AWalkerSpawnerBase::SetNumberOfWalkers(const int32 Count)
}
}
void AWalkerSpawnerBase::TryToSpawnRandomWalker()
const AWalkerSpawnPointBase &AWalkerSpawnerBase::GetRandomSpawnPoint() const
{
auto SpawnPoint = GetRandomSpawnPoint();
auto DestinationPoint = GetRandomSpawnPoint();
if ((SpawnPoint != nullptr) && (DestinationPoint != nullptr)) {
const auto StraightDistance =
DestinationPoint->GetActorLocation() -
SpawnPoint->GetActorLocation();
if (StraightDistance.Size() >= MinimumWalkDistance) {
SpawnWalkerAtSpawnPoint(*SpawnPoint, DestinationPoint->GetActorLocation());
}
} else {
UE_LOG(LogCarla, Error, TEXT("Unable to find spawn point"));
}
check(SpawnPoints.Num() > 0);
return *SpawnPoints[RandomStream.RandRange(0, SpawnPoints.Num() - 1)];
}
void AWalkerSpawnerBase::SpawnWalkerAtSpawnPoint(
const AWalkerSpawnPoint &SpawnPoint,
const FVector &Destination)
bool AWalkerSpawnerBase::TryGetValidDestination(const FVector &Origin, FVector &Destination) const
{
const auto &DestinationPoint = GetRandomSpawnPoint();
Destination = DestinationPoint.GetActorLocation();
return (GetDistance(Origin, Destination) >= MinimumWalkDistance);
}
bool AWalkerSpawnerBase::TryToSpawnWalkerAt(const AWalkerSpawnPointBase &SpawnPoint)
{
// Try find destination.
FVector Destination;
if (!TryGetValidDestination(SpawnPoint.GetActorLocation(), Destination)) {
return false;
}
// Spawn walker.
ACharacter *Walker;
SpawnWalker(SpawnPoint.GetActorTransform(), Walker);
if ((Walker != nullptr) && !Walker->IsPendingKill()) {
Walker->AIControllerClass = AWalkerAIController::StaticClass();
Walker->SpawnDefaultController();
auto Controller = GetController(Walker);
if (Controller != nullptr) { // Sometimes fails...
Controller->MoveToLocation(Destination);
Walkers.Add(Walker);
} else {
UE_LOG(LogCarla, Error, TEXT("Something went wrong creating the controller for the new walker"));
Walker->Destroy();
}
if (!WalkerIsValid(Walker)) {
return false;
}
// Assign controller.
Walker->AIControllerClass = AWalkerAIController::StaticClass();
Walker->SpawnDefaultController();
auto Controller = GetController(Walker);
if (Controller == nullptr) { // Sometimes fails...
UE_LOG(LogCarla, Error, TEXT("Something went wrong creating the controller for the new walker"));
Walker->Destroy();
return false;
}
// Add walker and set destination.
Walkers.Add(Walker);
Controller->MoveToLocation(Destination);
return true;
}
AWalkerSpawnPoint *AWalkerSpawnerBase::GetRandomSpawnPoint() const
bool AWalkerSpawnerBase::TrySetDestination(ACharacter &Walker) const
{
return (SpawnPoints.Num() > 0 ?
SpawnPoints[RandomStream.RandRange(0, SpawnPoints.Num() - 1)] :
nullptr);
// Try to retrieve controller.
auto Controller = GetController(&Walker);
if (Controller == nullptr) {
return false;
}
// Try find destination.
FVector Destination;
if (!TryGetValidDestination(Walker.GetActorLocation(), Destination)) {
return false;
}
Controller->MoveToLocation(Destination);
return true;
}

View File

@ -6,6 +6,7 @@
#include "WalkerSpawnerBase.generated.h"
class AWalkerSpawnPoint;
class AWalkerSpawnPointBase;
class UBoxComponent;
/// Base class for spawning walkers. Implement SpawnWalker in derived
@ -64,13 +65,20 @@ public:
void SetNumberOfWalkers(int32 Count);
int32 GetCurrentNumberOfWalkers() const
{
return Walkers.Num() + WalkersBlackList.Num();
}
private:
void TryToSpawnRandomWalker();
const AWalkerSpawnPointBase &GetRandomSpawnPoint() const;
void SpawnWalkerAtSpawnPoint(const AWalkerSpawnPoint &SpawnPoint, const FVector &Destination);
bool TryGetValidDestination(const FVector &Origin, FVector &Destination) const;
AWalkerSpawnPoint *GetRandomSpawnPoint() const;
bool TryToSpawnWalkerAt(const AWalkerSpawnPointBase &SpawnPoint);
bool TrySetDestination(ACharacter &Walker) const;
/// @}
@ -105,5 +113,8 @@ private:
UPROPERTY(Category = "Walker Spawner", VisibleAnywhere, AdvancedDisplay)
TArray<ACharacter *> Walkers;
UPROPERTY(Category = "Walker Spawner", VisibleAnywhere, AdvancedDisplay)
TArray<ACharacter *> WalkersBlackList;
uint32 CurrentIndexToCheck = 0u;
};

View File

@ -172,7 +172,7 @@ APlayerStart *ACarlaGameModeBase::FindUnOccupiedStartPoints(
if (!GetWorld()->EncroachingBlockingGeometry(PawnToFit, ActorLocation, ActorRotation)) {
UnOccupiedStartPoints.Add(PlayerStart);
}
#ifdef WITH_EDITOR
#if WITH_EDITOR
else if (GetWorld()->FindTeleportSpot(PawnToFit, ActorLocation, ActorRotation)) {
UE_LOG(
LogCarla,