From 83840d1626941a75b644aefac7b65eca67e8cd30 Mon Sep 17 00:00:00 2001 From: "j.foucher" Date: Mon, 13 Apr 2026 10:34:37 +0200 Subject: [PATCH] Integration fixes: BaseLevelEntry with full map path, ECC_Camera traces, PP disable, thumbnails - Replace TArray BaseLevels with TArray (DisplayName + MapPath) so levels in subfolders (e.g. /Game/Maps/Warehouse) are found correctly - Add BaseLevelPathMap lookup in MainWidget for Play, combo, and scene load - Add optional MapPath parameter to LoadBaseLevelAsSublevel - Switch all LineTrace channels from ECC_Visibility to ECC_Camera for better compatibility with characters that have Visibility disabled - Add W key binding as alias for Z (translate mode) for AZERTY convenience - Disable sublevel PostProcessVolume WeightedBlendables on load to avoid stencil conflicts with the plugin's selection outline - Fix thumbnail generation: NeverStream + TEXTUREGROUP_UI + force alpha to 255 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../PS_Editor/GameMode/PS_Editor_Pawn.cpp | 11 ++-- .../GameMode/PS_Editor_PlayerController.cpp | 50 ++++++++++++++++--- .../GameMode/PS_Editor_PlayerController.h | 12 ++++- .../PS_Editor/Gizmo/PS_Editor_Gizmo.cpp | 2 +- .../Selection/PS_Editor_SelectionManager.cpp | 4 +- .../Spawn/PS_Editor_SpawnCatalog.cpp | 7 +++ .../PS_Editor/Spawn/PS_Editor_SpawnCatalog.h | 30 +++++++++-- .../Spawn/PS_Editor_SpawnManager.cpp | 5 ++ ...Editor_SpawnableComponentCustomization.cpp | 7 +++ .../Spline/PS_Editor_SplineActor.cpp | 6 +-- .../UI/Widgets/PS_Editor_MainWidget.cpp | 41 ++++++++++++--- .../UI/Widgets/PS_Editor_MainWidget.h | 2 + 12 files changed, 146 insertions(+), 31 deletions(-) diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp index b988290..d2d34d9 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp @@ -163,6 +163,7 @@ void APS_Editor_Pawn::CreateInputActions() IA_TranslateMode = NewObject(this, TEXT("IA_TranslateMode")); IA_TranslateMode->ValueType = EInputActionValueType::Boolean; EditorMappingContext->MapKey(IA_TranslateMode, EKeys::Z); + EditorMappingContext->MapKey(IA_TranslateMode, EKeys::W); IA_RotateMode = NewObject(this, TEXT("IA_RotateMode")); IA_RotateMode->ValueType = EInputActionValueType::Boolean; @@ -609,7 +610,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) FCollisionQueryParams SplineParams; SplineParams.AddIgnoredActor(this); const FVector TraceEnd = RayOrigin + RayDir * 100000.0f; - if (GetWorld()->LineTraceSingleByChannel(SplineHit, RayOrigin, TraceEnd, ECC_Visibility, SplineParams)) + if (GetWorld()->LineTraceSingleByChannel(SplineHit, RayOrigin, TraceEnd, ECC_Camera, SplineParams)) { if (Spline->IsCenterCube(SplineHit.GetComponent())) { @@ -670,7 +671,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) LineParams.AddIgnoredActor(this); const FVector TraceEnd = RayOrigin + RayDir * 100000.0f; - if (GetWorld()->LineTraceSingleByChannel(LineHit, RayOrigin, TraceEnd, ECC_Visibility, LineParams)) + if (GetWorld()->LineTraceSingleByChannel(LineHit, RayOrigin, TraceEnd, ECC_Camera, LineParams)) { for (APS_Editor_SplineActor* Spline : TubeCandidates) { @@ -730,7 +731,7 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) const FVector AddEnd = RayOrigin + RayDir * 100000.0f; FVector ClickPos; - if (GetWorld()->LineTraceSingleByChannel(AddHit, RayOrigin, AddEnd, ECC_Visibility, AddParams)) + if (GetWorld()->LineTraceSingleByChannel(AddHit, RayOrigin, AddEnd, ECC_Camera, AddParams)) { ClickPos = AddHit.ImpactPoint + FVector(0.0f, 0.0f, 5.0f); } @@ -1106,7 +1107,7 @@ void APS_Editor_Pawn::HandleSnapToGround(const FInputActionValue& Value) } } - if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params)) + if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Camera, Params)) { UE_LOG(LogTemp, Log, TEXT("PS_Editor: SnapToGround - PointLoc=%s, HitZ=%.2f, HitActor=%s, HitComp=%s"), *PointLoc.ToString(), Hit.ImpactPoint.Z, @@ -1291,7 +1292,7 @@ void APS_Editor_Pawn::ComputeOrbitFocalPoint() FCollisionQueryParams Params; Params.AddIgnoredActor(this); - if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params)) + if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Camera, Params)) { OrbitFocalPoint = Hit.ImpactPoint; OrbitDistance = FVector::Dist(Start, OrbitFocalPoint); diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp index 29b3445..30458d4 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp @@ -9,6 +9,7 @@ #include "PS_Editor_SplineActor.h" #include "PS_Editor_PointPlaceable.h" #include "Engine/LevelStreamingDynamic.h" +#include "Engine/PostProcessVolume.h" #include "PS_Editor_GameInstanceSubsystem.h" #include "GameFramework/GameModeBase.h" #include "GameFramework/GameStateBase.h" @@ -61,7 +62,17 @@ void APS_Editor_PlayerController::BeginPlay() UE_LOG(LogTemp, Log, TEXT("PS_Editor: Restoring from subsystem - BaseLevel='%s', Scenario='%s'"), *Sub->PendingBaseLevel, *Sub->PendingScenarioName); - LoadBaseLevelAsSublevel(Sub->PendingBaseLevel); + // Resolve full map path from MasterCatalog + FString ResolvedMapPath; + for (const FPS_Editor_BaseLevelEntry& Entry : Master->BaseLevels) + { + if (Entry.DisplayName == Sub->PendingBaseLevel) + { + ResolvedMapPath = Entry.MapPath; + break; + } + } + LoadBaseLevelAsSublevel(Sub->PendingBaseLevel, ResolvedMapPath); if (!Sub->PendingScenarioName.IsEmpty() && SpawnManager && SceneSerializer) { @@ -197,7 +208,7 @@ void APS_Editor_PlayerController::CancelPointPlacement() FinishPointPlacement(); } -void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelName) +void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelName, const FString& MapPath) { // Unload previous sublevel first if (CurrentSublevel) @@ -238,11 +249,14 @@ void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelNa return; } + // Use MapPath if provided, otherwise fall back to LevelName (legacy behavior) + const FString& LevelToLoad = MapPath.IsEmpty() ? LevelName : MapPath; + // Load new sublevel bool bSuccess = false; CurrentSublevel = ULevelStreamingDynamic::LoadLevelInstance( GetWorld(), - LevelName, + LevelToLoad, FVector::ZeroVector, FRotator::ZeroRotator, bSuccess); @@ -252,11 +266,33 @@ void APS_Editor_PlayerController::LoadBaseLevelAsSublevel(const FString& LevelNa CurrentSublevel->SetShouldBeLoaded(true); CurrentSublevel->SetShouldBeVisible(true); CurrentBaseLevel = LevelName; - UE_LOG(LogTemp, Log, TEXT("PS_Editor: Loaded sublevel '%s'"), *LevelName); + + // Disable custom PP materials once sublevel actors are available + CurrentSublevel->OnLevelShown.AddUniqueDynamic(this, &APS_Editor_PlayerController::DisableSublevelPostProcessMaterials); + + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Loaded sublevel '%s' (path: '%s')"), *LevelName, *LevelToLoad); } else { - UE_LOG(LogTemp, Warning, TEXT("PS_Editor: Failed to load sublevel '%s'"), *LevelName); + UE_LOG(LogTemp, Warning, TEXT("PS_Editor: Failed to load sublevel '%s' (path: '%s')"), *LevelName, *LevelToLoad); + } +} + +void APS_Editor_PlayerController::DisableSublevelPostProcessMaterials() +{ + if (!CurrentSublevel) return; + + ULevel* Level = CurrentSublevel->GetLoadedLevel(); + if (!Level) return; + + for (AActor* Actor : Level->Actors) + { + if (!Actor) continue; + APostProcessVolume* PPVolume = Cast(Actor); + if (!PPVolume) continue; + + PPVolume->Settings.WeightedBlendables.Array.Empty(); + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Disabled PP materials on '%s'"), *PPVolume->GetName()); } } @@ -276,7 +312,7 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA const FVector End = WorldOrigin + WorldDirection * 100000.0f; FVector ClickWorldPos; - if (GetWorld()->LineTraceSingleByChannel(Hit, WorldOrigin, End, ECC_Visibility, Params)) + if (GetWorld()->LineTraceSingleByChannel(Hit, WorldOrigin, End, ECC_Camera, Params)) { ClickWorldPos = Hit.ImpactPoint + FVector(0.0f, 0.0f, 5.0f); } @@ -326,7 +362,7 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA TubeParams.AddIgnoredActor(GetPawn()); const FVector TubeEnd = WorldOrigin + WorldDirection * 100000.0f; - if (GetWorld()->LineTraceSingleByChannel(TubeHit, WorldOrigin, TubeEnd, ECC_Visibility, TubeParams)) + if (GetWorld()->LineTraceSingleByChannel(TubeHit, WorldOrigin, TubeEnd, ECC_Camera, TubeParams)) { if (Spline->IsSplineMesh(TubeHit.GetComponent())) { diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h index f51932e..e2d8a59 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h @@ -47,9 +47,13 @@ public: UPROPERTY(BlueprintReadWrite, Category = "PS Editor") FString CurrentBaseLevel; - /** Load a base level as sublevel for visual context. Unloads previous sublevel. */ + /** Load a base level as sublevel for visual context. Unloads previous sublevel. + * @param LevelName Display name stored in CurrentBaseLevel (used for scene filtering). + * @param MapPath Full package path for LoadLevelInstance (e.g. "/Game/Maps/Warehouse"). + * If empty, LevelName is used as-is (legacy behavior). + */ UFUNCTION(BlueprintCallable, Category = "PS Editor") - void LoadBaseLevelAsSublevel(const FString& LevelName); + void LoadBaseLevelAsSublevel(const FString& LevelName, const FString& MapPath = TEXT("")); // ---- Editor Mode (Normal / SplinePlacement) ---- @@ -119,5 +123,9 @@ private: UPROPERTY() TObjectPtr CurrentSublevel; + /** Disable custom post-process materials in the loaded sublevel to avoid stencil conflicts. */ + UFUNCTION() + void DisableSublevelPostProcessMaterials(); + void HandleEditorClick(FVector2D ScreenPos, bool bAdditive); }; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp index 36883c4..7020f11 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Gizmo/PS_Editor_Gizmo.cpp @@ -13,7 +13,7 @@ static void ConfigureGizmoComponent(UPrimitiveComponent* Comp) { Comp->SetCollisionEnabled(ECollisionEnabled::QueryOnly); Comp->SetCollisionResponseToAllChannels(ECR_Ignore); - Comp->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + Comp->SetCollisionResponseToChannel(ECC_Camera, ECR_Block); Comp->SetCollisionObjectType(ECC_WorldDynamic); Comp->SetCastShadow(false); Comp->SetRenderCustomDepth(false); diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp index 6e6da03..883c4a2 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp @@ -127,7 +127,7 @@ void UPS_Editor_SelectionManager::HandleClickAtScreenPosition(FVector2D ScreenPo } const FVector End = WorldOrigin + WorldDirection * 100000.0f; - const bool bHit = PC->GetWorld()->LineTraceSingleByChannel(Hit, WorldOrigin, End, ECC_Visibility, Params); + const bool bHit = PC->GetWorld()->LineTraceSingleByChannel(Hit, WorldOrigin, End, ECC_Camera, Params); if (bHit && Hit.GetActor()) { @@ -467,7 +467,7 @@ void UPS_Editor_SelectionManager::SnapSelectionToGround() Params.AddIgnoredActor(PC->GetPawn()); if (Gizmo) Params.AddIgnoredActor(Gizmo); - if (Actor->GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params)) + if (Actor->GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Camera, Params)) { FVector NewLocation = Hit.ImpactPoint; NewLocation.Z += BottomOffset; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.cpp index e7a93cf..197e7d6 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.cpp @@ -85,11 +85,18 @@ void UPS_Editor_SpawnCatalog::CaptureAllThumbnails() uint8* MipData = Texture->Source.LockMip(0); FMemory::Memcpy(MipData, PixelData.GetData(), PixelData.Num()); + // Force alpha to 255 — UE thumbnails may have zeroed alpha + for (int32 Px = 0; Px < Width * Height; ++Px) + { + MipData[Px * 4 + 3] = 255; + } Texture->Source.UnlockMip(0); Texture->SRGB = true; Texture->CompressionSettings = TC_Default; Texture->MipGenSettings = TMGS_NoMipmaps; + Texture->NeverStream = true; + Texture->LODGroup = TEXTUREGROUP_UI; Texture->PostEditChange(); Texture->UpdateResource(); diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h index fea028d..ebd38ba 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h @@ -44,6 +44,28 @@ public: #endif }; +/** + * A base level entry: display name + full map path. + * The MapPath is used for OpenLevel and sublevel loading. + * Example: DisplayName="Warehouse", MapPath="/Game/Maps/Warehouse" + */ +USTRUCT(BlueprintType) +struct FPS_Editor_BaseLevelEntry +{ + GENERATED_BODY() + + /** Display name shown in the editor combo box and used for scene filtering. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor") + FString DisplayName; + + /** + * Full map path used for OpenLevel and sublevel loading. + * Example: "/Game/Maps/Warehouse" + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor") + FString MapPath; +}; + /** * Master catalog that groups multiple SpawnCatalogs. * Assign this to the PlayerController. Each sub-catalog can represent a domain @@ -60,10 +82,10 @@ public: TArray> Catalogs; /** - * List of base level names available for scene editing. - * Used to filter saved scenes by level. Auto-matched by stripping "_Editor" from the current map name. - * Example: "Warehouse", "Office", "Parking" + * List of base levels available for scene editing. + * DisplayName is used for UI display and scene filtering. + * MapPath is the full package path for OpenLevel (e.g. "/Game/Maps/Warehouse"). */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor") - TArray BaseLevels; + TArray BaseLevels; }; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp index 119c13e..b06ccc3 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp @@ -67,6 +67,11 @@ void UPS_Editor_SpawnManager::AddCatalog(UPS_Editor_SpawnCatalog* InCatalog) Resolved.Category = SpawnComp ? SpawnComp->Category : TEXT("Default"); Resolved.Thumbnail = SpawnComp ? SpawnComp->Thumbnail : nullptr; + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Resolved '%s' - SpawnComp=%s, Thumbnail=%s"), + *Resolved.DisplayName, + SpawnComp ? TEXT("found") : TEXT("null"), + Resolved.Thumbnail ? *Resolved.Thumbnail->GetPathName() : TEXT("null")); + ResolvedEntries.Add(Resolved); } diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnableComponentCustomization.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnableComponentCustomization.cpp index 5206c38..c0bc3a2 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnableComponentCustomization.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnableComponentCustomization.cpp @@ -111,11 +111,18 @@ FReply FPS_Editor_SpawnableComponentCustomization::OnCaptureThumbnailClicked() uint8* MipData = Texture->Source.LockMip(0); FMemory::Memcpy(MipData, PixelData.GetData(), PixelData.Num()); + // Force alpha to 255 — UE thumbnails may have zeroed alpha + for (int32 Px = 0; Px < Width * Height; ++Px) + { + MipData[Px * 4 + 3] = 255; + } Texture->Source.UnlockMip(0); Texture->SRGB = true; Texture->CompressionSettings = TC_Default; Texture->MipGenSettings = TMGS_NoMipmaps; + Texture->NeverStream = true; + Texture->LODGroup = TEXTUREGROUP_UI; Texture->PostEditChange(); Texture->UpdateResource(); diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp index 1d6d417..ee7180c 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp @@ -27,7 +27,7 @@ APS_Editor_SplineActor::APS_Editor_SplineActor() SplineMeshComp->SetupAttachment(Root); SplineMeshComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly); SplineMeshComp->SetCollisionResponseToAllChannels(ECR_Ignore); - SplineMeshComp->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + SplineMeshComp->SetCollisionResponseToChannel(ECC_Camera, ECR_Block); SplineMeshComp->bUseComplexAsSimpleCollision = true; SplineMeshComp->SetCastShadow(false); @@ -101,7 +101,7 @@ void APS_Editor_SplineActor::BeginPlay() CenterCube->SetWorldScale3D(FVector(CenterCubeScale)); CenterCube->SetCollisionEnabled(ECollisionEnabled::QueryOnly); CenterCube->SetCollisionResponseToAllChannels(ECR_Ignore); - CenterCube->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + CenterCube->SetCollisionResponseToChannel(ECC_Camera, ECR_Block); CenterCube->SetCastShadow(false); if (CenterCubeMat) { @@ -316,7 +316,7 @@ UStaticMeshComponent* APS_Editor_SplineActor::CreatePointHandle(const FVector& W // Collision for raycast Handle->SetCollisionEnabled(ECollisionEnabled::QueryOnly); Handle->SetCollisionResponseToAllChannels(ECR_Ignore); - Handle->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + Handle->SetCollisionResponseToChannel(ECC_Camera, ECR_Block); Handle->SetCastShadow(false); Handle->SetRenderInDepthPass(false); diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp index 36659a3..e086b35 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp @@ -624,8 +624,16 @@ TSharedRef UPS_Editor_MainWidget::BuildSceneButtons() EditorPC->bShowMouseCursor = false; EditorPC->SetInputMode(FInputModeGameOnly()); - // Use full path to avoid confusion with sublevel instances - FString MapPath = FString::Printf(TEXT("/Game/%s"), *BaseLevel); + // Use full path from MasterCatalog, fallback to /Game/{name} + FString MapPath; + if (FString* Found = BaseLevelPathMap.Find(BaseLevel)) + { + MapPath = *Found; + } + else + { + MapPath = FString::Printf(TEXT("/Game/%s"), *BaseLevel); + } UGameplayStatics::OpenLevel(EditorPC, FName(*MapPath), true, TEXT("")); return FReply::Handled(); }) @@ -676,7 +684,12 @@ TSharedRef UPS_Editor_MainWidget::BuildSceneButtons() { if (EditorPC->CurrentBaseLevel != SceneLevelName) { - EditorPC->LoadBaseLevelAsSublevel(SceneLevelName); + FString SceneMapPath = SceneLevelName; + if (FString* Found = BaseLevelPathMap.Find(SceneLevelName)) + { + SceneMapPath = *Found; + } + EditorPC->LoadBaseLevelAsSublevel(SceneLevelName, SceneMapPath); CurrentLevelFilter = EditorPC->CurrentBaseLevel; for (const TSharedPtr& Opt : BaseLevelOptions) { @@ -748,7 +761,12 @@ TSharedRef UPS_Editor_MainWidget::BuildSceneButtons() SaveNameField->SetText(FText::GetEmpty()); } - EditorPC->LoadBaseLevelAsSublevel(*Selected); + FString SublevelPath = *Selected; + if (FString* Found = BaseLevelPathMap.Find(*Selected)) + { + SublevelPath = *Found; + } + EditorPC->LoadBaseLevelAsSublevel(*Selected, SublevelPath); CurrentLevelFilter = EditorPC->CurrentBaseLevel; if (BaseLevelComboText.IsValid()) @@ -834,9 +852,13 @@ void UPS_Editor_MainWidget::NativeConstruct() BaseLevelOptions.Add(MakeShared(TEXT("None"))); if (UPS_Editor_MasterCatalog* Master = EditorPC->MasterCatalogAsset) { - for (const FString& Level : Master->BaseLevels) + for (const FPS_Editor_BaseLevelEntry& Entry : Master->BaseLevels) { - BaseLevelOptions.Add(MakeShared(Level)); + BaseLevelOptions.Add(MakeShared(Entry.DisplayName)); + if (!Entry.MapPath.IsEmpty()) + { + BaseLevelPathMap.Add(Entry.DisplayName, Entry.MapPath); + } } } @@ -915,7 +937,12 @@ void UPS_Editor_MainWidget::PopulateSceneList() { if (EditorPC->CurrentBaseLevel != SceneLevelName) { - EditorPC->LoadBaseLevelAsSublevel(SceneLevelName); + FString SceneMapPath = SceneLevelName; + if (FString* Found = BaseLevelPathMap.Find(SceneLevelName)) + { + SceneMapPath = *Found; + } + EditorPC->LoadBaseLevelAsSublevel(SceneLevelName, SceneMapPath); CurrentLevelFilter = EditorPC->CurrentBaseLevel; for (const TSharedPtr& Opt : BaseLevelOptions) diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.h index f59a16b..842beb9 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.h @@ -75,6 +75,8 @@ private: TArray> BaseLevelOptions; TSharedPtr BaseLevelComboText; FString CurrentLevelFilter; + /** Maps DisplayName → full MapPath for base levels (from MasterCatalog). */ + TMap BaseLevelPathMap; TArray> CachedBrushes; // Transform fields