Add standalone SceneLoader for game runtime initialization
UPS_Editor_SceneLoader is a BlueprintFunctionLibrary that loads PS_Editor JSON scenes without any editor infrastructure (no PlayerController, Gizmo, SelectionManager, or UI required). Usage from any GameMode: TArray<AActor*> Actors; UPS_Editor_SceneLoader::LoadScene(this, "MyScene", Actors); Handles: class loading (BP + C++), transforms, custom editable properties via reflection (ImportText), and spline point data. All functions are BlueprintCallable for BP GameMode usage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,187 @@
|
|||||||
|
#include "PS_Editor_SceneLoader.h"
|
||||||
|
#include "PS_Editor_SceneData.h"
|
||||||
|
#include "PS_Editor_EditableComponent.h"
|
||||||
|
#include "PS_Editor_SplineActor.h"
|
||||||
|
#include "JsonObjectConverter.h"
|
||||||
|
#include "Misc/FileHelper.h"
|
||||||
|
#include "Misc/Paths.h"
|
||||||
|
#include "UObject/UnrealType.h"
|
||||||
|
#include "Components/ActorComponent.h"
|
||||||
|
|
||||||
|
bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors)
|
||||||
|
{
|
||||||
|
OutActors.Empty();
|
||||||
|
|
||||||
|
if (!WorldContextObject || SceneName.IsEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||||
|
if (!World)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read JSON file
|
||||||
|
const FString FilePath = GetSceneFilePath(SceneName);
|
||||||
|
FString JsonString;
|
||||||
|
if (!FFileHelper::LoadFileToString(JsonString, *FilePath))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("PS_Editor SceneLoader: Failed to read file: %s"), *FilePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON to struct
|
||||||
|
FPS_Editor_SceneData SceneData;
|
||||||
|
if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &SceneData, 0, 0))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("PS_Editor SceneLoader: Failed to parse JSON from: %s"), *FilePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadSceneFromData(World, SceneData, OutActors);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors)
|
||||||
|
{
|
||||||
|
if (!World)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 SpawnedCount = 0;
|
||||||
|
|
||||||
|
for (const FPS_Editor_ActorData& ActorData : SceneData.Actors)
|
||||||
|
{
|
||||||
|
AActor* SpawnedActor = SpawnActorFromData(World, ActorData);
|
||||||
|
if (SpawnedActor)
|
||||||
|
{
|
||||||
|
OutActors.Add(SpawnedActor);
|
||||||
|
SpawnedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("PS_Editor SceneLoader: Loaded %d actors from scene '%s'"),
|
||||||
|
SpawnedCount, *SceneData.SceneName);
|
||||||
|
return SpawnedCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AActor* UPS_Editor_SceneLoader::SpawnActorFromData(UWorld* World, const FPS_Editor_ActorData& ActorData)
|
||||||
|
{
|
||||||
|
// Load class (supports both Blueprint and C++ classes)
|
||||||
|
UClass* LoadedClass = LoadObject<UClass>(nullptr, *ActorData.ClassPath);
|
||||||
|
if (!LoadedClass)
|
||||||
|
{
|
||||||
|
LoadedClass = FindObject<UClass>(nullptr, *ActorData.ClassPath);
|
||||||
|
}
|
||||||
|
if (!LoadedClass)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PS_Editor SceneLoader: Failed to load class: %s"), *ActorData.ClassPath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn actor
|
||||||
|
FActorSpawnParameters SpawnParams;
|
||||||
|
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
|
||||||
|
|
||||||
|
AActor* SpawnedActor = World->SpawnActor<AActor>(LoadedClass, ActorData.Location, ActorData.Rotation, SpawnParams);
|
||||||
|
if (!SpawnedActor)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PS_Editor SceneLoader: Failed to spawn: %s"), *ActorData.ClassPath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply scale and ensure movable
|
||||||
|
SpawnedActor->SetActorScale3D(ActorData.Scale);
|
||||||
|
if (USceneComponent* Root = SpawnedActor->GetRootComponent())
|
||||||
|
{
|
||||||
|
Root->SetMobility(EComponentMobility::Movable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply custom editable properties
|
||||||
|
if (ActorData.CustomProperties.Num() > 0)
|
||||||
|
{
|
||||||
|
ApplyCustomProperties(SpawnedActor, ActorData.CustomProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import spline data if applicable
|
||||||
|
if (APS_Editor_SplineActor* Spline = Cast<APS_Editor_SplineActor>(SpawnedActor))
|
||||||
|
{
|
||||||
|
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SpawnedActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPS_Editor_SceneLoader::ApplyCustomProperties(AActor* Actor, const TMap<FString, FString>& CustomProperties)
|
||||||
|
{
|
||||||
|
UPS_Editor_EditableComponent* EditComp = Actor->FindComponentByClass<UPS_Editor_EditableComponent>();
|
||||||
|
if (!EditComp) return;
|
||||||
|
|
||||||
|
for (const FName& Path : EditComp->EditableProperties)
|
||||||
|
{
|
||||||
|
FString CompName, PropName;
|
||||||
|
UPS_Editor_EditableComponent::ParsePropertyPath(Path, CompName, PropName);
|
||||||
|
const FString Key = Path.ToString();
|
||||||
|
|
||||||
|
const FString* ValueStr = CustomProperties.Find(Key);
|
||||||
|
if (!ValueStr) continue;
|
||||||
|
|
||||||
|
// Find target object (actor or component)
|
||||||
|
UObject* Target = CompName.IsEmpty() ? static_cast<UObject*>(Actor) : nullptr;
|
||||||
|
if (!Target)
|
||||||
|
{
|
||||||
|
TArray<UActorComponent*> Components;
|
||||||
|
Actor->GetComponents(Components);
|
||||||
|
for (UActorComponent* Comp : Components)
|
||||||
|
{
|
||||||
|
if (Comp && Comp->GetName() == CompName)
|
||||||
|
{
|
||||||
|
Target = Comp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Target) continue;
|
||||||
|
|
||||||
|
// Set property value via reflection
|
||||||
|
FProperty* Prop = FindFProperty<FProperty>(Target->GetClass(), *PropName);
|
||||||
|
if (Prop)
|
||||||
|
{
|
||||||
|
Prop->ImportText_InContainer(**ValueStr, Target, Target, PPF_None);
|
||||||
|
if (UActorComponent* AsComp = Cast<UActorComponent>(Target))
|
||||||
|
{
|
||||||
|
AsComp->MarkRenderStateDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UPS_Editor_SceneLoader::GetScenesDirectory()
|
||||||
|
{
|
||||||
|
return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("PS_Editor"), TEXT("Scenes"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UPS_Editor_SceneLoader::GetSceneFilePath(const FString& SceneName)
|
||||||
|
{
|
||||||
|
return FPaths::Combine(GetScenesDirectory(), SceneName + TEXT(".json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FString> UPS_Editor_SceneLoader::GetSavedSceneNames()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "PS_Editor_SceneData.h"
|
||||||
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
|
#include "PS_Editor_SceneLoader.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standalone scene loader for PS_Editor.
|
||||||
|
* Loads a JSON scene file and spawns all actors with their properties and spline data.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* void AMyGameMode::BeginPlay()
|
||||||
|
* {
|
||||||
|
* Super::BeginPlay();
|
||||||
|
* TArray<AActor*> SpawnedActors;
|
||||||
|
* UPS_Editor_SceneLoader::LoadScene(GetWorld(), TEXT("MyScene"), SpawnedActors);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class PS_EDITOR_API UPS_Editor_SceneLoader : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Load a scene from a JSON file and spawn all actors.
|
||||||
|
*
|
||||||
|
* @param World The world to spawn actors into.
|
||||||
|
* @param SceneName Scene name (without .json extension).
|
||||||
|
* @param OutActors Optional: filled with all spawned actors.
|
||||||
|
* @return True if the scene was loaded successfully.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "PS Editor", meta = (WorldContext = "WorldContextObject"))
|
||||||
|
static bool LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load scene from an already-parsed SceneData struct.
|
||||||
|
* Useful if you want to parse the JSON yourself or modify data before spawning.
|
||||||
|
*/
|
||||||
|
static bool LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the directory where scenes are stored.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "PS Editor")
|
||||||
|
static FString GetScenesDirectory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full file path for a scene name.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "PS Editor")
|
||||||
|
static FString GetSceneFilePath(const FString& SceneName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all saved scene names.
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "PS Editor")
|
||||||
|
static TArray<FString> GetSavedSceneNames();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Spawn a single actor from saved data and apply all properties. */
|
||||||
|
static AActor* SpawnActorFromData(UWorld* World, const FPS_Editor_ActorData& ActorData);
|
||||||
|
|
||||||
|
/** Apply custom editable properties to a spawned actor. */
|
||||||
|
static void ApplyCustomProperties(AActor* Actor, const TMap<FString, FString>& CustomProperties);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user