diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b26a88bb..afbde3c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## CARLA 0.3.0 + + * Added basic dynamic weather functionality. + - Weather and sun light can be changed during game. + - Presets stored in config file CarlaWeather.ini. + * Add basic functionality to spawn pedestrians. + * Split road meshes for intersections and turns for better precission of the road map. + * Better debug for road map. + * Implemented collision count for other cars and pedestrians. + * Command line argument -carla-settings now accepts relative paths. + * Improved performance when semantic segmentation is disabled. + * Improved tagger system. + ## CARLA 0.2.4 * Fixed serialization of road map resulting in a huge map size. diff --git a/Carla.uplugin b/Carla.uplugin index e51103ee1..59594374d 100644 --- a/Carla.uplugin +++ b/Carla.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "0.2.4", + "VersionName": "0.3.0", "FriendlyName": "CARLA", "Description": "", "Category": "Science", diff --git a/Resources/Example.CarlaSettings.ini b/Resources/Example.CarlaSettings.ini index 20810b9dc..4aa5a2582 100644 --- a/Resources/Example.CarlaSettings.ini +++ b/Resources/Example.CarlaSettings.ini @@ -14,6 +14,14 @@ WorldPort=2000 WritePort=2001 ReadPort=2002 +[CARLA/LevelSettings] +; Number of NPC vehicles to be spawned into the level. +NumberOfVehicles=5 +; Number of NPC pedestrians to be spawned into the level. +NumberOfPedestrians=15 +; Index of the weather presets to use. If negative, weather won't be changed. +WeatherId=-1 + [CARLA/SceneCapture] ; Names of the scene capture cameras to attach to the player, each of them ; should be defined in its own subsection. diff --git a/Resources/RELEASE.README.md b/Resources/RELEASE.README.md index f9e1ab0f2..3704f9231 100644 --- a/Resources/RELEASE.README.md +++ b/Resources/RELEASE.README.md @@ -20,3 +20,32 @@ following [/Script/Engine.RendererSettings] r.CustomDepth=3 ``` + +Weather presets +--------------- + +To change the weather and sun light, set `WeatherId` in CarlaSettings.ini +from the following + + * 0 - Default + * 1 - ClearNoon + * 2 - CloudyNoon + * 3 - WetNoon + * 4 - WetCloudyNoon + * 5 - MidRainyNoon + * 6 - HardRainNoon + * 7 - SoftRainNoon + * 8 - ClearSunset + * 9 - CloudySunset + * 10 - WetSunset + * 11 - WetCloudySunset + * 12 - MidRainSunset + * 13 - HardRainSunset + * 14 - SoftRainSunset + +E.g., to choose the weather to be hard-rain at noon, add to CarlaSettings.ini + +``` +[CARLA/LevelSettings] +WeatherId=6 +``` diff --git a/Source/Carla/AI/WalkerSpawnPoint.h b/Source/Carla/AI/WalkerSpawnPoint.h index d9249df1e..ecf8401df 100644 --- a/Source/Carla/AI/WalkerSpawnPoint.h +++ b/Source/Carla/AI/WalkerSpawnPoint.h @@ -5,9 +5,25 @@ #include "Engine/TargetPoint.h" #include "WalkerSpawnPoint.generated.h" -/// Used to set spawner locations for walkers in the level. -UCLASS() -class CARLA_API AWalkerSpawnPoint : public ATargetPoint +/// Base class for spawner locations for walkers. +UCLASS(Abstract) +class CARLA_API AWalkerSpawnPointBase : public ATargetPoint +{ + GENERATED_BODY() +}; + +/// Used to set spawner locations for walkers in the level. These positions will +/// be used solely to spawn walkers at begin play. +UCLASS() +class CARLA_API AWalkerStartSpawnPoint : public AWalkerSpawnPointBase +{ + GENERATED_BODY() +}; + +/// Used to set spawner locations for walkers in the level. These positions will +/// be used as spawn points as well as destination points for walkers. +UCLASS() +class CARLA_API AWalkerSpawnPoint : public AWalkerSpawnPointBase { GENERATED_BODY() }; diff --git a/Source/Carla/AI/WalkerSpawnerBase.cpp b/Source/Carla/AI/WalkerSpawnerBase.cpp index 813dc8163..7b2b771a2 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,32 @@ 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) { + uint32 Count = 0u; + for (auto i = 0; i < NumberOfWalkers; ++i) { + if (TryToSpawnWalkerAt(*BeginSpawnPoints[i % BeginSpawnPoints.Num()])) { + ++Count; + } + } + UE_LOG(LogCarla, Log, TEXT("Spawned %d walkers at begin play."), Count); } } @@ -65,21 +100,39 @@ 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) { + TrySetDestination(*Walker); + WalkersBlackList.Add(Walker); + Walkers.RemoveAtSwap(Index); } } } @@ -98,45 +151,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/Carla.h b/Source/Carla/Carla.h index cd8de7a90..b44bb2e9c 100644 --- a/Source/Carla/Carla.h +++ b/Source/Carla/Carla.h @@ -12,7 +12,7 @@ DECLARE_LOG_CATEGORY_EXTERN(LogCarla, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogCarlaServer, Log, All); // Options to compile with extra debug options. -#ifdef WITH_EDITOR +#if WITH_EDITOR // #define CARLA_ROAD_GENERATOR_EXTRA_LOG /// @todo #1 Crashes in Linux. // #define CARLA_SERVER_EXTRA_LOG // #define CARLA_TAGGER_EXTRA_LOG diff --git a/Source/Carla/CityMapGenerator.cpp b/Source/Carla/CityMapGenerator.cpp index 743fa3378..a1ac89e94 100644 --- a/Source/Carla/CityMapGenerator.cpp +++ b/Source/Carla/CityMapGenerator.cpp @@ -276,7 +276,7 @@ void ACityMapGenerator::GenerateRoadMap() } } -#ifdef WITH_EDITOR +#if WITH_EDITOR RoadMap->Log(); #endif // WITH_EDITOR @@ -286,7 +286,7 @@ void ACityMapGenerator::GenerateRoadMap() RoadMap->SaveAsPNG(FilePath); } -#ifdef WITH_EDITOR +#if WITH_EDITOR RoadMap->DrawDebugPixelsToLevel(GetWorld(), !bDrawDebugPixelsToLevel); #endif // WITH_EDITOR } diff --git a/Source/Carla/DynamicWeather.cpp b/Source/Carla/DynamicWeather.cpp index faad95dcc..1bbdf98cf 100644 --- a/Source/Carla/DynamicWeather.cpp +++ b/Source/Carla/DynamicWeather.cpp @@ -71,7 +71,17 @@ ADynamicWeather::ADynamicWeather(const FObjectInitializer& ObjectInitializer) void ADynamicWeather::OnConstruction(const FTransform &Transform) { Super::OnConstruction(Transform); +#if WITH_EDITOR Update(); +#endif // WITH_EDITOR +} + +void ADynamicWeather::BeginPlay() +{ + Super::BeginPlay(); +#if WITH_EDITOR + Update(); +#endif // WITH_EDITOR } #if WITH_EDITOR @@ -82,6 +92,9 @@ void ADynamicWeather::PostEditChangeProperty(FPropertyChangedEvent &Event) const FName PropertyName = (Event.Property != NULL ? Event.Property->GetFName() : NAME_None); if (PropertyName == GET_MEMBER_NAME_CHECKED(ADynamicWeather, Weather)) { Update(); + } else if ((PropertyName == GET_MEMBER_NAME_CHECKED(ADynamicWeather, bSaveToConfigFile)) || + (PropertyName == GET_MEMBER_NAME_CHECKED(ADynamicWeather, bLoadFromConfigFile))) { + // Do nothing. } else { AdjustSunPositionBasedOnActorRotation(); } @@ -97,6 +110,7 @@ void ADynamicWeather::PostEditChangeProperty(FPropertyChangedEvent &Event) bLoadFromConfigFile = false; if (LoadFromConfigFile()) { UE_LOG(LogCarla, Log, TEXT("Weather \"%s\" loaded from config file"), *Weather.Name); + Update(); } else { UE_LOG(LogCarla, Error, TEXT("Error loading weather from config file")); } @@ -123,16 +137,6 @@ FVector ADynamicWeather::GetSunDirection() const return - SphericalCoords.SphericalToUnitCartesian(); } -void ADynamicWeather::Update() -{ - // Modify this actor's rotation according to Sun position. - if (!SetActorRotation(FQuat(GetSunDirection().Rotation()), ETeleportType::None)) { - UE_LOG(LogCarla, Warning, TEXT("Unable to rotate actor")); - } - - RefreshWeather(); -} - void ADynamicWeather::AdjustSunPositionBasedOnActorRotation() { const FVector Direction = - GetActorQuat().GetForwardVector(); @@ -143,6 +147,18 @@ void ADynamicWeather::AdjustSunPositionBasedOnActorRotation() #if WITH_EDITOR +void ADynamicWeather::Update() +{ + // Modify this actor's rotation according to Sun position. + if (!SetActorRotation(FQuat(GetSunDirection().Rotation()), ETeleportType::None)) { + UE_LOG(LogCarla, Warning, TEXT("Unable to rotate actor")); + } + + if (bRefreshAutomatically) { + RefreshWeather(); + } +} + bool ADynamicWeather::LoadFromConfigFile() { FString FileName; diff --git a/Source/Carla/DynamicWeather.h b/Source/Carla/DynamicWeather.h index 7340b7e77..e4b57f797 100644 --- a/Source/Carla/DynamicWeather.h +++ b/Source/Carla/DynamicWeather.h @@ -22,6 +22,8 @@ public: virtual void OnConstruction(const FTransform &Transform) override; + virtual void BeginPlay() override; + #if WITH_EDITOR virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; @@ -49,12 +51,12 @@ public: private: - void Update(); - void AdjustSunPositionBasedOnActorRotation(); #if WITH_EDITOR + void Update(); + bool LoadFromConfigFile(); bool SaveToConfigFile() const; @@ -66,9 +68,17 @@ private: UPROPERTY() UArrowComponent *ArrowComponent; + /** If true, the weather is refreshed on construction and at begin play. + * Useful for editing the weather (Editor only). + */ + UPROPERTY(Category = "Weather Description", EditAnywhere) + bool bRefreshAutomatically = false; + + /** Load the section with the currently set name. */ UPROPERTY(Category = "Weather Description", EditAnywhere) bool bLoadFromConfigFile = false; + /** Save current settings to disk. */ UPROPERTY(Category = "Weather Description", EditAnywhere) bool bSaveToConfigFile = false; diff --git a/Source/Carla/Game/CarlaGameInstance.cpp b/Source/Carla/Game/CarlaGameInstance.cpp index 76372e34e..f65568698 100644 --- a/Source/Carla/Game/CarlaGameInstance.cpp +++ b/Source/Carla/Game/CarlaGameInstance.cpp @@ -16,7 +16,8 @@ UCarlaGameInstance::UCarlaGameInstance() { UCarlaGameInstance::~UCarlaGameInstance() {} -void UCarlaGameInstance::InitializeGameControllerIfNotPresent() +void UCarlaGameInstance::InitializeGameControllerIfNotPresent( + const FMockGameControllerSettings &MockControllerSettings) { if (GameController == nullptr) { if (CarlaSettings->bUseNetworking) { @@ -25,8 +26,8 @@ void UCarlaGameInstance::InitializeGameControllerIfNotPresent() CarlaSettings->WritePort, CarlaSettings->ReadPort); } else { - GameController = MakeUnique(); - UE_LOG(LogCarla, Warning, TEXT("Using mock CARLA controller")); + GameController = MakeUnique(MockControllerSettings); + UE_LOG(LogCarla, Log, TEXT("Using mock CARLA controller")); } } } diff --git a/Source/Carla/Game/CarlaGameInstance.h b/Source/Carla/Game/CarlaGameInstance.h index 837f3478c..bd768a904 100644 --- a/Source/Carla/Game/CarlaGameInstance.h +++ b/Source/Carla/Game/CarlaGameInstance.h @@ -7,6 +7,7 @@ #include "CarlaGameInstance.generated.h" class UCarlaSettings; +struct FMockGameControllerSettings; /// The game instance contains elements that must be kept alive in between /// levels. It is instantiate once per game. @@ -21,7 +22,8 @@ public: ~UCarlaGameInstance(); - void InitializeGameControllerIfNotPresent(); + void InitializeGameControllerIfNotPresent( + const FMockGameControllerSettings &MockControllerSettings); CarlaGameControllerBase &GetGameController() { diff --git a/Source/Carla/Game/CarlaGameModeBase.cpp b/Source/Carla/Game/CarlaGameModeBase.cpp index e916d68b4..0de5199ca 100644 --- a/Source/Carla/Game/CarlaGameModeBase.cpp +++ b/Source/Carla/Game/CarlaGameModeBase.cpp @@ -23,6 +23,7 @@ ACarlaGameModeBase::ACarlaGameModeBase(const FObjectInitializer& ObjectInitializ { PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.TickGroup = TG_PrePhysics; + bAllowTickBeforeBeginPlay = false; PlayerControllerClass = ACarlaVehicleController::StaticClass(); GameStateClass = ACarlaGameState::StaticClass(); @@ -43,7 +44,7 @@ void ACarlaGameModeBase::InitGame( checkf( GameInstance != nullptr, TEXT("GameInstance is not a UCarlaGameInstance, did you forget to set it in the project settings?")); - GameInstance->InitializeGameControllerIfNotPresent(); + GameInstance->InitializeGameControllerIfNotPresent(MockGameControllerSettings); GameController = &GameInstance->GetGameController(); GameController->Initialize(GameInstance->GetCarlaSettings()); GameInstance->GetCarlaSettings().LogSettings(); @@ -98,14 +99,21 @@ void ACarlaGameModeBase::BeginPlay() // Change weather. if (DynamicWeather != nullptr) { - const auto &Weather = CarlaSettings.GetActiveWeatherDescription(); - UE_LOG(LogCarla, Log, TEXT("Changing weather settings to \"%s\""), *Weather.Name); - DynamicWeather->SetWeatherDescription(Weather); + const auto *Weather = CarlaSettings.GetActiveWeatherDescription(); + if (Weather != nullptr) { + UE_LOG(LogCarla, Log, TEXT("Changing weather settings to \"%s\""), *Weather->Name); + DynamicWeather->SetWeatherDescription(*Weather); + DynamicWeather->RefreshWeather(); + } + } else { + UE_LOG(LogCarla, Error, TEXT("Missing dynamic weather actor!")); } // Setup walkers. if (WalkerSpawner != nullptr) { WalkerSpawner->SetNumberOfWalkers(CarlaSettings.NumberOfPedestrians); + } else { + UE_LOG(LogCarla, Error, TEXT("Missing walker spawner actor!")); } GameController->BeginPlay(); @@ -164,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, diff --git a/Source/Carla/Game/CarlaGameModeBase.h b/Source/Carla/Game/CarlaGameModeBase.h index 224dfc050..893c0ef9b 100644 --- a/Source/Carla/Game/CarlaGameModeBase.h +++ b/Source/Carla/Game/CarlaGameModeBase.h @@ -6,6 +6,7 @@ #include "AI/WalkerSpawnerBase.h" #include "CarlaGameControllerBase.h" #include "DynamicWeather.h" +#include "MockGameControllerSettings.h" #include "CarlaGameModeBase.generated.h" class APlayerStart; @@ -15,7 +16,7 @@ class UTaggerDelegate; /** * */ -UCLASS() +UCLASS(HideCategories=(ActorTick)) class CARLA_API ACarlaGameModeBase : public AGameModeBase { GENERATED_BODY() @@ -34,6 +35,10 @@ public: protected: + /** Used only when networking is disabled. */ + UPROPERTY(Category = "Mock CARLA Controller", EditAnywhere, BlueprintReadOnly, meta = (ExposeFunctionCategories = "Mock CARLA Controller")) + FMockGameControllerSettings MockGameControllerSettings; + /** The class of DynamicWeather to spawn. */ UPROPERTY(Category = "CARLA Classes", EditAnywhere, BlueprintReadOnly) TSubclassOf DynamicWeatherClass; diff --git a/Source/Carla/Game/CarlaSettings.cpp b/Source/Carla/Game/CarlaSettings.cpp index f5b55a08d..cedcd7b9a 100644 --- a/Source/Carla/Game/CarlaSettings.cpp +++ b/Source/Carla/Game/CarlaSettings.cpp @@ -90,9 +90,9 @@ static void LoadSettingsFromFile(const FString &FileName, UCarlaSettings &Settin Settings.WeatherDescriptions.Empty(); ADynamicWeather::LoadWeatherDescriptionsFromFile(Settings.WeatherDescriptions); check(Settings.WeatherDescriptions.Num() > 0); - if (static_cast(Settings.WeatherId) >= Settings.WeatherDescriptions.Num()) { + if (Settings.WeatherId >= Settings.WeatherDescriptions.Num()) { UE_LOG(LogCarla, Error, TEXT("Provided weather id %d cannot be found"), Settings.WeatherId); - Settings.WeatherId = 0u; + Settings.WeatherId = -1; } // SceneCapture. FString Cameras; diff --git a/Source/Carla/Game/CarlaSettings.h b/Source/Carla/Game/CarlaSettings.h index 0d62f107b..935c751ce 100644 --- a/Source/Carla/Game/CarlaSettings.h +++ b/Source/Carla/Game/CarlaSettings.h @@ -22,9 +22,12 @@ public: /** Log settings values. */ void LogSettings() const; - const FWeatherDescription &GetActiveWeatherDescription() const + const FWeatherDescription *GetActiveWeatherDescription() const { - return WeatherDescriptions[WeatherId]; + if ((WeatherId >= 0) && (WeatherId < WeatherDescriptions.Num())) { + return &WeatherDescriptions[WeatherId]; + } + return nullptr; } private: @@ -70,9 +73,9 @@ public: UPROPERTY(Category = "Level Settings", EditDefaultsOnly) uint32 NumberOfPedestrians = 15u; - /** Index of the weather setting to use. */ + /** Index of the weather setting to use. If negative, weather won't be changed. */ UPROPERTY(Category = "Level Settings", EditDefaultsOnly) - uint32 WeatherId = 0; + int32 WeatherId = -1; /** Available weather settings. */ UPROPERTY(Category = "Level Settings", EditDefaultsOnly) diff --git a/Source/Carla/Game/MockGameController.cpp b/Source/Carla/Game/MockGameController.cpp index 839de6494..dead01937 100644 --- a/Source/Carla/Game/MockGameController.cpp +++ b/Source/Carla/Game/MockGameController.cpp @@ -3,15 +3,46 @@ #include "Carla.h" #include "MockGameController.h" -void MockGameController::Initialize(UCarlaSettings & /*CarlaSettings*/) -{ +MockGameController::MockGameController(const FMockGameControllerSettings &InSettings) : + Settings(InSettings) {} +void MockGameController::Initialize(UCarlaSettings & CarlaSettings) +{ +#if WITH_EDITOR + if (Settings.bOverrideCarlaSettings) { + CarlaSettings.NumberOfVehicles = Settings.NumberOfVehicles; + CarlaSettings.NumberOfPedestrians = Settings.NumberOfPedestrians; + CarlaSettings.WeatherId = Settings.WeatherId; + } +#endif // WITH_EDITOR + + if (Settings.bChangeWeatherOnBeginPlay && (CarlaSettings.WeatherDescriptions.Num() > 0)) { + static uint32 StaticIndex = 0u; + CarlaSettings.WeatherId = StaticIndex % CarlaSettings.WeatherDescriptions.Num(); + ++StaticIndex; + } + +#if WITH_EDITOR + if (Settings.bForceEnableSemanticSegmentation) { + CarlaSettings.bSemanticSegmentationEnabled = true; + } +#endif // WITH_EDITOR } APlayerStart *MockGameController::ChoosePlayerStart( const TArray &AvailableStartSpots) { - return AvailableStartSpots[FMath::RandRange(0, AvailableStartSpots.Num() - 1)]; + check(AvailableStartSpots.Num() > 0); + uint32 Index; + if (Settings.bRandomPlayerStart) { + Index = FMath::RandRange(0, AvailableStartSpots.Num() - 1); + } else { + static uint32 StaticIndex = 0u; + Index = StaticIndex % AvailableStartSpots.Num(); + ++StaticIndex; + } + UE_LOG(LogCarla, Log, TEXT("Spawning player at player start %d/%d"), Index, AvailableStartSpots.Num()); + return AvailableStartSpots[Index]; } void MockGameController::RegisterPlayer(AController &NewPlayer) diff --git a/Source/Carla/Game/MockGameController.h b/Source/Carla/Game/MockGameController.h index 2b95bcf1c..e86b65fc9 100644 --- a/Source/Carla/Game/MockGameController.h +++ b/Source/Carla/Game/MockGameController.h @@ -3,12 +3,15 @@ #pragma once #include "CarlaGameControllerBase.h" +#include "MockGameControllerSettings.h" /// Mocks the CARLA game controller class for testing purposes. class CARLA_API MockGameController : public CarlaGameControllerBase { public: + explicit MockGameController(const FMockGameControllerSettings &Settings); + virtual void Initialize(UCarlaSettings &CarlaSettings) override; virtual APlayerStart *ChoosePlayerStart(const TArray &AvailableStartSpots) override; @@ -18,4 +21,8 @@ public: virtual void BeginPlay() override; virtual void Tick(float DeltaSeconds) override; + +private: + + FMockGameControllerSettings Settings; }; diff --git a/Source/Carla/Game/MockGameControllerSettings.h b/Source/Carla/Game/MockGameControllerSettings.h new file mode 100644 index 000000000..a06a5a0d1 --- /dev/null +++ b/Source/Carla/Game/MockGameControllerSettings.h @@ -0,0 +1,46 @@ +// CARLA, Copyright (C) 2017 Computer Vision Center (CVC) + +#pragma once + +#include "MockGameControllerSettings.generated.h" + +USTRUCT(BlueprintType) +struct FMockGameControllerSettings +{ + GENERATED_USTRUCT_BODY() + + /** If true, weather will be changed every time we start the level. + * + * Has precedence over options in "Override CARLA Settings". + */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller") + bool bChangeWeatherOnBeginPlay = true; + + /** If true, a random player start position will be chosen every time we start the level. */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller") + bool bRandomPlayerStart = true; + + /** If true, semantic segmentation will be always enabled even if no camera needs it. */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller") + bool bForceEnableSemanticSegmentation = false; + +#if WITH_EDITORONLY_DATA + + /** Override available settings in CARLA Settings (Editor only). */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller", meta = (DisplayName = "Override CARLA Settings")) + bool bOverrideCarlaSettings = false; + + /** Number of NPC vehicles to be spawned into the level. */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller", meta = (EditCondition = "bOverrideCarlaSettings", ClampMin = 0)) + int32 NumberOfVehicles = 5; + + /** Number of NPC pedestrians to be spawned into the level. */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller", meta = (EditCondition = "bOverrideCarlaSettings", ClampMin = 0)) + int32 NumberOfPedestrians = 15; + + /** Index of the weather setting to use. If negative, weather won't be changed. */ + UPROPERTY(EditAnywhere, Category = "Mock CARLA Controller", meta = (EditCondition = "bOverrideCarlaSettings")) + int32 WeatherId = -1; + +#endif // WITH_EDITORONLY_DATA +}; diff --git a/Source/Carla/MapGen/RoadMap.cpp b/Source/Carla/MapGen/RoadMap.cpp index 3eefe0cc3..b60fa43cf 100644 --- a/Source/Carla/MapGen/RoadMap.cpp +++ b/Source/Carla/MapGen/RoadMap.cpp @@ -5,7 +5,7 @@ #include "HighResScreenshot.h" -#ifdef WITH_EDITOR +#if WITH_EDITOR #include "DrawDebugHelpers.h" #endif // WITH_EDITOR @@ -261,7 +261,7 @@ bool URoadMap::SaveAsPNG(const FString &Path) const return true; } -#ifdef WITH_EDITOR +#if WITH_EDITOR void URoadMap::Log() const { diff --git a/Source/Carla/MapGen/RoadMap.h b/Source/Carla/MapGen/RoadMap.h index 3ba073526..22a3aca3c 100644 --- a/Source/Carla/MapGen/RoadMap.h +++ b/Source/Carla/MapGen/RoadMap.h @@ -141,7 +141,7 @@ public: /// Save the current map as PNG with the pixel data encoded as color. bool SaveAsPNG(const FString &Path) const; -#ifdef WITH_EDITOR +#if WITH_EDITOR /// Log status of the map to the console. void Log() const;