// Copyright Epic Games, Inc. All Rights Reserved. #include "NavSystemConfigOverride.h" #include "Engine/World.h" #include "GameFramework/WorldSettings.h" #include "AI/NavigationSystemBase.h" #include "NavigationSystem.h" #include "TimerManager.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(NavSystemConfigOverride) #if WITH_EDITORONLY_DATA #include "UObject/ConstructorHelpers.h" #include "Components/BillboardComponent.h" #include "Engine/Texture2D.h" #include "Editor.h" #endif // WITH_EDITORONLY_DATA ANavSystemConfigOverride::ANavSystemConfigOverride(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { USceneComponent* SceneComponent = CreateDefaultSubobject(TEXT("SceneComp")); RootComponent = SceneComponent; RootComponent->Mobility = EComponentMobility::Static; #if WITH_EDITORONLY_DATA bIsSpatiallyLoaded = false; SpriteComponent = CreateEditorOnlyDefaultSubobject(TEXT("Sprite")); if (!IsRunningCommandlet()) { // Structure to hold one-time initialization struct FConstructorStatics { ConstructorHelpers::FObjectFinderOptional NoteTextureObject; FName ID_Notes; FText NAME_Notes; FConstructorStatics() : NoteTextureObject(TEXT("/Engine/EditorResources/S_Note")) , ID_Notes(TEXT("Notes")) , NAME_Notes(NSLOCTEXT("SpriteCategory", "Notes", "Notes")) { } }; static FConstructorStatics ConstructorStatics; if (SpriteComponent) { SpriteComponent->Sprite = ConstructorStatics.NoteTextureObject.Get(); SpriteComponent->SetRelativeScale3D(FVector(0.5f, 0.5f, 0.5f)); SpriteComponent->SpriteInfo.Category = ConstructorStatics.ID_Notes; SpriteComponent->SpriteInfo.DisplayName = ConstructorStatics.NAME_Notes; SpriteComponent->SetupAttachment(RootComponent); SpriteComponent->Mobility = EComponentMobility::Static; } } #endif // WITH_EDITORONLY_DATA SetHidden(true); SetCanBeDamaged(false); bNetLoadOnClient = false; } void ANavSystemConfigOverride::BeginPlay() { Super::BeginPlay(); ApplyConfig(); } void ANavSystemConfigOverride::ApplyConfig() { UWorld* World = GetWorld(); if (World) { UNavigationSystemBase* PrevNavSys = World->GetNavigationSystem(); if (PrevNavSys == nullptr || OverridePolicy == ENavSystemOverridePolicy::Override) { OverrideNavSystem(); } // PrevNavSys != null at this point else if (OverridePolicy == ENavSystemOverridePolicy::Append) { // take the prev nav system and append data to it AppendToNavSystem(*PrevNavSys); } // else PrevNavSys != null AND OverridePolicy == ENavSystemOverridePolicy::Skip, so ignoring the override } } void ANavSystemConfigOverride::PostInitProperties() { Super::PostInitProperties(); } void ANavSystemConfigOverride::AppendToNavSystem(UNavigationSystemBase& PrevNavSys) { if (NavigationSystemConfig) { PrevNavSys.AppendConfig(*NavigationSystemConfig); } } void ANavSystemConfigOverride::OverrideNavSystem() { UWorld* World = GetWorld(); if (World == nullptr) { return; } AWorldSettings* WorldSetting = World->GetWorldSettings(); if (WorldSetting) { WorldSetting->SetNavigationSystemConfigOverride(NavigationSystemConfig); } if (World->bIsWorldInitialized && NavigationSystemConfig) { const FNavigationSystemRunMode RunMode = World->WorldType == EWorldType::Editor ? FNavigationSystemRunMode::EditorMode : (World->WorldType == EWorldType::PIE ? FNavigationSystemRunMode::PIEMode : FNavigationSystemRunMode::GameMode) ; if (FNavigationSystem::IsEditorRunMode(RunMode)) { FNavigationSystem::AddNavigationSystemToWorld(*World, RunMode, NavigationSystemConfig, /*bInitializeForWorld=*/false, /*bOverridePreviousNavSys=*/true); #if WITH_EDITOR UNavigationSystemBase* NewNavSys = World->GetNavigationSystem(); if (NewNavSys) { GEditor->GetTimerManager()->SetTimerForNextTick(FTimerDelegate::CreateUObject(this , &ANavSystemConfigOverride::InitializeForWorld, NewNavSys, World, RunMode)); } #endif // WITH_EDITOR } else { FNavigationSystem::AddNavigationSystemToWorld(*World, RunMode, NavigationSystemConfig, /*bInitializeForWorld=*/true, /*bOverridePreviousNavSys=*/true); } } } #if WITH_EDITOR void ANavSystemConfigOverride::PostRegisterAllComponents() { Super::PostRegisterAllComponents(); // ApplyConfig in PostRegisterAllComponents only for Editor worlds; applied in BeginPlay for Game worlds. UWorld* World = GetWorld(); if (World == nullptr || World->IsGameWorld() || World->WorldType == EWorldType::Inactive) { return; } // While refreshing streaming levels, components are unregistered then registered again // and we don't want to modify the configuration and recreate the NavigationSystem. if (World->IsRefreshingStreamingLevels()) { return; } // Config override should not be applied during cooking. if (GIsCookerLoadingPackage) { return; } ApplyConfig(); } void ANavSystemConfigOverride::PostUnregisterAllComponents() { Super::PostUnregisterAllComponents(); if (NavigationSystemConfig == nullptr) { return; } // ApplyConfig was performed in PostRegisterAllComponents for Editor worlds so nothing to unregister for Game worlds. UWorld* World = GetWorld(); if (World == nullptr || World->IsGameWorld() || World->WorldType == EWorldType::Inactive || !World->IsInitialized() || World->IsBeingCleanedUp()) { return; } // While refreshing streaming levels, components are unregistered then registered again // and we don't want to modify the configuration and recreate the NavigationSystem. if (World->IsRefreshingStreamingLevels()) { return; } // Config override should not be applied during cooking. if (GIsCookerLoadingPackage) { return; } // If our override was used to create the navigation system, we remove the dependency // and recreate the navigation system (if needed after removing the override) AWorldSettings* WorldSetting = World->GetWorldSettings(); if (WorldSetting && WorldSetting->GetNavigationSystemConfigOverride() == NavigationSystemConfig) { WorldSetting->SetNavigationSystemConfigOverride(nullptr); FNavigationSystem::AddNavigationSystemToWorld(*World, FNavigationSystemRunMode::EditorMode, nullptr, /*bInitializeForWorld=*/true, /*bOverridePreviousNavSys=*/true); } } void ANavSystemConfigOverride::InitializeForWorld(UNavigationSystemBase* NewNavSys, UWorld* World, const FNavigationSystemRunMode RunMode) { if (NewNavSys && World) { NewNavSys->InitializeForWorld(*World, RunMode); } } void ANavSystemConfigOverride::ApplyChanges() { UWorld* World = GetWorld(); if (World) { AWorldSettings* WorldSetting = World->GetWorldSettings(); if (WorldSetting) { WorldSetting->SetNavigationSystemConfigOverride(NavigationSystemConfig); } // recreate nav sys World->SetNavigationSystem(nullptr); FNavigationSystem::AddNavigationSystemToWorld(*World, FNavigationSystemRunMode::EditorMode, NavigationSystemConfig, /*bInitializeForWorld=*/true); } } void ANavSystemConfigOverride::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); bNetLoadOnClient = bLoadOnClient; } #endif // WITH_EDITOR