Add LoadSceneAdditive / UnloadSceneAdditive for briefing scene preload
Lets a briefing scene materialize only the preload-tagged actors from a scenario .pss (player placement markers, briefing props) without clearing the current world, pushing timeline data, registering ActorIds, or starting any auto-play. The actors spawned by each LoadSceneAdditive call are tracked in a static TWeakObjectPtr array; UnloadSceneAdditive destroys every still-valid one and clears the array. Typical BP usage on scenario change: UnloadSceneAdditive(this) -> LoadSceneAdditive(this, NewSceneName) No BP-side bookkeeping required. Filter defaults to PreloadOnly which matches the intended briefing use case; the parameter stays exposed for other niche cases (ExcludePreload, All). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,102 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<TWeakObjectPtr<AActor>> UPS_Editor_SceneLoader::AdditiveSpawnedActors;
|
||||
|
||||
bool UPS_Editor_SceneLoader::LoadSceneAdditive(const UObject* WorldContextObject, const FString& SceneName, EPS_Editor_SceneLoadFilter Filter, bool bStripEditorComponents)
|
||||
{
|
||||
if (!WorldContextObject || SceneName.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
if (!World)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read + parse the scenario JSON exactly like LoadScene
|
||||
const FString FilePath = GetSceneFilePath(SceneName);
|
||||
FString JsonString;
|
||||
if (!FFileHelper::LoadFileToString(JsonString, *FilePath))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("PS_Editor SceneLoader (Additive): Failed to read file: %s"), *FilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
FPS_Editor_SceneData SceneData;
|
||||
if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &SceneData, 0, 0))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("PS_Editor SceneLoader (Additive): Failed to parse JSON from: %s"), *FilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn matching actors WITHOUT touching the timeline subsystem, registering ActorIds,
|
||||
// or starting any auto-play. The briefing scene drives its own timeline.
|
||||
int32 SpawnedCount = 0;
|
||||
bIsCurrentlyLoading = true;
|
||||
for (const FPS_Editor_ActorData& ActorData : SceneData.Actors)
|
||||
{
|
||||
AActor* SpawnedActor = SpawnActorFromData(World, ActorData, Filter);
|
||||
if (SpawnedActor)
|
||||
{
|
||||
AdditiveSpawnedActors.Add(SpawnedActor);
|
||||
|
||||
if (bStripEditorComponents)
|
||||
{
|
||||
TArray<UActorComponent*> ToRemove;
|
||||
if (UActorComponent* Spawnable = SpawnedActor->FindComponentByClass<UPS_Editor_SpawnableComponent>())
|
||||
{
|
||||
ToRemove.Add(Spawnable);
|
||||
}
|
||||
if (UActorComponent* Editable = SpawnedActor->FindComponentByClass<UPS_Editor_EditableComponent>())
|
||||
{
|
||||
ToRemove.Add(Editable);
|
||||
}
|
||||
for (UActorComponent* Comp : ToRemove)
|
||||
{
|
||||
Comp->DestroyComponent();
|
||||
}
|
||||
|
||||
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(SpawnedActor))
|
||||
{
|
||||
Spline->SetVisualizationVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
SpawnedCount++;
|
||||
}
|
||||
}
|
||||
bIsCurrentlyLoading = false;
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor SceneLoader (Additive): Spawned %d actor(s) from '%s' [filter=%d]"),
|
||||
SpawnedCount, *SceneName, (int32)Filter);
|
||||
|
||||
return SpawnedCount > 0;
|
||||
}
|
||||
|
||||
int32 UPS_Editor_SceneLoader::UnloadSceneAdditive(const UObject* WorldContextObject)
|
||||
{
|
||||
int32 Destroyed = 0;
|
||||
for (TWeakObjectPtr<AActor>& Weak : AdditiveSpawnedActors)
|
||||
{
|
||||
if (AActor* Actor = Weak.Get())
|
||||
{
|
||||
Actor->Destroy();
|
||||
++Destroyed;
|
||||
}
|
||||
// else: already gone (GC, manual destroy, level reload) — skip silently
|
||||
}
|
||||
AdditiveSpawnedActors.Reset();
|
||||
|
||||
if (Destroyed > 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor SceneLoader (Additive): Unloaded %d actor(s)"), Destroyed);
|
||||
}
|
||||
return Destroyed;
|
||||
}
|
||||
|
||||
bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors, EPS_Editor_SceneLoadFilter Filter)
|
||||
{
|
||||
if (!World)
|
||||
|
||||
@@ -56,6 +56,33 @@ public:
|
||||
static bool LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors,
|
||||
EPS_Editor_SceneLoadFilter Filter = EPS_Editor_SceneLoadFilter::All);
|
||||
|
||||
/**
|
||||
* Additive load: spawns actors from a scenario WITHOUT clearing the current world and
|
||||
* WITHOUT touching the timeline subsystem. Designed for briefing scenes where you only
|
||||
* want to materialize preload-tagged actors (player placement markers, briefing props)
|
||||
* configured in the main scenario.
|
||||
*
|
||||
* Spawned actors are tracked internally — call UnloadSceneAdditive() to destroy them all
|
||||
* before loading the next scenario.
|
||||
*
|
||||
* @param SceneName Scenario name (without extension)
|
||||
* @param Filter Which actors to spawn — defaults to PreloadOnly
|
||||
* @param bStripEditorComponents If true, removes SpawnableComponent / EditableComponent after spawn
|
||||
* @return True if at least one actor was spawned
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
|
||||
static bool LoadSceneAdditive(const UObject* WorldContextObject, const FString& SceneName,
|
||||
EPS_Editor_SceneLoadFilter Filter = EPS_Editor_SceneLoadFilter::PreloadOnly,
|
||||
bool bStripEditorComponents = true);
|
||||
|
||||
/**
|
||||
* Destroy every actor previously spawned via LoadSceneAdditive. No-op if nothing was loaded.
|
||||
* Call this before LoadSceneAdditive when switching briefing scenarios.
|
||||
* @return Number of actors destroyed.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
|
||||
static int32 UnloadSceneAdditive(const UObject* WorldContextObject);
|
||||
|
||||
/**
|
||||
* Get the directory where scenes are stored.
|
||||
*/
|
||||
@@ -114,4 +141,8 @@ private:
|
||||
|
||||
/** Apply custom editable properties to a spawned actor. */
|
||||
static void ApplyCustomProperties(AActor* Actor, const TMap<FString, FString>& CustomProperties);
|
||||
|
||||
/** Actors spawned via LoadSceneAdditive, tracked so UnloadSceneAdditive can find them.
|
||||
* TWeakObjectPtr handles GC / manual destruction gracefully (skipped at unload time). */
|
||||
static TArray<TWeakObjectPtr<AActor>> AdditiveSpawnedActors;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user