Fix packaged build: weak ptr timing, factory module, hard refs, brace fix
Critical fixes for packaged builds: - Widget weak ptrs (SpawnManager/SceneSerializer) are null in packaged due to HUD deferred init. All button lambdas now get references fresh from PlayerController via GetOwningPlayer() cast instead of cached weak ptrs. - HUD BeginPlay deferred by one tick (SetTimerForNextTick) to ensure PlayerController has initialized SpawnManager before wiring. Module split: - Factories moved to separate PS_EditorTools module (Type: Editor) to fix UHT error with UFactory in packaging builds. - PS_Editor module no longer depends on UnrealEd for factories. Asset references: - TSoftObjectPtr/TSoftClassPtr changed to TObjectPtr/TSubclassOf for MasterCatalog, SpawnCatalogs, and ActorClass entries. Hard references ensure assets are cooked in packaged builds. Other fixes: - GetActorLabel() replaced with GetClass()->GetName() (editor-only API) - Missing brace in Browse dialog BaseLevel sync code - BaseLevel sync only when scenario specifies a LevelName (no more clearing on empty) - Save As dialog with native Windows file picker - Debug info in catalog UI when empty (shows chain: Master→Catalogs→Entries) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,11 @@
|
||||
"Name": "PS_Editor",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
},
|
||||
{
|
||||
"Name": "PS_EditorTools",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
|
||||
@@ -41,11 +41,11 @@ void APS_Editor_PlayerController::BeginPlay()
|
||||
|
||||
// Load catalogs from MasterCatalog
|
||||
|
||||
if (UPS_Editor_MasterCatalog* Master = MasterCatalogAsset.LoadSynchronous())
|
||||
if (UPS_Editor_MasterCatalog* Master = MasterCatalogAsset)
|
||||
{
|
||||
for (const TSoftObjectPtr<UPS_Editor_SpawnCatalog>& CatalogRef : Master->Catalogs)
|
||||
for (UPS_Editor_SpawnCatalog* Catalog : Master->Catalogs)
|
||||
{
|
||||
if (UPS_Editor_SpawnCatalog* Catalog = CatalogRef.LoadSynchronous())
|
||||
if (Catalog)
|
||||
{
|
||||
SpawnManager->AddCatalog(Catalog);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
|
||||
/** Master catalog grouping all sub-catalogs (Tools, Characters, Props...). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||
TSoftObjectPtr<UPS_Editor_MasterCatalog> MasterCatalogAsset;
|
||||
TObjectPtr<UPS_Editor_MasterCatalog> MasterCatalogAsset;
|
||||
|
||||
/** Current base level for scene save/load filtering. Auto-detected at startup, switchable via UI. */
|
||||
UPROPERTY(BlueprintReadWrite, Category = "PS Editor")
|
||||
|
||||
@@ -29,7 +29,8 @@ public class PS_Editor : ModuleRules
|
||||
"UMG",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"ProceduralMeshComponent"
|
||||
"ProceduralMeshComponent",
|
||||
"DesktopPlatform"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
@@ -38,7 +39,7 @@ public class PS_Editor : ModuleRules
|
||||
"SlateCore"
|
||||
});
|
||||
|
||||
// Editor-only: Factory for SpawnCatalog DataAsset creation
|
||||
// Editor-only: CaptureAllThumbnails uses ThumbnailTools, material auto-generation uses AssetRegistry
|
||||
if (Target.bBuildEditor)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
|
||||
@@ -16,7 +16,7 @@ void UPS_Editor_SpawnCatalog::CaptureAllThumbnails()
|
||||
|
||||
for (FPS_Editor_SpawnEntry& Entry : Entries)
|
||||
{
|
||||
UClass* LoadedClass = Entry.ActorClass.LoadSynchronous();
|
||||
UClass* LoadedClass = Entry.ActorClass;
|
||||
if (!LoadedClass)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: Failed to load class for catalog entry"));
|
||||
|
||||
@@ -15,7 +15,7 @@ struct FPS_Editor_SpawnEntry
|
||||
|
||||
/** The Blueprint class to spawn. Must have a PS_Editor_SpawnableComponent for name/category. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||
TSoftClassPtr<AActor> ActorClass;
|
||||
TSubclassOf<AActor> ActorClass;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ class PS_EDITOR_API UPS_Editor_MasterCatalog : public UPrimaryDataAsset
|
||||
public:
|
||||
/** Sub-catalogs to load. Each one contributes its entries to the runtime spawn UI. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||
TArray<TSoftObjectPtr<UPS_Editor_SpawnCatalog>> Catalogs;
|
||||
TArray<TObjectPtr<UPS_Editor_SpawnCatalog>> Catalogs;
|
||||
|
||||
/**
|
||||
* List of base level names available for scene editing.
|
||||
|
||||
@@ -18,8 +18,7 @@ void UPS_Editor_SpawnManager::AddCatalog(UPS_Editor_SpawnCatalog* InCatalog)
|
||||
|
||||
for (const FPS_Editor_SpawnEntry& Entry : InCatalog->Entries)
|
||||
{
|
||||
// Synchronously load the class
|
||||
UClass* LoadedClass = Entry.ActorClass.LoadSynchronous();
|
||||
UClass* LoadedClass = Entry.ActorClass;
|
||||
if (!LoadedClass)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: Failed to load class for catalog entry"));
|
||||
|
||||
@@ -2,26 +2,29 @@
|
||||
#include "PS_Editor_MainWidget.h"
|
||||
#include "PS_Editor_PlayerController.h"
|
||||
#include "PS_Editor_SelectionManager.h"
|
||||
#include "PS_Editor_SpawnManager.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
|
||||
void APS_Editor_HUD::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (APlayerController* PC = GetOwningPlayerController())
|
||||
// Defer widget wiring to next tick to ensure PlayerController::BeginPlay has run
|
||||
GetWorldTimerManager().SetTimerForNextTick([this]()
|
||||
{
|
||||
MainWidget = CreateWidget<UPS_Editor_MainWidget>(PC, UPS_Editor_MainWidget::StaticClass());
|
||||
if (MainWidget)
|
||||
{
|
||||
// Wire the SelectionManager to the UI
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(PC))
|
||||
{
|
||||
MainWidget->SetSelectionManager(EditorPC->GetSelectionManager());
|
||||
MainWidget->SetSpawnManager(EditorPC->GetSpawnManager());
|
||||
MainWidget->SetSceneSerializer(EditorPC->GetSceneSerializer());
|
||||
}
|
||||
APlayerController* PC = GetOwningPlayerController();
|
||||
if (!PC) return;
|
||||
|
||||
MainWidget->AddToViewport(0);
|
||||
MainWidget = CreateWidget<UPS_Editor_MainWidget>(PC, UPS_Editor_MainWidget::StaticClass());
|
||||
if (!MainWidget) return;
|
||||
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(PC))
|
||||
{
|
||||
MainWidget->SetSelectionManager(EditorPC->GetSelectionManager());
|
||||
MainWidget->SetSpawnManager(EditorPC->GetSpawnManager());
|
||||
MainWidget->SetSceneSerializer(EditorPC->GetSceneSerializer());
|
||||
}
|
||||
}
|
||||
|
||||
MainWidget->AddToViewport(0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Widgets/SOverlay.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "DesktopPlatformModule.h"
|
||||
#include "PS_Editor_GameInstanceSubsystem.h"
|
||||
|
||||
void UPS_Editor_MainWidget::SetSelectionManager(UPS_Editor_SelectionManager* InSelectionManager)
|
||||
@@ -463,14 +464,24 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
if (SaveNameField.IsValid() && SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
if (SaveNameField.IsValid() && SceneSerializer.IsValid())
|
||||
{
|
||||
FString Name = SaveNameField->GetText().ToString();
|
||||
if (!Name.IsEmpty())
|
||||
{
|
||||
SceneSerializer->SaveScene(Name, SpawnManager.Get());
|
||||
// Refresh scene list
|
||||
PopulateSceneList();
|
||||
// Get SpawnManager fresh from PlayerController (widget weak ptr may be stale in packaged)
|
||||
UPS_Editor_SpawnManager* SaveSM = nullptr;
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
SaveSM = EditorPC->GetSpawnManager();
|
||||
}
|
||||
if (!SaveSM) SaveSM = SpawnManager.Get();
|
||||
|
||||
if (SaveSM)
|
||||
{
|
||||
SceneSerializer->SaveScene(Name, SaveSM);
|
||||
PopulateSceneList();
|
||||
}
|
||||
}
|
||||
}
|
||||
return FReply::Handled();
|
||||
@@ -481,6 +492,53 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
||||
if (!DesktopPlatform) return FReply::Handled();
|
||||
|
||||
const FString DefaultPath = UPS_Editor_SceneLoader::GetScenesDirectory();
|
||||
TArray<FString> OutFiles;
|
||||
|
||||
void* ParentWindowHandle = FSlateApplication::Get().GetActiveTopLevelWindow().IsValid()
|
||||
? FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()->GetOSWindowHandle()
|
||||
: nullptr;
|
||||
|
||||
bool bOpened = DesktopPlatform->SaveFileDialog(
|
||||
ParentWindowHandle,
|
||||
TEXT("Save Scenario As"),
|
||||
DefaultPath,
|
||||
SaveNameField.IsValid() ? SaveNameField->GetText().ToString() + TEXT(".json") : TEXT(""),
|
||||
TEXT("Scenario Files (*.json)|*.json"),
|
||||
0,
|
||||
OutFiles);
|
||||
|
||||
if (bOpened && OutFiles.Num() > 0)
|
||||
{
|
||||
FString FileName = FPaths::GetBaseFilename(OutFiles[0]);
|
||||
if (!FileName.IsEmpty() && SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
{
|
||||
SceneSerializer->SaveScene(FileName, SpawnManager.Get());
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
SaveNameField->SetText(FText::FromString(FileName));
|
||||
}
|
||||
PopulateSceneList();
|
||||
}
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Save As")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
]
|
||||
]
|
||||
// Action buttons: New + Load list
|
||||
+ SVerticalBox::Slot()
|
||||
@@ -518,13 +576,14 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
if (!EditorPC) return FReply::Handled();
|
||||
|
||||
// Auto-save current scene before playing
|
||||
if (SaveNameField.IsValid() && SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
UPS_Editor_SpawnManager* PlaySM = EditorPC->GetSpawnManager();
|
||||
UPS_Editor_SceneSerializer* PlaySer = EditorPC->GetSceneSerializer();
|
||||
if (SaveNameField.IsValid() && PlaySer && PlaySM)
|
||||
{
|
||||
FString SceneName = SaveNameField->GetText().ToString();
|
||||
if (!SceneName.IsEmpty())
|
||||
{
|
||||
SceneSerializer->SaveScene(SceneName, SpawnManager.Get());
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Auto-saved '%s' before Play"), *SceneName);
|
||||
PlaySer->SaveScene(SceneName, PlaySM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,7 +591,7 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
FString BaseLevel = EditorPC->CurrentBaseLevel;
|
||||
if (BaseLevel.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: No BaseLevel set, cannot Play"));
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: No BaseLevel set, cannot Play. CurrentBaseLevel is empty."));
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
@@ -566,6 +625,78 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
.ColorAndOpacity(FLinearColor(0.2f, 0.9f, 0.2f))
|
||||
]
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(4.0f, 0.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
||||
if (!DesktopPlatform) return FReply::Handled();
|
||||
|
||||
const FString DefaultPath = UPS_Editor_SceneLoader::GetScenesDirectory();
|
||||
TArray<FString> OutFiles;
|
||||
|
||||
void* ParentWindowHandle = FSlateApplication::Get().GetActiveTopLevelWindow().IsValid()
|
||||
? FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()->GetOSWindowHandle()
|
||||
: nullptr;
|
||||
|
||||
bool bOpened = DesktopPlatform->OpenFileDialog(
|
||||
ParentWindowHandle,
|
||||
TEXT("Load Scenario"),
|
||||
DefaultPath,
|
||||
TEXT(""),
|
||||
TEXT("Scenario Files (*.json)|*.json"),
|
||||
0,
|
||||
OutFiles);
|
||||
|
||||
if (bOpened && OutFiles.Num() > 0)
|
||||
{
|
||||
FString FileName = FPaths::GetBaseFilename(OutFiles[0]);
|
||||
|
||||
if (SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
{
|
||||
// Sync BaseLevel sublevel only if scenario specifies one
|
||||
FString SceneLevelName = UPS_Editor_SceneLoader::GetSceneLevelName(FileName);
|
||||
if (!SceneLevelName.IsEmpty())
|
||||
{
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (EditorPC->CurrentBaseLevel != SceneLevelName)
|
||||
{
|
||||
EditorPC->LoadBaseLevelAsSublevel(SceneLevelName);
|
||||
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
||||
for (const TSharedPtr<FString>& Opt : BaseLevelOptions)
|
||||
{
|
||||
if (*Opt == SceneLevelName)
|
||||
{
|
||||
if (BaseLevelCombo.IsValid()) BaseLevelCombo->SetSelectedItem(Opt);
|
||||
if (BaseLevelComboText.IsValid()) BaseLevelComboText->SetText(FText::FromString(SceneLevelName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneSerializer->LoadScene(FileName, SpawnManager.Get());
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
SaveNameField->SetText(FText::FromString(FileName));
|
||||
}
|
||||
PopulateSceneList();
|
||||
LastOutlinerActorCount = -1;
|
||||
}
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Browse")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
]
|
||||
]
|
||||
// BaseLevel combo box
|
||||
+ SVerticalBox::Slot()
|
||||
@@ -594,9 +725,12 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
if (!EditorPC) return;
|
||||
|
||||
// Clear current scenario before switching BaseLevel
|
||||
if (SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
if (UPS_Editor_SceneSerializer* Ser = EditorPC->GetSceneSerializer())
|
||||
{
|
||||
SceneSerializer->ClearScene(SpawnManager.Get());
|
||||
if (UPS_Editor_SpawnManager* SM = EditorPC->GetSpawnManager())
|
||||
{
|
||||
Ser->ClearScene(SM);
|
||||
}
|
||||
}
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
@@ -686,7 +820,7 @@ void UPS_Editor_MainWidget::NativeConstruct()
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
BaseLevelOptions.Add(MakeShared<FString>(TEXT("None")));
|
||||
if (UPS_Editor_MasterCatalog* Master = EditorPC->MasterCatalogAsset.Get())
|
||||
if (UPS_Editor_MasterCatalog* Master = EditorPC->MasterCatalogAsset)
|
||||
{
|
||||
for (const FString& Level : Master->BaseLevels)
|
||||
{
|
||||
@@ -761,11 +895,36 @@ void UPS_Editor_MainWidget::PopulateSceneList()
|
||||
{
|
||||
if (SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
{
|
||||
// Sync the BaseLevel sublevel only if scenario specifies one
|
||||
FString SceneLevelName = UPS_Editor_SceneLoader::GetSceneLevelName(Name);
|
||||
if (!SceneLevelName.IsEmpty())
|
||||
{
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (EditorPC->CurrentBaseLevel != SceneLevelName)
|
||||
{
|
||||
EditorPC->LoadBaseLevelAsSublevel(SceneLevelName);
|
||||
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
||||
|
||||
for (const TSharedPtr<FString>& Opt : BaseLevelOptions)
|
||||
{
|
||||
if (*Opt == SceneLevelName)
|
||||
{
|
||||
if (BaseLevelCombo.IsValid()) BaseLevelCombo->SetSelectedItem(Opt);
|
||||
if (BaseLevelComboText.IsValid()) BaseLevelComboText->SetText(FText::FromString(SceneLevelName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneSerializer->LoadScene(Name, SpawnManager.Get());
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
SaveNameField->SetText(FText::FromString(Name));
|
||||
}
|
||||
LastOutlinerActorCount = -1;
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
@@ -794,9 +953,9 @@ void UPS_Editor_MainWidget::PopulateSpawnCatalog()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("No catalog loaded")))
|
||||
.Text(FText::FromString(TEXT("SpawnManager is null")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Italic", 9))
|
||||
.ColorAndOpacity(FLinearColor(0.5f, 0.5f, 0.5f))
|
||||
.ColorAndOpacity(FLinearColor(1.0f, 0.3f, 0.3f))
|
||||
];
|
||||
return;
|
||||
}
|
||||
@@ -804,13 +963,35 @@ void UPS_Editor_MainWidget::PopulateSpawnCatalog()
|
||||
const TArray<FPS_Editor_ResolvedEntry>& Entries = SM->GetResolvedEntries();
|
||||
if (Entries.Num() == 0)
|
||||
{
|
||||
// Debug: show why catalog is empty
|
||||
FString DebugInfo = TEXT("Catalog empty.");
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (EditorPC->MasterCatalogAsset)
|
||||
{
|
||||
DebugInfo += FString::Printf(TEXT(" Master OK (%d sub-catalogs)."),
|
||||
EditorPC->MasterCatalogAsset->Catalogs.Num());
|
||||
for (UPS_Editor_SpawnCatalog* Cat : EditorPC->MasterCatalogAsset->Catalogs)
|
||||
{
|
||||
DebugInfo += FString::Printf(TEXT(" [%s:%d]"),
|
||||
Cat ? *Cat->GetName() : TEXT("NULL"),
|
||||
Cat ? Cat->Entries.Num() : 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugInfo += TEXT(" MasterCatalog is NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
SpawnListContainer->AddSlot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Catalog is empty")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Italic", 9))
|
||||
.ColorAndOpacity(FLinearColor(0.5f, 0.5f, 0.5f))
|
||||
.Text(FText::FromString(DebugInfo))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Italic", 8))
|
||||
.ColorAndOpacity(FLinearColor(1.0f, 0.5f, 0.3f))
|
||||
.AutoWrapText(true)
|
||||
];
|
||||
return;
|
||||
}
|
||||
@@ -886,7 +1067,13 @@ void UPS_Editor_MainWidget::PopulateSpawnCatalog()
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this, EntryIndex]() -> FReply
|
||||
{
|
||||
UPS_Editor_SpawnManager* Mgr = SpawnManager.Get();
|
||||
// Get SpawnManager fresh from PlayerController
|
||||
UPS_Editor_SpawnManager* Mgr = nullptr;
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
Mgr = PC->GetSpawnManager();
|
||||
}
|
||||
if (!Mgr) Mgr = SpawnManager.Get();
|
||||
if (!Mgr) return FReply::Handled();
|
||||
|
||||
// Check if this actor implements IPS_Editor_PointPlaceable → enter placement mode
|
||||
@@ -1073,7 +1260,9 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
||||
|
||||
if (SelectionInfoText.IsValid())
|
||||
{
|
||||
FString Info = FString::Printf(TEXT("%s (%d)"), *Actor->GetActorLabel(), Selected.Num());
|
||||
FString ActorName = Actor->GetClass()->GetName();
|
||||
ActorName.RemoveFromEnd(TEXT("_C"));
|
||||
FString Info = FString::Printf(TEXT("%s (%d)"), *ActorName, Selected.Num());
|
||||
SelectionInfoText->SetText(FText::FromString(Info));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class PS_EditorTools : ModuleRules
|
||||
{
|
||||
public PS_EditorTools(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"PS_Editor",
|
||||
"UnrealEd"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "PS_EditorTools.h"
|
||||
|
||||
IMPLEMENT_MODULE(FPS_EditorToolsModule, PS_EditorTools)
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FPS_EditorToolsModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override {}
|
||||
virtual void ShutdownModule() override {}
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "PS_Editor_SpawnCatalogFactory.h"
|
||||
#include "PS_Editor_SpawnCatalog.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "PS_Editor"
|
||||
|
||||
// ---- SpawnCatalog Factory ----
|
||||
|
||||
UPS_Editor_SpawnCatalogFactory::UPS_Editor_SpawnCatalogFactory()
|
||||
{
|
||||
SupportedClass = UPS_Editor_SpawnCatalog::StaticClass();
|
||||
@@ -22,7 +22,7 @@ FText UPS_Editor_SpawnCatalogFactory::GetDisplayName() const
|
||||
return LOCTEXT("SpawnCatalogDisplayName", "PS Editor Spawn Catalog");
|
||||
}
|
||||
|
||||
// ---- Master Catalog Factory ----
|
||||
// ---- MasterCatalog Factory ----
|
||||
|
||||
UPS_Editor_MasterCatalogFactory::UPS_Editor_MasterCatalogFactory()
|
||||
{
|
||||
@@ -42,5 +42,3 @@ FText UPS_Editor_MasterCatalogFactory::GetDisplayName() const
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "PS_Editor_SpawnCatalogFactory.generated.h"
|
||||
@@ -31,5 +29,3 @@ public:
|
||||
virtual bool ShouldShowInNewMenu() const override { return true; }
|
||||
virtual FText GetDisplayName() const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user