Compare commits
3 Commits
ac91e4e8f9
...
4a98c6402b
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a98c6402b | |||
| f6efc00c73 | |||
| 34280de44a |
@@ -1,4 +1,4 @@
|
||||
- [PS_Editor plugin project](project_ps_editor.md) — Runtime editor plugin for UE 5.5, Phases 1-4 done, outline material pending
|
||||
- [PS_Editor plugin project](project_ps_editor.md) — Runtime editor plugin for UE 5.7 (migrated from 5.5 in April 2026), Phases 1-4 done, outline material pending
|
||||
- [Underscore naming](feedback_naming.md) — Always use PS_Editor_ prefix with underscores in file/class names
|
||||
- [UE5 build process](reference_build.md) — CLI build command for the project (Build.bat path, target name, flags)
|
||||
- [UE5 build process](reference_build.md) — CLI build command for PROSERVE_UE_5_7 (5.5 host deprecated), Build.bat path, target name, junction check
|
||||
- [Build workflow](feedback_build_workflow.md) — Prefer Live Coding (Ctrl+Alt+F11) when UE is open, CLI build only when UE is closed
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: PS_Editor plugin project
|
||||
description: Runtime editor plugin for UE 5.5 - object placement, scene editing, JSON save/load in packaged builds
|
||||
description: Runtime editor plugin for UE 5.7 - object placement, scene editing, JSON save/load in packaged builds
|
||||
type: project
|
||||
---
|
||||
|
||||
PS_Editor is a runtime editor plugin for Unreal Engine 5.5 for the PROSERVE project.
|
||||
PS_Editor is a runtime editor plugin for Unreal Engine 5.7 for the PROSERVE project. The plugin was started on UE 5.5; the host project migrated to UE 5.7 in April 2026 and the plugin follows. Build details in reference_build.md.
|
||||
|
||||
Key decisions:
|
||||
- UI: UMG driven by C++ (Slate)
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
---
|
||||
name: UE5 build process
|
||||
description: How to build the PS_ProserveEditor UE 5.5 project from command line on Windows
|
||||
description: How to build the PROSERVE host project (currently UE 5.7) from command line on Windows
|
||||
type: reference
|
||||
---
|
||||
|
||||
UE 5.5 is installed at `C:\Program Files\Epic Games\UE_5.5` (found via registry key `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.5`).
|
||||
The PS_Editor plugin is developed in `E:\ASTERION\GIT\PS_Editor\Unreal\Plugins\PS_Editor` and consumed by the PROSERVE host project via an NTFS junction. Always build the host project, not the plugin in isolation.
|
||||
|
||||
**Current host project (April 2026):** `E:\ASTERION\SVN\DEV\PROSERVE_UE_5_7\PROSERVE_UE_5_7.uproject` (Unreal Engine 5.7)
|
||||
|
||||
The previous 5.5 host (`PROSERVE_UE_5_5`) is deprecated — do not use unless explicitly asked.
|
||||
|
||||
UE 5.7 is installed at `C:\Program Files\Epic Games\UE_5.7` (found via registry key `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.7`).
|
||||
|
||||
Build command (Editor target, Development):
|
||||
```
|
||||
"C:/Program Files/Epic Games/UE_5.5/Engine/Build/BatchFiles/Build.bat" PS_ProserveEditorEditor Win64 Development -Project="C:/ASTERION/GIT/PS_ProserveEditor/Unreal/PS_ProserveEditor.uproject" -WaitMutex -FromMsBuild
|
||||
"C:/Program Files/Epic Games/UE_5.7/Engine/Build/BatchFiles/Build.bat" PROSERVE_UE_5_7Editor Win64 Development -Project="E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/PROSERVE_UE_5_7.uproject" -WaitMutex -FromMsBuild
|
||||
```
|
||||
|
||||
**How to apply:** Use this command to compile after code changes. Timeout should be set to 600000ms (10 min) for safety. The target name is `PS_ProserveEditorEditor` (Editor suffix) for editor builds.
|
||||
**How to apply:** Use this command to compile after code changes. Timeout should be set to 600000ms (10 min) for safety. The target name is `PROSERVE_UE_5_7Editor` (Editor suffix) for editor builds.
|
||||
|
||||
**When to use CLI vs Live Coding:**
|
||||
- Live Coding (Ctrl+Alt+F11): .cpp only changes (no new UCLASS, USTRUCT, UENUM, UPROPERTY in headers)
|
||||
- Full CLI build (UE must be closed): new files, header changes with new reflected types, Build.cs changes
|
||||
|
||||
**Plugin junction verification:**
|
||||
```
|
||||
ls -la E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/Plugins/ | grep PS_Editor
|
||||
```
|
||||
Should show a symlink pointing to `/e/ASTERION/GIT/PS_Editor/Unreal/Plugins/PS_Editor`. If broken, the host project will build against a stale or missing plugin copy.
|
||||
|
||||
14
CLAUDE.md
14
CLAUDE.md
@@ -1,7 +1,7 @@
|
||||
# PS_ProserveEditor - Project Guidelines
|
||||
|
||||
## Project Overview
|
||||
Unreal Engine 5.5 project with runtime editor plugin (PS_Editor). The plugin enables runtime scene editing in packaged builds: object placement, property editing, scenario definition, splines, lighting, and JSON scene save/load.
|
||||
Unreal Engine 5.7 project with runtime editor plugin (PS_Editor). The plugin enables runtime scene editing in packaged builds: object placement, property editing, scenario definition, splines, lighting, and JSON scene save/load. The plugin was started on UE 5.5; the host project migrated to UE 5.7 in April 2026.
|
||||
|
||||
## Architecture
|
||||
- **Plugin**: PS_Editor (runtime plugin, must work in packaged builds)
|
||||
@@ -118,14 +118,16 @@ Symptom that the wrong widget was edited: logs you added don't appear in the Out
|
||||
and wait. Don't pre-emptively write + apply the code change in the same turn.
|
||||
|
||||
## Build
|
||||
- Engine: Unreal Engine 5.5
|
||||
- Plugin location: `Unreal/Plugins/PS_Editor/`
|
||||
- Game module: `PS_ProserveEditor` (in `Unreal/Source/PS_ProserveEditor/`)
|
||||
- Engine: Unreal Engine 5.7 (host project migrated from 5.5 in April 2026)
|
||||
- Plugin location: `Unreal/Plugins/PS_Editor/` (consumed via NTFS junction from the PROSERVE host project's `Plugins/` directory)
|
||||
- Host project: `E:\ASTERION\SVN\DEV\PROSERVE_UE_5_7\PROSERVE_UE_5_7.uproject` — always build this, not the plugin in isolation
|
||||
- Game module: `PROSERVE_UE_5_7` (in the host project's `Source/`)
|
||||
- CLI build command (Editor, Development):
|
||||
```
|
||||
"<UE_INSTALL>/Engine/Build/BatchFiles/Build.bat" PS_ProserveEditorEditor Win64 Development -Project="<REPO>/Unreal/PS_ProserveEditor.uproject" -WaitMutex -FromMsBuild
|
||||
"C:/Program Files/Epic Games/UE_5.7/Engine/Build/BatchFiles/Build.bat" PROSERVE_UE_5_7Editor Win64 Development -Project="E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/PROSERVE_UE_5_7.uproject" -WaitMutex -FromMsBuild
|
||||
```
|
||||
UE install path can be found via registry: `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.5` > `InstalledDirectory`
|
||||
UE install path can be found via registry: `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.7` > `InstalledDirectory`
|
||||
- The 5.5 host (`PROSERVE_UE_5_5`) is deprecated — do not build against it unless explicitly asked.
|
||||
|
||||
## Project Memory
|
||||
Shared project memory is stored in `.claude/memory/` (versioned in the repository). When starting a new session on any machine, read these files to restore context:
|
||||
|
||||
@@ -138,8 +138,8 @@ void APS_Editor_PlayerController::BeginPlay()
|
||||
|
||||
if (!Sub->PendingScenarioName.IsEmpty() && SpawnManager && SceneSerializer)
|
||||
{
|
||||
bool bLoaded = SceneSerializer->LoadScene(Sub->PendingScenarioName, SpawnManager);
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: LoadScene('%s') = %s"),
|
||||
bool bLoaded = SceneSerializer->LoadScenario(Sub->PendingScenarioName, SpawnManager);
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: LoadScenario('%s') = %s"),
|
||||
*Sub->PendingScenarioName, bLoaded ? TEXT("OK") : TEXT("FAILED"));
|
||||
}
|
||||
Sub->PendingScenarioName.Empty();
|
||||
|
||||
@@ -49,11 +49,13 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors, bool bStripEditorComponents, EPS_Editor_SceneLoadFilter Filter)
|
||||
TArray<TWeakObjectPtr<AActor>> UPS_Editor_SceneLoader::TrackedSpawnedActors;
|
||||
|
||||
bool UPS_Editor_SceneLoader::LoadScenario(const UObject* WorldContextObject, const FString& ScenarioName, TArray<AActor*>& OutActors, bool bStripEditorComponents, EPS_Editor_SceneLoadFilter Filter)
|
||||
{
|
||||
OutActors.Empty();
|
||||
|
||||
if (!WorldContextObject || SceneName.IsEmpty())
|
||||
if (!WorldContextObject || ScenarioName.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -65,7 +67,7 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
||||
}
|
||||
|
||||
// Read JSON file
|
||||
const FString FilePath = GetSceneFilePath(SceneName);
|
||||
const FString FilePath = GetSceneFilePath(ScenarioName);
|
||||
FString JsonString;
|
||||
if (!FFileHelper::LoadFileToString(JsonString, *FilePath))
|
||||
{
|
||||
@@ -81,11 +83,22 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadSceneFromData(World, SceneData, OutActors, Filter))
|
||||
if (!LoadScenarioFromData(World, SceneData, OutActors, Filter))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Track every spawned actor so UnloadScenario() can dispose of them later — this is the
|
||||
// briefing-flow hook (load preload markers, then unload on scenario change). Gameplay
|
||||
// callers that never unload don't pay a price beyond a small TWeakObjectPtr array.
|
||||
for (AActor* Actor : OutActors)
|
||||
{
|
||||
if (Actor)
|
||||
{
|
||||
TrackedSpawnedActors.Add(Actor);
|
||||
}
|
||||
}
|
||||
|
||||
// Strip editor-only components for clean gameplay
|
||||
if (bStripEditorComponents)
|
||||
{
|
||||
@@ -94,11 +107,6 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
||||
if (!Actor) continue;
|
||||
|
||||
TArray<UActorComponent*> ToRemove;
|
||||
for (UActorComponent* Comp : Actor->GetComponentsByInterface(UPS_Editor_PointPlaceable::StaticClass()))
|
||||
{
|
||||
// Don't remove — PointPlaceable is on the actor itself, not a component
|
||||
}
|
||||
|
||||
if (UActorComponent* Spawnable = Actor->FindComponentByClass<UPS_Editor_SpawnableComponent>())
|
||||
{
|
||||
ToRemove.Add(Spawnable);
|
||||
@@ -124,7 +132,28 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors, EPS_Editor_SceneLoadFilter Filter)
|
||||
int32 UPS_Editor_SceneLoader::UnloadScenario(const UObject* WorldContextObject)
|
||||
{
|
||||
int32 Destroyed = 0;
|
||||
for (TWeakObjectPtr<AActor>& Weak : TrackedSpawnedActors)
|
||||
{
|
||||
if (AActor* Actor = Weak.Get())
|
||||
{
|
||||
Actor->Destroy();
|
||||
++Destroyed;
|
||||
}
|
||||
// else: already gone (GC, manual destroy, level reload) — skip silently
|
||||
}
|
||||
TrackedSpawnedActors.Reset();
|
||||
|
||||
if (Destroyed > 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor SceneLoader: UnloadScenario destroyed %d actor(s)"), Destroyed);
|
||||
}
|
||||
return Destroyed;
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneLoader::LoadScenarioFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors, EPS_Editor_SceneLoadFilter Filter)
|
||||
{
|
||||
if (!World)
|
||||
{
|
||||
@@ -144,7 +173,13 @@ bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_S
|
||||
// Static nav is preserved on disk for scenarios that don't need this.
|
||||
PS_Editor_NavUtils::SetDynamicNavigationEnabled(World, SceneData.bUseDynamicNavigation);
|
||||
|
||||
UPS_Editor_TimelineSubsystem* TimelineSS = World->GetSubsystem<UPS_Editor_TimelineSubsystem>();
|
||||
// PreloadOnly is the briefing path: only player-placement markers / briefing props
|
||||
// should spawn, and the briefing scene runs its OWN timeline (don't push the scenario's
|
||||
// timeline data or auto-play it). Skip the timeline subsystem entirely in that case.
|
||||
const bool bSkipTimeline = (Filter == EPS_Editor_SceneLoadFilter::PreloadOnly);
|
||||
UPS_Editor_TimelineSubsystem* TimelineSS = bSkipTimeline
|
||||
? nullptr
|
||||
: World->GetSubsystem<UPS_Editor_TimelineSubsystem>();
|
||||
|
||||
int32 SpawnedCount = 0;
|
||||
|
||||
@@ -154,7 +189,8 @@ bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_S
|
||||
AActor* SpawnedActor = SpawnActorFromData(World, ActorData, Filter);
|
||||
if (SpawnedActor)
|
||||
{
|
||||
// Register the stable ID so timeline tracks can find this actor
|
||||
// Register the stable ID so timeline tracks can find this actor.
|
||||
// Only when timeline is active for this load (PreloadOnly skips the subsystem).
|
||||
if (TimelineSS && ActorData.ActorId.IsValid())
|
||||
{
|
||||
TimelineSS->RegisterActorId(SpawnedActor, ActorData.ActorId);
|
||||
@@ -220,8 +256,8 @@ bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_S
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor SceneLoader: Loaded %d actors from scene '%s'"),
|
||||
SpawnedCount, *SceneData.SceneName);
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor SceneLoader: Loaded %d actor(s) from scenario '%s' [filter=%d]"),
|
||||
SpawnedCount, *SceneData.SceneName, (int32)Filter);
|
||||
return SpawnedCount > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "PS_Editor_SceneLoader.generated.h"
|
||||
|
||||
/** Filter for selective scene loading. */
|
||||
/** Filter for selective scenario loading. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EPS_Editor_SceneLoadFilter : uint8
|
||||
{
|
||||
@@ -15,20 +15,27 @@ enum class EPS_Editor_SceneLoadFilter : uint8
|
||||
};
|
||||
|
||||
/**
|
||||
* Standalone scene loader for PS_Editor.
|
||||
* Loads a JSON scene file and spawns all actors with their properties and spline data.
|
||||
* Standalone scenario loader for PS_Editor.
|
||||
* A "scenario" is a .pss file describing actors + properties + timeline saved by the runtime
|
||||
* editor. This loader spawns those actors into an already-loaded UE level — it does NOT
|
||||
* change the current UE level. Spawning is additive: existing world content is preserved.
|
||||
*
|
||||
* This is the entry point for GAME MODE usage — it does NOT require
|
||||
* PS_Editor's PlayerController, SelectionManager, Gizmo, or UI.
|
||||
* Just call LoadScene() from your GameMode's BeginPlay.
|
||||
* Just call LoadScenario() from your GameMode's BeginPlay.
|
||||
*
|
||||
* Usage:
|
||||
* void AMyGameMode::BeginPlay()
|
||||
* {
|
||||
* Super::BeginPlay();
|
||||
* TArray<AActor*> SpawnedActors;
|
||||
* UPS_Editor_SceneLoader::LoadScene(GetWorld(), TEXT("MyScene"), SpawnedActors);
|
||||
* UPS_Editor_SceneLoader::LoadScenario(GetWorld(), TEXT("MyScenario"), SpawnedActors);
|
||||
* }
|
||||
*
|
||||
* Briefing usage (player placement markers only, no timeline animation):
|
||||
* UPS_Editor_SceneLoader::UnloadScenario(this);
|
||||
* UPS_Editor_SceneLoader::LoadScenario(this, TEXT("MyScenario"), Out, true,
|
||||
* EPS_Editor_SceneLoadFilter::PreloadOnly);
|
||||
*/
|
||||
UCLASS()
|
||||
class PS_EDITOR_API UPS_Editor_SceneLoader : public UBlueprintFunctionLibrary
|
||||
@@ -37,56 +44,71 @@ class PS_EDITOR_API UPS_Editor_SceneLoader : public UBlueprintFunctionLibrary
|
||||
|
||||
public:
|
||||
/**
|
||||
* Load a scene from a JSON file and spawn all actors.
|
||||
* Load a scenario from a .pss file and spawn its actors into the current world.
|
||||
*
|
||||
* @param World The world to spawn actors into.
|
||||
* @param SceneName Scene name (without .json extension).
|
||||
* @param OutActors Filled with all spawned actors.
|
||||
* The call is additive — existing world content is preserved. Spawned actors are tracked
|
||||
* internally so UnloadScenario() can clean them up; this lets you switch scenarios at
|
||||
* runtime (typical briefing flow). When Filter == PreloadOnly, the timeline subsystem is
|
||||
* NOT touched (no SetData, no auto-play, no ActorId registration) so a briefing scene's
|
||||
* own timeline keeps running untouched.
|
||||
*
|
||||
* @param ScenarioName Scenario name (without .pss extension).
|
||||
* @param OutActors Filled with all spawned actors (also tracked internally).
|
||||
* @param bStripEditorComponents If true, removes SpawnableComponent and EditableComponent after spawn (cleaner for gameplay).
|
||||
* @return True if the scene was loaded successfully.
|
||||
* @param Filter Which actors to spawn. PreloadOnly = briefing-only, skips timeline setup.
|
||||
* @return True if the scenario was loaded successfully.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
|
||||
static bool LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors,
|
||||
static bool LoadScenario(const UObject* WorldContextObject, const FString& ScenarioName, TArray<AActor*>& OutActors,
|
||||
bool bStripEditorComponents = true, EPS_Editor_SceneLoadFilter Filter = EPS_Editor_SceneLoadFilter::All);
|
||||
|
||||
/**
|
||||
* Load scene from an already-parsed SceneData struct.
|
||||
* Useful if you want to parse the JSON yourself or modify data before spawning.
|
||||
* Destroy every actor previously spawned via LoadScenario(). No-op if nothing was loaded
|
||||
* or all tracked actors have already been GC'd / manually destroyed.
|
||||
* Call this before LoadScenario() when switching scenarios.
|
||||
* @return Number of actors destroyed.
|
||||
*/
|
||||
static bool LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors,
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
|
||||
static int32 UnloadScenario(const UObject* WorldContextObject);
|
||||
|
||||
/**
|
||||
* Load scenario actors from an already-parsed SceneData struct.
|
||||
* Useful if you want to parse the .pss JSON yourself or modify data before spawning.
|
||||
*/
|
||||
static bool LoadScenarioFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors,
|
||||
EPS_Editor_SceneLoadFilter Filter = EPS_Editor_SceneLoadFilter::All);
|
||||
|
||||
/**
|
||||
* Get the directory where scenes are stored.
|
||||
* Get the directory where scenarios are stored.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
static FString GetScenesDirectory();
|
||||
|
||||
/**
|
||||
* Get the full file path for a scene name.
|
||||
* Get the full file path for a scenario name.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
static FString GetSceneFilePath(const FString& SceneName);
|
||||
|
||||
/** Get the full file path for a scene name and its base level. Uses level-specific subdirectory. */
|
||||
/** Get the full file path for a scenario name and its base level. Uses level-specific subdirectory. */
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
static FString GetSceneFilePathForLevel(const FString& SceneName, const FString& LevelName);
|
||||
|
||||
/**
|
||||
* Get all saved scene names. Optionally filter by level name.
|
||||
* @param LevelFilter If not empty, only return scenes associated with this level.
|
||||
* Get all saved scenario names. Optionally filter by level name.
|
||||
* @param LevelFilter If not empty, only return scenarios associated with this level.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
static TArray<FString> GetSavedSceneNames(const FString& LevelFilter = TEXT(""));
|
||||
|
||||
/**
|
||||
* Get the level name associated with a saved scene (reads the JSON header without spawning).
|
||||
* Get the level name associated with a saved scenario (reads the JSON header without spawning).
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
static FString GetSceneLevelName(const FString& SceneName);
|
||||
|
||||
/**
|
||||
* Returns true while a scene is being loaded (actors are being spawned from JSON).
|
||||
* Returns true while a scenario is being loaded (actors are being spawned from JSON).
|
||||
* Use in BeginPlay to skip default initialization — wait for OnPropertiesLoaded instead.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
|
||||
@@ -103,7 +125,7 @@ public:
|
||||
static bool IsInEditorMode() { return bIsEditorModeActive; }
|
||||
|
||||
/**
|
||||
* Get all saved scenes for the current level.
|
||||
* Get all saved scenarios for the current level.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
|
||||
static TArray<FString> GetSavedSceneNamesForCurrentLevel(const UObject* WorldContextObject);
|
||||
@@ -114,4 +136,8 @@ private:
|
||||
|
||||
/** Apply custom editable properties to a spawned actor. */
|
||||
static void ApplyCustomProperties(AActor* Actor, const TMap<FString, FString>& CustomProperties);
|
||||
|
||||
/** Actors spawned via LoadScenario(), tracked so UnloadScenario() can find them.
|
||||
* TWeakObjectPtr handles GC / manual destruction gracefully (skipped at unload time). */
|
||||
static TArray<TWeakObjectPtr<AActor>> TrackedSpawnedActors;
|
||||
};
|
||||
|
||||
@@ -202,7 +202,7 @@ bool UPS_Editor_SceneSerializer::SaveScene(const FString& SceneName, UPS_Editor_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager)
|
||||
bool UPS_Editor_SceneSerializer::LoadScenario(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager)
|
||||
{
|
||||
if (!SpawnManager || SceneName.IsEmpty())
|
||||
{
|
||||
@@ -523,9 +523,9 @@ void UPS_Editor_SceneSerializer::ClearScene(UPS_Editor_SpawnManager* SpawnManage
|
||||
|
||||
// NOTE: Mandatory-actor auto-spawn is intentionally NOT done here. ClearScene is called
|
||||
// from three contexts: (1) "New Scenario" button (we want mandatory spawn — handled in the
|
||||
// widget after ClearScene returns), (2) LoadScene before re-spawning saved actors (we
|
||||
// widget after ClearScene returns), (2) LoadScenario before re-spawning saved actors (we
|
||||
// don't want it — the JSON itself contains the mandatory actor, and there's a final
|
||||
// EnsureMandatoryActorsPresent at the end of LoadScene that handles legacy saves), and
|
||||
// EnsureMandatoryActorsPresent at the end of LoadScenario that handles legacy saves), and
|
||||
// (3) BaseLevel switch (we don't want it — the new baseLevel isn't loaded yet, the
|
||||
// MandatoryAnchor isn't in the world; the post-OnLevelShown hook handles that).
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ 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);
|
||||
/** Load a scenario from a .pss file. Destroys current spawned actors and re-spawns from file. */
|
||||
bool LoadScenario(const FString& SceneName, UPS_Editor_SpawnManager* SpawnManager);
|
||||
|
||||
/** Get a list of all saved scene names (without .json extension). */
|
||||
TArray<FString> GetSavedSceneNames() const;
|
||||
|
||||
@@ -1352,7 +1352,7 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
}
|
||||
}
|
||||
|
||||
SceneSerializer->LoadScene(FileName, SpawnManager.Get());
|
||||
SceneSerializer->LoadScenario(FileName, SpawnManager.Get());
|
||||
bSceneDirty = false;
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
@@ -1653,7 +1653,7 @@ void UPS_Editor_MainWidget::PopulateSceneList()
|
||||
// current sublevel as-is — don't unload it (avoids empty scenes).
|
||||
}
|
||||
|
||||
SceneSerializer->LoadScene(Name, SpawnManager.Get());
|
||||
SceneSerializer->LoadScenario(Name, SpawnManager.Get());
|
||||
bSceneDirty = false;
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
|
||||
@@ -1192,7 +1192,7 @@ TSharedRef<SWidget> UPS_Editor_MainWidget_Legacy::BuildSceneButtons()
|
||||
}
|
||||
}
|
||||
|
||||
SceneSerializer->LoadScene(FileName, SpawnManager.Get());
|
||||
SceneSerializer->LoadScenario(FileName, SpawnManager.Get());
|
||||
bSceneDirty = false;
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
@@ -1487,7 +1487,7 @@ void UPS_Editor_MainWidget_Legacy::PopulateSceneList()
|
||||
// current sublevel as-is — don't unload it (avoids empty scenes).
|
||||
}
|
||||
|
||||
SceneSerializer->LoadScene(Name, SpawnManager.Get());
|
||||
SceneSerializer->LoadScenario(Name, SpawnManager.Get());
|
||||
bSceneDirty = false;
|
||||
if (SaveNameField.IsValid())
|
||||
{
|
||||
|
||||
@@ -130,7 +130,7 @@ bool UPS_Editor_MainWidget_UMG::LoadScenario(const FString& Name)
|
||||
}
|
||||
if (!SM) SM = SpawnManager.Get();
|
||||
if (!Ser || !SM || Name.IsEmpty()) return false;
|
||||
Ser->LoadScene(Name, SM);
|
||||
Ser->LoadScenario(Name, SM);
|
||||
OnSceneDirty.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user