From 795ec2e12eaf8cf907ec5380b8beb8c61174f8f1 Mon Sep 17 00:00:00 2001 From: nsubiron Date: Wed, 24 May 2017 11:31:14 +0100 Subject: [PATCH] Spawn walkers at begin play and add a black list for misbehaving walkers --- Source/Carla/AI/WalkerSpawnerBase.cpp | 145 +++++++++++++++++------- Source/Carla/AI/WalkerSpawnerBase.h | 17 ++- Source/Carla/Game/CarlaGameModeBase.cpp | 2 +- 3 files changed, 121 insertions(+), 43 deletions(-) diff --git a/Source/Carla/AI/WalkerSpawnerBase.cpp b/Source/Carla/AI/WalkerSpawnerBase.cpp index 813dc8163..94865b24b 100644 --- a/Source/Carla/AI/WalkerSpawnerBase.cpp +++ b/Source/Carla/AI/WalkerSpawnerBase.cpp @@ -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(Walker->GetController()) : nullptr); + return (WalkerIsValid(Walker) ? Cast(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 It(GetWorld()); It; ++It) { - SpawnPoints.Add(*It); + TArray BeginSpawnPoints; + for (TActorIterator It(GetWorld()); It; ++It) { + BeginSpawnPoints.Add(*It); + AWalkerSpawnPoint *SpawnPoint = Cast(*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; } diff --git a/Source/Carla/AI/WalkerSpawnerBase.h b/Source/Carla/AI/WalkerSpawnerBase.h index fd9703e71..f2a186056 100644 --- a/Source/Carla/AI/WalkerSpawnerBase.h +++ b/Source/Carla/AI/WalkerSpawnerBase.h @@ -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 Walkers; + UPROPERTY(Category = "Walker Spawner", VisibleAnywhere, AdvancedDisplay) + TArray WalkersBlackList; + uint32 CurrentIndexToCheck = 0u; }; diff --git a/Source/Carla/Game/CarlaGameModeBase.cpp b/Source/Carla/Game/CarlaGameModeBase.cpp index 19c4c747e..0de5199ca 100644 --- a/Source/Carla/Game/CarlaGameModeBase.cpp +++ b/Source/Carla/Game/CarlaGameModeBase.cpp @@ -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,