Add Phase 4: JSON scene save/load system
Serialization:
- SceneData USTRUCT with Version, SceneName, Timestamp, Actors array
- ActorData: ClassPath, Location, Rotation, Scale per spawned actor
- SceneSerializer: SaveScene/LoadScene/ClearScene/GetSavedSceneNames
- Files stored in Saved/PS_Editor/Scenes/{name}.json
- Uses FJsonObjectConverter for clean USTRUCT-to-JSON conversion
SpawnManager:
- Tracks all spawned actors in SpawnedActors array
- SpawnActorDirect: spawn from class/transform (used by scene loading)
- DestroyAllSpawnedActors: clean scene clear
UI:
- Save field + button in toolbar area
- New Scene button to clear all spawned actors
- Saved scenes list with click-to-load
- Auto-refreshes scene list after save
- Clears selection on load/new
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -2,6 +2,7 @@
|
||||
#include "PS_Editor_SelectionManager.h"
|
||||
#include "PS_Editor_UndoManager.h"
|
||||
#include "PS_Editor_SpawnManager.h"
|
||||
#include "PS_Editor_SceneSerializer.h"
|
||||
#include "PS_Editor_Gizmo.h"
|
||||
#include "PS_Editor_Pawn.h"
|
||||
|
||||
@@ -28,6 +29,9 @@ void APS_Editor_PlayerController::BeginPlay()
|
||||
UPS_Editor_SpawnCatalog* LoadedCatalog = SpawnCatalogAsset.LoadSynchronous();
|
||||
SpawnManager->Initialize(this, LoadedCatalog);
|
||||
|
||||
SceneSerializer = NewObject<UPS_Editor_SceneSerializer>(this);
|
||||
SceneSerializer->SetSelectionManager(SelectionManager);
|
||||
|
||||
// After undo/redo, deselect hidden actors and refresh gizmo
|
||||
UndoManager->OnUndoRedo.AddWeakLambda(this, [this]()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ class UPS_Editor_SelectionManager;
|
||||
class UPS_Editor_UndoManager;
|
||||
class UPS_Editor_SpawnManager;
|
||||
class UPS_Editor_SpawnCatalog;
|
||||
class UPS_Editor_SceneSerializer;
|
||||
|
||||
/**
|
||||
* Player controller for PS_Editor runtime editing.
|
||||
@@ -30,6 +31,9 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "PS Editor")
|
||||
UPS_Editor_SpawnManager* GetSpawnManager() const { return SpawnManager; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "PS Editor")
|
||||
UPS_Editor_SceneSerializer* GetSceneSerializer() const { return SceneSerializer; }
|
||||
|
||||
/** The spawn catalog DataAsset to load. Set this in the Blueprint or World Settings. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||
TSoftObjectPtr<UPS_Editor_SpawnCatalog> SpawnCatalogAsset;
|
||||
@@ -48,5 +52,8 @@ private:
|
||||
UPROPERTY()
|
||||
TObjectPtr<UPS_Editor_SpawnManager> SpawnManager;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UPS_Editor_SceneSerializer> SceneSerializer;
|
||||
|
||||
void HandleEditorClick(FVector2D ScreenPos, bool bAdditive);
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ public class PS_Editor : ModuleRules
|
||||
PublicIncludePaths.Add(Path.Combine(SourceDir, "Selection"));
|
||||
PublicIncludePaths.Add(Path.Combine(SourceDir, "Gizmo"));
|
||||
PublicIncludePaths.Add(Path.Combine(SourceDir, "Spawn"));
|
||||
PublicIncludePaths.Add(Path.Combine(SourceDir, "Serialization"));
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
@@ -24,7 +25,9 @@ public class PS_Editor : ModuleRules
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"EnhancedInput",
|
||||
"UMG"
|
||||
"UMG",
|
||||
"Json",
|
||||
"JsonUtilities"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "PS_Editor_SceneData.generated.h"
|
||||
|
||||
/** Serialized data for a single spawned actor. */
|
||||
USTRUCT()
|
||||
struct FPS_Editor_ActorData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Class path (e.g. "/Game/BP_Cube.BP_Cube_C"). */
|
||||
UPROPERTY()
|
||||
FString ClassPath;
|
||||
|
||||
UPROPERTY()
|
||||
FVector Location = FVector::ZeroVector;
|
||||
|
||||
UPROPERTY()
|
||||
FRotator Rotation = FRotator::ZeroRotator;
|
||||
|
||||
UPROPERTY()
|
||||
FVector Scale = FVector::OneVector;
|
||||
};
|
||||
|
||||
/** Serialized data for a complete scene. */
|
||||
USTRUCT()
|
||||
struct FPS_Editor_SceneData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
int32 Version = 1;
|
||||
|
||||
UPROPERTY()
|
||||
FString SceneName;
|
||||
|
||||
UPROPERTY()
|
||||
FString Timestamp;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<FPS_Editor_ActorData> Actors;
|
||||
};
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "PS_Editor_SceneSerializer.h"
|
||||
#include "PS_Editor_SpawnManager.h"
|
||||
#include "PS_Editor_SelectionManager.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
|
||||
bool UPS_Editor_SceneSerializer::SaveScene(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager)
|
||||
{
|
||||
if (!SpawnManager || SceneName.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build scene data from currently spawned actors
|
||||
FPS_Editor_SceneData SceneData;
|
||||
SceneData.SceneName = SceneName;
|
||||
SceneData.Timestamp = FDateTime::Now().ToString();
|
||||
|
||||
const TArray<TWeakObjectPtr<AActor>>& SpawnedActors = SpawnManager->GetSpawnedActors();
|
||||
for (const TWeakObjectPtr<AActor>& Weak : SpawnedActors)
|
||||
{
|
||||
AActor* Actor = Weak.Get();
|
||||
if (!Actor || Actor->IsHidden())
|
||||
{
|
||||
continue; // Skip destroyed or soft-deleted actors
|
||||
}
|
||||
|
||||
FPS_Editor_ActorData ActorData;
|
||||
ActorData.ClassPath = Actor->GetClass()->GetPathName();
|
||||
ActorData.Location = Actor->GetActorLocation();
|
||||
ActorData.Rotation = Actor->GetActorRotation();
|
||||
ActorData.Scale = Actor->GetActorScale3D();
|
||||
|
||||
SceneData.Actors.Add(ActorData);
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
FString JsonString;
|
||||
if (!FJsonObjectConverter::UStructToJsonObjectString(SceneData, JsonString, 0, 0, 0, nullptr, true))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("PS_Editor: Failed to serialize scene to JSON"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
const FString Directory = GetScenesDirectory();
|
||||
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
||||
if (!PlatformFile.DirectoryExists(*Directory))
|
||||
{
|
||||
PlatformFile.CreateDirectoryTree(*Directory);
|
||||
}
|
||||
|
||||
// Write file
|
||||
const FString FilePath = GetSceneFilePath(SceneName);
|
||||
if (!FFileHelper::SaveStringToFile(JsonString, *FilePath))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("PS_Editor: Failed to write scene file: %s"), *FilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Scene saved: %s (%d actors) -> %s"), *SceneName, SceneData.Actors.Num(), *FilePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager)
|
||||
{
|
||||
if (!SpawnManager || SceneName.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read file
|
||||
const FString FilePath = GetSceneFilePath(SceneName);
|
||||
FString JsonString;
|
||||
if (!FFileHelper::LoadFileToString(JsonString, *FilePath))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("PS_Editor: Failed to read scene file: %s"), *FilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse JSON
|
||||
FPS_Editor_SceneData SceneData;
|
||||
if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &SceneData, 0, 0))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("PS_Editor: Failed to parse scene JSON"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear existing spawned actors
|
||||
ClearScene(SpawnManager);
|
||||
|
||||
// Re-spawn actors from saved data
|
||||
int32 SpawnedCount = 0;
|
||||
for (const FPS_Editor_ActorData& ActorData : SceneData.Actors)
|
||||
{
|
||||
UClass* LoadedClass = LoadObject<UClass>(nullptr, *ActorData.ClassPath);
|
||||
if (!LoadedClass)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: Failed to load class: %s"), *ActorData.ClassPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
AActor* SpawnedActor = SpawnManager->SpawnActorDirect(LoadedClass, ActorData.Location, ActorData.Rotation, ActorData.Scale);
|
||||
if (SpawnedActor)
|
||||
{
|
||||
SpawnedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Scene loaded: %s (%d actors)"), *SceneName, SpawnedCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<FString> UPS_Editor_SceneSerializer::GetSavedSceneNames() const
|
||||
{
|
||||
TArray<FString> SceneNames;
|
||||
const FString Directory = GetScenesDirectory();
|
||||
|
||||
TArray<FString> Files;
|
||||
IFileManager::Get().FindFiles(Files, *FPaths::Combine(Directory, TEXT("*.json")), true, false);
|
||||
|
||||
for (const FString& File : Files)
|
||||
{
|
||||
SceneNames.Add(FPaths::GetBaseFilename(File));
|
||||
}
|
||||
|
||||
SceneNames.Sort();
|
||||
return SceneNames;
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneSerializer::DeleteScene(const FString& SceneName)
|
||||
{
|
||||
const FString FilePath = GetSceneFilePath(SceneName);
|
||||
if (IFileManager::Get().Delete(*FilePath))
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Scene deleted: %s"), *SceneName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UPS_Editor_SceneSerializer::ClearScene(UPS_Editor_SpawnManager* SpawnManager)
|
||||
{
|
||||
if (UPS_Editor_SelectionManager* SM = SelectionManagerRef.Get())
|
||||
{
|
||||
SM->ClearSelection();
|
||||
}
|
||||
|
||||
if (SpawnManager)
|
||||
{
|
||||
SpawnManager->DestroyAllSpawnedActors();
|
||||
}
|
||||
}
|
||||
|
||||
FString UPS_Editor_SceneSerializer::GetSceneFilePath(const FString& SceneName) const
|
||||
{
|
||||
return FPaths::Combine(GetScenesDirectory(), SceneName + TEXT(".json"));
|
||||
}
|
||||
|
||||
FString UPS_Editor_SceneSerializer::GetScenesDirectory() const
|
||||
{
|
||||
return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("PS_Editor"), TEXT("Scenes"));
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "PS_Editor_SceneData.h"
|
||||
#include "PS_Editor_SceneSerializer.generated.h"
|
||||
|
||||
class UPS_Editor_SpawnManager;
|
||||
class UPS_Editor_SelectionManager;
|
||||
|
||||
/**
|
||||
* Handles saving and loading PS_Editor scenes as JSON files.
|
||||
* Scenes are stored in {ProjectDir}/Saved/PS_Editor/Scenes/{SceneName}.json
|
||||
*/
|
||||
UCLASS()
|
||||
class PS_EDITOR_API UPS_Editor_SceneSerializer : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Save the current spawned actors to a JSON file. */
|
||||
bool SaveScene(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager);
|
||||
|
||||
/** Load a scene from a JSON file. Destroys current spawned actors and re-spawns from file. */
|
||||
bool LoadScene(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager);
|
||||
|
||||
/** Get a list of all saved scene names (without .json extension). */
|
||||
TArray<FString> GetSavedSceneNames() const;
|
||||
|
||||
/** Delete a saved scene file. */
|
||||
bool DeleteScene(const FString& SceneName);
|
||||
|
||||
/** Clear all spawned actors (new scene). Also clears selection and undo history. */
|
||||
void ClearScene(UPS_Editor_SpawnManager* SpawnManager);
|
||||
|
||||
/** Set optional selection manager to clear on load/new. */
|
||||
void SetSelectionManager(UPS_Editor_SelectionManager* InSelectionManager) { SelectionManagerRef = InSelectionManager; }
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<UPS_Editor_SelectionManager> SelectionManagerRef;
|
||||
|
||||
/** Get the full file path for a scene name. */
|
||||
FString GetSceneFilePath(const FString& SceneName) const;
|
||||
|
||||
/** Get the directory where scenes are stored. */
|
||||
FString GetScenesDirectory() const;
|
||||
};
|
||||
@@ -138,6 +138,9 @@ AActor* UPS_Editor_SpawnManager::SpawnFromCatalogAtLocation(int32 Index, FVector
|
||||
}
|
||||
}
|
||||
|
||||
// Track spawned actor
|
||||
SpawnedActors.Add(SpawnedActor);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Spawned %s at %s"), *Entry.DisplayName, *WorldLocation.ToString());
|
||||
return SpawnedActor;
|
||||
}
|
||||
@@ -158,6 +161,48 @@ FVector UPS_Editor_SpawnManager::ComputeSpawnLocation() const
|
||||
return CameraLocation + CameraRotation.Vector() * 500.0f;
|
||||
}
|
||||
|
||||
AActor* UPS_Editor_SpawnManager::SpawnActorDirect(UClass* ActorClass, FVector Location, FRotator Rotation, FVector Scale)
|
||||
{
|
||||
APlayerController* PC = OwnerPC.Get();
|
||||
if (!PC || !PC->GetWorld() || !ActorClass)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FActorSpawnParameters SpawnParams;
|
||||
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
|
||||
|
||||
AActor* SpawnedActor = PC->GetWorld()->SpawnActor<AActor>(ActorClass, Location, Rotation, SpawnParams);
|
||||
if (!SpawnedActor)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (USceneComponent* Root = SpawnedActor->GetRootComponent())
|
||||
{
|
||||
Root->SetMobility(EComponentMobility::Movable);
|
||||
}
|
||||
|
||||
SpawnedActor->SetActorScale3D(Scale);
|
||||
SpawnedActors.Add(SpawnedActor);
|
||||
|
||||
return SpawnedActor;
|
||||
}
|
||||
|
||||
void UPS_Editor_SpawnManager::DestroyAllSpawnedActors()
|
||||
{
|
||||
for (const TWeakObjectPtr<AActor>& Weak : SpawnedActors)
|
||||
{
|
||||
if (AActor* Actor = Weak.Get())
|
||||
{
|
||||
Actor->Destroy();
|
||||
}
|
||||
}
|
||||
SpawnedActors.Empty();
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: All spawned actors destroyed"));
|
||||
}
|
||||
|
||||
TArray<FString> UPS_Editor_SpawnManager::GetCategories() const
|
||||
{
|
||||
TArray<FString> Categories;
|
||||
|
||||
@@ -45,6 +45,15 @@ public:
|
||||
/** Spawn the actor at a specific world location. */
|
||||
AActor* SpawnFromCatalogAtLocation(int32 Index, FVector WorldLocation);
|
||||
|
||||
/** Spawn an actor directly from a class (used by scene loading). No undo recorded. */
|
||||
AActor* SpawnActorDirect(UClass* ActorClass, FVector Location, FRotator Rotation, FVector Scale);
|
||||
|
||||
/** Destroy all spawned actors (for scene clear/load). */
|
||||
void DestroyAllSpawnedActors();
|
||||
|
||||
/** Get all currently spawned actors. */
|
||||
const TArray<TWeakObjectPtr<AActor>>& GetSpawnedActors() const { return SpawnedActors; }
|
||||
|
||||
/** Get the resolved catalog entries for UI display. */
|
||||
const TArray<FPS_Editor_ResolvedEntry>& GetResolvedEntries() const { return ResolvedEntries; }
|
||||
|
||||
@@ -61,6 +70,10 @@ private:
|
||||
UPROPERTY()
|
||||
TArray<FPS_Editor_ResolvedEntry> ResolvedEntries;
|
||||
|
||||
/** All actors spawned by this manager (tracked for save/load). */
|
||||
UPROPERTY()
|
||||
TArray<TWeakObjectPtr<AActor>> SpawnedActors;
|
||||
|
||||
/** Resolve all catalog entries (load classes, read SpawnableComponent metadata). */
|
||||
void ResolveCatalog();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ void APS_Editor_HUD::BeginPlay()
|
||||
{
|
||||
MainWidget->SetSelectionManager(EditorPC->GetSelectionManager());
|
||||
MainWidget->SetSpawnManager(EditorPC->GetSpawnManager());
|
||||
MainWidget->SetSceneSerializer(EditorPC->GetSceneSerializer());
|
||||
}
|
||||
|
||||
MainWidget->AddToViewport(0);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "PS_Editor_MainWidget.h"
|
||||
#include "PS_Editor_SelectionManager.h"
|
||||
#include "PS_Editor_SpawnManager.h"
|
||||
#include "PS_Editor_SceneSerializer.h"
|
||||
#include "PS_Editor_UndoManager.h"
|
||||
#include "PS_Editor_PlayerController.h"
|
||||
#include "Widgets/SBoxPanel.h"
|
||||
@@ -23,6 +24,11 @@ void UPS_Editor_MainWidget::SetSpawnManager(UPS_Editor_SpawnManager* InSpawnMana
|
||||
SpawnManager = InSpawnManager;
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::SetSceneSerializer(UPS_Editor_SceneSerializer* InSerializer)
|
||||
{
|
||||
SceneSerializer = InSerializer;
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
||||
{
|
||||
return SNew(SOverlay)
|
||||
@@ -70,6 +76,13 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
||||
[
|
||||
BuildToolbar()
|
||||
]
|
||||
// Scene save/load
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 4.0f, 0.0f, 0.0f)
|
||||
[
|
||||
BuildSceneButtons()
|
||||
]
|
||||
]
|
||||
// Left panel: Spawn Catalogue
|
||||
+ SOverlay::Slot()
|
||||
@@ -258,6 +271,88 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildPropertiesPanel()
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
{
|
||||
return SNew(SBorder)
|
||||
.BorderBackgroundColor(FLinearColor(0.05f, 0.05f, 0.05f, 0.85f))
|
||||
.Padding(FMargin(8.0f, 4.0f))
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
// Save row: text field + save button
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 2.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
|
||||
[
|
||||
SAssignNew(SaveNameField, SEditableTextBox)
|
||||
.HintText(FText::FromString(TEXT("Scene name...")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
if (SaveNameField.IsValid() && SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
{
|
||||
FString Name = SaveNameField->GetText().ToString();
|
||||
if (!Name.IsEmpty())
|
||||
{
|
||||
SceneSerializer->SaveScene(Name, SpawnManager.Get());
|
||||
// Refresh scene list
|
||||
PopulateSceneList();
|
||||
}
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Save")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
]
|
||||
]
|
||||
// Action buttons: New + Load list
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 2.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
if (SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
{
|
||||
SceneSerializer->ClearScene(SpawnManager.Get());
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("New Scene")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
]
|
||||
]
|
||||
// Saved scenes list
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 4.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SAssignNew(SceneListContainer, SVerticalBox)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSpawnCatalog()
|
||||
{
|
||||
TSharedRef<SVerticalBox> CatalogList = SNew(SVerticalBox);
|
||||
@@ -302,6 +397,67 @@ void UPS_Editor_MainWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
PopulateSpawnCatalog();
|
||||
PopulateSceneList();
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::PopulateSceneList()
|
||||
{
|
||||
if (!SceneListContainer.IsValid()) return;
|
||||
SceneListContainer->ClearChildren();
|
||||
|
||||
UPS_Editor_SceneSerializer* Ser = SceneSerializer.Get();
|
||||
if (!Ser) return;
|
||||
|
||||
TArray<FString> Scenes = Ser->GetSavedSceneNames();
|
||||
if (Scenes.Num() == 0)
|
||||
{
|
||||
SceneListContainer->AddSlot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("No saved scenes")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Italic", 9))
|
||||
.ColorAndOpacity(FLinearColor(0.5f, 0.5f, 0.5f))
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
SceneListContainer->AddSlot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Load scene:")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 9))
|
||||
.ColorAndOpacity(FLinearColor(0.6f, 0.6f, 0.6f))
|
||||
];
|
||||
|
||||
for (const FString& SceneName : Scenes)
|
||||
{
|
||||
FString Name = SceneName;
|
||||
SceneListContainer->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 1.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
.OnClicked_Lambda([this, Name]() -> FReply
|
||||
{
|
||||
if (SceneSerializer.IsValid() && SpawnManager.IsValid())
|
||||
{
|
||||
SceneSerializer->LoadScene(Name, SpawnManager.Get());
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
SaveNameField->SetText(FText::FromString(Name));
|
||||
}
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(SceneName))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 10))
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::PopulateSpawnCatalog()
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
class UPS_Editor_SelectionManager;
|
||||
class UPS_Editor_SpawnManager;
|
||||
class UPS_Editor_SceneSerializer;
|
||||
|
||||
/**
|
||||
* Main editor widget for PS_Editor.
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
/** Set manager references. Must be called after construction. */
|
||||
void SetSelectionManager(UPS_Editor_SelectionManager* InSelectionManager);
|
||||
void SetSpawnManager(UPS_Editor_SpawnManager* InSpawnManager);
|
||||
void SetSceneSerializer(UPS_Editor_SceneSerializer* InSerializer);
|
||||
|
||||
protected:
|
||||
virtual TSharedRef<SWidget> RebuildWidget() override;
|
||||
@@ -35,12 +37,19 @@ private:
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<UPS_Editor_SpawnManager> SpawnManager;
|
||||
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<UPS_Editor_SceneSerializer> SceneSerializer;
|
||||
|
||||
// ---- Slate widget refs for dynamic updates ----
|
||||
TSharedPtr<STextBlock> ModeText;
|
||||
TSharedPtr<STextBlock> SelectionInfoText;
|
||||
|
||||
// Spawn catalog
|
||||
TSharedPtr<SVerticalBox> SpawnListContainer;
|
||||
|
||||
// Scene save/load
|
||||
TSharedPtr<SEditableTextBox> SaveNameField;
|
||||
TSharedPtr<SVerticalBox> SceneListContainer;
|
||||
TArray<TSharedPtr<FSlateBrush>> CachedBrushes;
|
||||
|
||||
// Transform fields
|
||||
@@ -52,6 +61,8 @@ private:
|
||||
TSharedRef<SWidget> BuildToolbar();
|
||||
TSharedRef<SWidget> BuildPropertiesPanel();
|
||||
TSharedRef<SWidget> BuildSpawnCatalog();
|
||||
TSharedRef<SWidget> BuildSceneButtons();
|
||||
void PopulateSceneList();
|
||||
void PopulateSpawnCatalog();
|
||||
void RefreshPropertiesFromSelection();
|
||||
void ApplyTransformFromUI();
|
||||
|
||||
Reference in New Issue
Block a user