Add ChildMovable interface, preload filter, IsInEditorMode, lightmap init
- Add IPS_Editor_ChildMovable interface for per-child editing of sub-elements (VR placement spots, etc.) with full gizmo, undo, and serialization support - Add "Edit Children" button in toolbar when a ChildMovable actor is selected - Add bPreload tag on SpawnableComponent for briefing/placement phase actors - Add EPS_Editor_SceneLoadFilter enum (All, PreloadOnly, ExcludePreload) to LoadScene for selective loading of preload vs gameplay actors - Add bPreload field to FPS_Editor_ActorData for JSON persistence - Add static IsInEditorMode() getter on SceneLoader (BlueprintPure) - Add Level::InitializeRenderingResources() call on sublevel load for lightmaps - Update Pawn, PlayerController, UndoManager, SceneSerializer, SceneLoader, MainWidget to support ChildMovable alongside existing SplineEditable Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
#include "PS_Editor_UndoManager.h"
|
#include "PS_Editor_UndoManager.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
#include "PS_Editor_SplineEditable.h"
|
#include "PS_Editor_SplineEditable.h"
|
||||||
|
#include "PS_Editor_ChildMovable.h"
|
||||||
#include "Components/SplineComponent.h"
|
#include "Components/SplineComponent.h"
|
||||||
#include "DrawDebugHelpers.h"
|
#include "DrawDebugHelpers.h"
|
||||||
#include "PS_Editor_ConfirmDialog.h"
|
#include "PS_Editor_ConfirmDialog.h"
|
||||||
@@ -380,16 +381,21 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
}
|
}
|
||||||
const FVector ConstrainedDelta = DisplacementAlongAxis * AxisDir;
|
const FVector ConstrainedDelta = DisplacementAlongAxis * AxisDir;
|
||||||
|
|
||||||
// Spline point mode: move the single point instead of actors
|
// Spline point / ChildMovable mode: move the single sub-element instead of actors
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
|
||||||
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
|
||||||
{
|
{
|
||||||
if (SplinePointDragInitialState.IsValidIndex(ActiveSplinePointIndex))
|
if (SplinePointDragInitialState.IsValidIndex(ActiveSplinePointIndex))
|
||||||
{
|
{
|
||||||
const FVector NewPos = SplinePointDragInitialState[ActiveSplinePointIndex] + ConstrainedDelta;
|
const FVector NewPos = SplinePointDragInitialState[ActiveSplinePointIndex] + ConstrainedDelta;
|
||||||
|
|
||||||
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
|
{
|
||||||
SplineActor->MovePoint(ActiveSplinePointIndex, NewPos);
|
SplineActor->MovePoint(ActiveSplinePointIndex, NewPos);
|
||||||
}
|
}
|
||||||
|
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_MoveChild(DraggedSplineActor.Get(), ActiveSplinePointIndex, NewPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -475,11 +481,15 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
{
|
{
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
// Following a single spline point
|
// Following a single spline point or child movable element
|
||||||
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
Gizmo->SetActorLocation(SplineActor->GetPointLocation(ActiveSplinePointIndex));
|
Gizmo->SetActorLocation(SplineActor->GetPointLocation(ActiveSplinePointIndex));
|
||||||
}
|
}
|
||||||
|
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
Gizmo->SetActorLocation(IPS_Editor_ChildMovable::Execute_GetChildLocation(DraggedSplineActor.Get(), ActiveSplinePointIndex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (Selected.Num() == 1)
|
else if (Selected.Num() == 1)
|
||||||
{
|
{
|
||||||
@@ -648,6 +658,35 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for ChildMovable child handle hit
|
||||||
|
if (APS_Editor_PlayerController* EdPC_CM = Cast<APS_Editor_PlayerController>(GetController()))
|
||||||
|
{
|
||||||
|
if (EdPC_CM->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement)
|
||||||
|
{
|
||||||
|
AActor* ActiveActor = EdPC_CM->GetActivePlacementActor();
|
||||||
|
if (ActiveActor && ActiveActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
const int32 ChildIdx = IPS_Editor_ChildMovable::Execute_GetChildIndexUnderCursor(ActiveActor, RayOrigin, RayDir);
|
||||||
|
if (ChildIdx >= 0)
|
||||||
|
{
|
||||||
|
ActiveSplinePointIndex = ChildIdx;
|
||||||
|
DraggedSplineActor = ActiveActor;
|
||||||
|
IPS_Editor_ChildMovable::Execute_HighlightChild(ActiveActor, ChildIdx);
|
||||||
|
|
||||||
|
if (UPS_Editor_SelectionManager* SM = EdPC_CM->GetSelectionManager())
|
||||||
|
{
|
||||||
|
if (APS_Editor_Gizmo* Gizmo = SM->GetGizmo())
|
||||||
|
{
|
||||||
|
Gizmo->SetGizmoActive(true, IPS_Editor_ChildMovable::Execute_GetChildLocation(ActiveActor, ChildIdx));
|
||||||
|
}
|
||||||
|
SM->SetTransformMode(EPS_Editor_TransformMode::Translate);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// In SplinePlacement mode: click on spline tube mesh → insert a CP
|
// In SplinePlacement mode: click on spline tube mesh → insert a CP
|
||||||
if (APS_Editor_PlayerController* EdPC2 = Cast<APS_Editor_PlayerController>(GetController()))
|
if (APS_Editor_PlayerController* EdPC2 = Cast<APS_Editor_PlayerController>(GetController()))
|
||||||
{
|
{
|
||||||
@@ -804,13 +843,27 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
// Spline point drag: record SplinePointAction
|
// Spline point drag: record SplinePointAction
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
|
TArray<FVector> NewPoints;
|
||||||
|
FString Desc;
|
||||||
|
|
||||||
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
|
{
|
||||||
|
NewPoints = SplineActor->GetAllPointLocations();
|
||||||
|
Desc = TEXT("Move spline point");
|
||||||
|
}
|
||||||
|
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
NewPoints = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(DraggedSplineActor.Get());
|
||||||
|
Desc = TEXT("Move child element");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NewPoints.Num() > 0)
|
||||||
{
|
{
|
||||||
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
TSharedPtr<FPS_Editor_SplinePointAction> Action = MakeShared<FPS_Editor_SplinePointAction>();
|
||||||
Action->SplineActor = Cast<AActor>(SplineActor);
|
Action->SplineActor = DraggedSplineActor.Get();
|
||||||
Action->OldPoints = SplinePointDragInitialState;
|
Action->OldPoints = SplinePointDragInitialState;
|
||||||
Action->NewPoints = SplineActor->GetAllPointLocations();
|
Action->NewPoints = NewPoints;
|
||||||
Action->Description = TEXT("Move spline point");
|
Action->Description = Desc;
|
||||||
UM->RecordAction(Action);
|
UM->RecordAction(Action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1439,13 +1492,17 @@ void APS_Editor_Pawn::BeginGizmoDrag(EPS_Editor_GizmoAxis Axis)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store initial spline state if editing a point
|
// Store initial sub-element state if editing a point/child
|
||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
{
|
{
|
||||||
SplinePointDragInitialState = Spline->GetAllPointLocations();
|
SplinePointDragInitialState = Spline->GetAllPointLocations();
|
||||||
}
|
}
|
||||||
|
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
SplinePointDragInitialState = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(DraggedSplineActor.Get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCameraMode(EPS_Editor_CameraMode::GizmoDrag);
|
SetCameraMode(EPS_Editor_CameraMode::GizmoDrag);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "Engine/LevelStreamingDynamic.h"
|
#include "Engine/LevelStreamingDynamic.h"
|
||||||
#include "Engine/PostProcessVolume.h"
|
#include "Engine/PostProcessVolume.h"
|
||||||
#include "PS_Editor_CameraStart.h"
|
#include "PS_Editor_CameraStart.h"
|
||||||
|
#include "PS_Editor_ChildMovable.h"
|
||||||
|
#include "PS_Editor_SceneLoader.h"
|
||||||
#include "PS_Editor_HUD.h"
|
#include "PS_Editor_HUD.h"
|
||||||
#include "PS_Editor_MainWidget.h"
|
#include "PS_Editor_MainWidget.h"
|
||||||
#include "Camera/PlayerCameraManager.h"
|
#include "Camera/PlayerCameraManager.h"
|
||||||
@@ -32,6 +34,7 @@ void APS_Editor_PlayerController::BeginPlay()
|
|||||||
{
|
{
|
||||||
Super::BeginPlay();
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
UPS_Editor_SceneLoader::bIsEditorModeActive = true;
|
||||||
SetInputMode(FInputModeGameAndUI().SetHideCursorDuringCapture(false));
|
SetInputMode(FInputModeGameAndUI().SetHideCursorDuringCapture(false));
|
||||||
|
|
||||||
// AI logic will be stopped per-pawn when spawned (see SpawnManager)
|
// AI logic will be stopped per-pawn when spawned (see SpawnManager)
|
||||||
@@ -190,6 +193,7 @@ void APS_Editor_PlayerController::CloseEditor()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify actors: leaving editor mode
|
// Notify actors: leaving editor mode
|
||||||
|
UPS_Editor_SceneLoader::bIsEditorModeActive = false;
|
||||||
if (SpawnManager)
|
if (SpawnManager)
|
||||||
{
|
{
|
||||||
SpawnManager->NotifyEditorModeChanged(false);
|
SpawnManager->NotifyEditorModeChanged(false);
|
||||||
@@ -303,20 +307,29 @@ void APS_Editor_PlayerController::BeginPointPlacement(TSubclassOf<AActor> InActo
|
|||||||
|
|
||||||
void APS_Editor_PlayerController::BeginPointPlacementAppend(AActor* ExistingActor)
|
void APS_Editor_PlayerController::BeginPointPlacementAppend(AActor* ExistingActor)
|
||||||
{
|
{
|
||||||
if (!ExistingActor || !ExistingActor->Implements<UPS_Editor_PointPlaceable>()) return;
|
if (!ExistingActor) return;
|
||||||
|
|
||||||
|
const bool bIsPointPlaceable = ExistingActor->Implements<UPS_Editor_PointPlaceable>();
|
||||||
|
const bool bIsChildMovable = ExistingActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass());
|
||||||
|
if (!bIsPointPlaceable && !bIsChildMovable) return;
|
||||||
|
|
||||||
ActivePlacementActor = ExistingActor;
|
ActivePlacementActor = ExistingActor;
|
||||||
bAppendingToActor = true;
|
bAppendingToActor = true;
|
||||||
EditorMode = EPS_Editor_EditorMode::SplinePlacement;
|
EditorMode = EPS_Editor_EditorMode::SplinePlacement;
|
||||||
|
|
||||||
// Store initial state for undo (spline-specific)
|
// Store initial state and show handles
|
||||||
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(ExistingActor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(ExistingActor))
|
||||||
{
|
{
|
||||||
AppendInitialPoints = Spline->GetAllPointLocations();
|
AppendInitialPoints = Spline->GetAllPointLocations();
|
||||||
Spline->SetHandlesVisible(true);
|
Spline->SetHandlesVisible(true);
|
||||||
}
|
}
|
||||||
|
else if (bIsChildMovable)
|
||||||
|
{
|
||||||
|
AppendInitialPoints = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(ExistingActor);
|
||||||
|
IPS_Editor_ChildMovable::Execute_SetChildHandlesVisible(ExistingActor, true);
|
||||||
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Appending points to %s"), *ExistingActor->GetName());
|
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Editing sub-elements of %s"), *ExistingActor->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
void APS_Editor_PlayerController::FinishPointPlacement()
|
void APS_Editor_PlayerController::FinishPointPlacement()
|
||||||
@@ -332,6 +345,11 @@ void APS_Editor_PlayerController::FinishPointPlacement()
|
|||||||
Spline->SetHandlesVisible(false);
|
Spline->SetHandlesVisible(false);
|
||||||
Spline->ClearHandleHighlight();
|
Spline->ClearHandleHighlight();
|
||||||
}
|
}
|
||||||
|
else if (PlacementActor && PlacementActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_SetChildHandlesVisible(PlacementActor, false);
|
||||||
|
IPS_Editor_ChildMovable::Execute_ClearChildHighlight(PlacementActor);
|
||||||
|
}
|
||||||
|
|
||||||
if (PlacementActor && SelectionManager)
|
if (PlacementActor && SelectionManager)
|
||||||
{
|
{
|
||||||
@@ -452,6 +470,9 @@ void APS_Editor_PlayerController::DisableSublevelPostProcessMaterials()
|
|||||||
ULevel* Level = CurrentSublevel->GetLoadedLevel();
|
ULevel* Level = CurrentSublevel->GetLoadedLevel();
|
||||||
if (!Level) return;
|
if (!Level) return;
|
||||||
|
|
||||||
|
// Force lighting/rendering resources to initialize properly for baked lightmaps
|
||||||
|
Level->InitializeRenderingResources();
|
||||||
|
|
||||||
APS_Editor_CameraStart* CameraStart = nullptr;
|
APS_Editor_CameraStart* CameraStart = nullptr;
|
||||||
|
|
||||||
for (AActor* Actor : Level->Actors)
|
for (AActor* Actor : Level->Actors)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "PS_Editor_UndoManager.h"
|
#include "PS_Editor_UndoManager.h"
|
||||||
#include "PS_Editor_SplineEditable.h"
|
#include "PS_Editor_SplineEditable.h"
|
||||||
|
#include "PS_Editor_ChildMovable.h"
|
||||||
#include "UObject/UnrealType.h"
|
#include "UObject/UnrealType.h"
|
||||||
#include "Components/ActorComponent.h"
|
#include "Components/ActorComponent.h"
|
||||||
|
|
||||||
@@ -9,6 +10,10 @@ void FPS_Editor_SplinePointAction::Execute()
|
|||||||
{
|
{
|
||||||
Spline->SetAllPointLocations(NewPoints);
|
Spline->SetAllPointLocations(NewPoints);
|
||||||
}
|
}
|
||||||
|
else if (SplineActor.IsValid() && SplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_SetAllChildLocations(SplineActor.Get(), NewPoints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPS_Editor_SplinePointAction::Undo()
|
void FPS_Editor_SplinePointAction::Undo()
|
||||||
@@ -17,6 +22,10 @@ void FPS_Editor_SplinePointAction::Undo()
|
|||||||
{
|
{
|
||||||
Spline->SetAllPointLocations(OldPoints);
|
Spline->SetAllPointLocations(OldPoints);
|
||||||
}
|
}
|
||||||
|
else if (SplineActor.IsValid() && SplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_SetAllChildLocations(SplineActor.Get(), OldPoints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPS_Editor_PropertyAction::ApplyValue(const FString& ValueStr)
|
void FPS_Editor_PropertyAction::ApplyValue(const FString& ValueStr)
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ struct FPS_Editor_ActorData
|
|||||||
/** Custom property values: key = "ComponentName.PropertyName", value = text-serialized value. */
|
/** Custom property values: key = "ComponentName.PropertyName", value = text-serialized value. */
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TMap<FString, FString> CustomProperties;
|
TMap<FString, FString> CustomProperties;
|
||||||
|
|
||||||
|
/** True if this actor is tagged as preload (briefing/placement phase). */
|
||||||
|
UPROPERTY()
|
||||||
|
bool bPreload = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Serialized data for a complete scene. */
|
/** Serialized data for a complete scene. */
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "PS_Editor_SceneData.h"
|
#include "PS_Editor_SceneData.h"
|
||||||
#include "PS_Editor_EditableComponent.h"
|
#include "PS_Editor_EditableComponent.h"
|
||||||
#include "PS_Editor_EditableInterface.h"
|
#include "PS_Editor_EditableInterface.h"
|
||||||
|
#include "PS_Editor_ChildMovable.h"
|
||||||
#include "PS_Editor_SpawnableComponent.h"
|
#include "PS_Editor_SpawnableComponent.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
#include "PS_Editor_SplineEditable.h"
|
#include "PS_Editor_SplineEditable.h"
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
#include "UObject/UnrealType.h"
|
#include "UObject/UnrealType.h"
|
||||||
#include "Components/ActorComponent.h"
|
#include "Components/ActorComponent.h"
|
||||||
|
|
||||||
bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors, bool bStripEditorComponents)
|
bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors, bool bStripEditorComponents, EPS_Editor_SceneLoadFilter Filter)
|
||||||
{
|
{
|
||||||
OutActors.Empty();
|
OutActors.Empty();
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LoadSceneFromData(World, SceneData, OutActors))
|
if (!LoadSceneFromData(World, SceneData, OutActors, Filter))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,7 @@ bool UPS_Editor_SceneLoader::LoadScene(const UObject* WorldContextObject, const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors)
|
bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_SceneData& SceneData, TArray<AActor*>& OutActors, EPS_Editor_SceneLoadFilter Filter)
|
||||||
{
|
{
|
||||||
if (!World)
|
if (!World)
|
||||||
{
|
{
|
||||||
@@ -99,7 +100,7 @@ bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_S
|
|||||||
bIsCurrentlyLoading = true;
|
bIsCurrentlyLoading = true;
|
||||||
for (const FPS_Editor_ActorData& ActorData : SceneData.Actors)
|
for (const FPS_Editor_ActorData& ActorData : SceneData.Actors)
|
||||||
{
|
{
|
||||||
AActor* SpawnedActor = SpawnActorFromData(World, ActorData);
|
AActor* SpawnedActor = SpawnActorFromData(World, ActorData, Filter);
|
||||||
if (SpawnedActor)
|
if (SpawnedActor)
|
||||||
{
|
{
|
||||||
OutActors.Add(SpawnedActor);
|
OutActors.Add(SpawnedActor);
|
||||||
@@ -113,8 +114,12 @@ bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_S
|
|||||||
return SpawnedCount > 0;
|
return SpawnedCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AActor* UPS_Editor_SceneLoader::SpawnActorFromData(UWorld* World, const FPS_Editor_ActorData& ActorData)
|
AActor* UPS_Editor_SceneLoader::SpawnActorFromData(UWorld* World, const FPS_Editor_ActorData& ActorData, EPS_Editor_SceneLoadFilter Filter)
|
||||||
{
|
{
|
||||||
|
// Apply preload filter
|
||||||
|
if (Filter == EPS_Editor_SceneLoadFilter::PreloadOnly && !ActorData.bPreload) return nullptr;
|
||||||
|
if (Filter == EPS_Editor_SceneLoadFilter::ExcludePreload && ActorData.bPreload) return nullptr;
|
||||||
|
|
||||||
// Load class (supports both Blueprint and C++ classes)
|
// Load class (supports both Blueprint and C++ classes)
|
||||||
UClass* LoadedClass = LoadObject<UClass>(nullptr, *ActorData.ClassPath);
|
UClass* LoadedClass = LoadObject<UClass>(nullptr, *ActorData.ClassPath);
|
||||||
if (!LoadedClass)
|
if (!LoadedClass)
|
||||||
@@ -156,6 +161,12 @@ AActor* UPS_Editor_SceneLoader::SpawnActorFromData(UWorld* World, const FPS_Edit
|
|||||||
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Import child movable data
|
||||||
|
if (SpawnedActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_ImportChildLocations(SpawnedActor, ActorData.CustomProperties);
|
||||||
|
}
|
||||||
|
|
||||||
// Notify the actor that all properties are ready
|
// Notify the actor that all properties are ready
|
||||||
if (SpawnedActor->GetClass()->ImplementsInterface(UPS_Editor_EditableInterface::StaticClass()))
|
if (SpawnedActor->GetClass()->ImplementsInterface(UPS_Editor_EditableInterface::StaticClass()))
|
||||||
{
|
{
|
||||||
@@ -211,6 +222,7 @@ void UPS_Editor_SceneLoader::ApplyCustomProperties(AActor* Actor, const TMap<FSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool UPS_Editor_SceneLoader::bIsCurrentlyLoading = false;
|
bool UPS_Editor_SceneLoader::bIsCurrentlyLoading = false;
|
||||||
|
bool UPS_Editor_SceneLoader::bIsEditorModeActive = false;
|
||||||
|
|
||||||
bool UPS_Editor_SceneLoader::IsLoadingFromScene(const AActor* Actor)
|
bool UPS_Editor_SceneLoader::IsLoadingFromScene(const AActor* Actor)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,15 @@
|
|||||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
#include "PS_Editor_SceneLoader.generated.h"
|
#include "PS_Editor_SceneLoader.generated.h"
|
||||||
|
|
||||||
|
/** Filter for selective scene loading. */
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EPS_Editor_SceneLoadFilter : uint8
|
||||||
|
{
|
||||||
|
All, /** Load all actors. */
|
||||||
|
PreloadOnly, /** Load only actors tagged as preload (briefing/placement). */
|
||||||
|
ExcludePreload /** Load all actors except those tagged as preload. */
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standalone scene loader for PS_Editor.
|
* Standalone scene loader for PS_Editor.
|
||||||
* Loads a JSON scene file and spawns all actors with their properties and spline data.
|
* Loads a JSON scene file and spawns all actors with their properties and spline data.
|
||||||
@@ -37,13 +46,15 @@ public:
|
|||||||
* @return True if the scene was loaded successfully.
|
* @return True if the scene was loaded successfully.
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, Category = "PS Editor", meta = (WorldContext = "WorldContextObject"))
|
UFUNCTION(BlueprintCallable, Category = "PS Editor", meta = (WorldContext = "WorldContextObject"))
|
||||||
static bool LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors, bool bStripEditorComponents = true);
|
static bool LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors,
|
||||||
|
bool bStripEditorComponents = true, EPS_Editor_SceneLoadFilter Filter = EPS_Editor_SceneLoadFilter::All);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load scene from an already-parsed SceneData struct.
|
* Load scene from an already-parsed SceneData struct.
|
||||||
* Useful if you want to parse the JSON yourself or modify data before spawning.
|
* 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);
|
static bool LoadSceneFromData(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 scenes are stored.
|
||||||
@@ -80,6 +91,13 @@ public:
|
|||||||
/** Static flag: true while SceneLoader or SceneSerializer is spawning actors. */
|
/** Static flag: true while SceneLoader or SceneSerializer is spawning actors. */
|
||||||
static bool bIsCurrentlyLoading;
|
static bool bIsCurrentlyLoading;
|
||||||
|
|
||||||
|
/** Static flag: true while the PS_Editor runtime editor is active. */
|
||||||
|
static bool bIsEditorModeActive;
|
||||||
|
|
||||||
|
/** Returns true if the PS_Editor runtime editor is currently active. Callable from any BP. */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS Editor")
|
||||||
|
static bool IsInEditorMode() { return bIsEditorModeActive; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all saved scenes for the current level.
|
* Get all saved scenes for the current level.
|
||||||
*/
|
*/
|
||||||
@@ -87,8 +105,8 @@ public:
|
|||||||
static TArray<FString> GetSavedSceneNamesForCurrentLevel(const UObject* WorldContextObject);
|
static TArray<FString> GetSavedSceneNamesForCurrentLevel(const UObject* WorldContextObject);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Spawn a single actor from saved data and apply all properties. */
|
/** Spawn a single actor from saved data and apply all properties. Returns nullptr if filtered out. */
|
||||||
static AActor* SpawnActorFromData(UWorld* World, const FPS_Editor_ActorData& ActorData);
|
static AActor* SpawnActorFromData(UWorld* World, const FPS_Editor_ActorData& ActorData, EPS_Editor_SceneLoadFilter Filter);
|
||||||
|
|
||||||
/** Apply custom editable properties to a spawned actor. */
|
/** Apply custom editable properties to a spawned actor. */
|
||||||
static void ApplyCustomProperties(AActor* Actor, const TMap<FString, FString>& CustomProperties);
|
static void ApplyCustomProperties(AActor* Actor, const TMap<FString, FString>& CustomProperties);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "PS_Editor_SelectionManager.h"
|
#include "PS_Editor_SelectionManager.h"
|
||||||
#include "PS_Editor_EditableComponent.h"
|
#include "PS_Editor_EditableComponent.h"
|
||||||
#include "PS_Editor_EditableInterface.h"
|
#include "PS_Editor_EditableInterface.h"
|
||||||
|
#include "PS_Editor_SpawnableComponent.h"
|
||||||
|
#include "PS_Editor_ChildMovable.h"
|
||||||
#include "PS_Editor_PlayerController.h"
|
#include "PS_Editor_PlayerController.h"
|
||||||
#include "PS_Editor_SceneLoader.h"
|
#include "PS_Editor_SceneLoader.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
@@ -49,6 +51,12 @@ bool UPS_Editor_SceneSerializer::SaveScene(const FString& SceneName, UPS_Editor_
|
|||||||
ActorData.Rotation = Actor->GetActorRotation();
|
ActorData.Rotation = Actor->GetActorRotation();
|
||||||
ActorData.Scale = Actor->GetActorScale3D();
|
ActorData.Scale = Actor->GetActorScale3D();
|
||||||
|
|
||||||
|
// Save preload tag
|
||||||
|
if (UPS_Editor_SpawnableComponent* SpawnComp = Actor->FindComponentByClass<UPS_Editor_SpawnableComponent>())
|
||||||
|
{
|
||||||
|
ActorData.bPreload = SpawnComp->bPreload;
|
||||||
|
}
|
||||||
|
|
||||||
// Export spline data
|
// Export spline data
|
||||||
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
if (IPS_Editor_SplineEditable* Spline = Cast<IPS_Editor_SplineEditable>(Actor))
|
||||||
{
|
{
|
||||||
@@ -57,6 +65,12 @@ bool UPS_Editor_SceneSerializer::SaveScene(const FString& SceneName, UPS_Editor_
|
|||||||
Spline->GetNumPoints(), *ActorData.ClassPath);
|
Spline->GetNumPoints(), *ActorData.ClassPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export child movable data
|
||||||
|
if (Actor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_ExportChildLocations(Actor, ActorData.CustomProperties);
|
||||||
|
}
|
||||||
|
|
||||||
// Export custom editable properties
|
// Export custom editable properties
|
||||||
if (UPS_Editor_EditableComponent* EditComp = Actor->FindComponentByClass<UPS_Editor_EditableComponent>())
|
if (UPS_Editor_EditableComponent* EditComp = Actor->FindComponentByClass<UPS_Editor_EditableComponent>())
|
||||||
{
|
{
|
||||||
@@ -171,6 +185,15 @@ bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_
|
|||||||
AActor* SpawnedActor = SpawnManager->SpawnActorDirect(LoadedClass, ActorData.Location, ActorData.Rotation, ActorData.Scale);
|
AActor* SpawnedActor = SpawnManager->SpawnActorDirect(LoadedClass, ActorData.Location, ActorData.Rotation, ActorData.Scale);
|
||||||
if (SpawnedActor)
|
if (SpawnedActor)
|
||||||
{
|
{
|
||||||
|
// Restore preload tag
|
||||||
|
if (ActorData.bPreload)
|
||||||
|
{
|
||||||
|
if (UPS_Editor_SpawnableComponent* SpawnComp = SpawnedActor->FindComponentByClass<UPS_Editor_SpawnableComponent>())
|
||||||
|
{
|
||||||
|
SpawnComp->bPreload = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Import custom properties
|
// Import custom properties
|
||||||
if (ActorData.CustomProperties.Num() > 0)
|
if (ActorData.CustomProperties.Num() > 0)
|
||||||
{
|
{
|
||||||
@@ -222,6 +245,12 @@ bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_
|
|||||||
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
Spline->ImportFromCustomProperties(ActorData.CustomProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Import child movable data
|
||||||
|
if (SpawnedActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_ImportChildLocations(SpawnedActor, ActorData.CustomProperties);
|
||||||
|
}
|
||||||
|
|
||||||
// Notify the actor that properties were loaded and we are in editor mode
|
// Notify the actor that properties were loaded and we are in editor mode
|
||||||
if (SpawnedActor->GetClass()->ImplementsInterface(UPS_Editor_EditableInterface::StaticClass()))
|
if (SpawnedActor->GetClass()->ImplementsInterface(UPS_Editor_EditableInterface::StaticClass()))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UObject/Interface.h"
|
||||||
|
#include "PS_Editor_ChildMovable.generated.h"
|
||||||
|
|
||||||
|
UINTERFACE(BlueprintType, meta = (DisplayName = "PS Editor Child Movable"))
|
||||||
|
class PS_EDITOR_API UPS_Editor_ChildMovable : public UInterface
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for actors containing sub-elements (child actors, components)
|
||||||
|
* that can be individually selected and moved in the PS_Editor runtime editor.
|
||||||
|
*
|
||||||
|
* Implement this in your Blueprint to enable per-child editing.
|
||||||
|
* The plugin handles selection, gizmo, undo, and serialization.
|
||||||
|
*
|
||||||
|
* Example: VR placement root with 10 movable placement spots.
|
||||||
|
*/
|
||||||
|
class PS_EDITOR_API IPS_Editor_ChildMovable
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Number of movable sub-elements. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
int32 GetMovableChildCount() const;
|
||||||
|
|
||||||
|
/** World location of the sub-element at the given index. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
FVector GetChildLocation(int32 Index) const;
|
||||||
|
|
||||||
|
/** Move the sub-element at the given index to a new world position. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void MoveChild(int32 Index, const FVector& NewWorldPosition);
|
||||||
|
|
||||||
|
/** Return all sub-element positions (used for undo snapshots). */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
TArray<FVector> GetAllChildLocations() const;
|
||||||
|
|
||||||
|
/** Restore all sub-element positions (used for undo). */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void SetAllChildLocations(const TArray<FVector>& Positions);
|
||||||
|
|
||||||
|
/** Index of the sub-element under the cursor ray (-1 if none).
|
||||||
|
* Implement with sphere-ray intersection or any hit detection that suits your visuals. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
int32 GetChildIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection) const;
|
||||||
|
|
||||||
|
/** Show or hide visual handles for the sub-elements. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void SetChildHandlesVisible(bool bVisible);
|
||||||
|
|
||||||
|
/** Highlight a specific sub-element (e.g. change handle color). */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void HighlightChild(int32 Index);
|
||||||
|
|
||||||
|
/** Clear all sub-element highlights. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void ClearChildHighlight();
|
||||||
|
|
||||||
|
/** Export sub-element positions to the CustomProperties map for scene saving. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void ExportChildLocations(TMap<FString, FString>& OutProperties) const;
|
||||||
|
|
||||||
|
/** Import sub-element positions from the CustomProperties map on scene load. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void ImportChildLocations(const TMap<FString, FString>& Properties);
|
||||||
|
};
|
||||||
@@ -47,4 +47,9 @@ public:
|
|||||||
/** If true, scaling is always uniform on all axes. Dragging any single axis scales all three equally. */
|
/** If true, scaling is always uniform on all axes. Dragging any single axis scales all three equally. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawnable")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawnable")
|
||||||
bool bUniformScaleOnly = false;
|
bool bUniformScaleOnly = false;
|
||||||
|
|
||||||
|
/** If true, this actor belongs to the preload/briefing phase.
|
||||||
|
* Use scene loading filters to load only preload actors or exclude them. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawnable")
|
||||||
|
bool bPreload = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "PS_Editor_SpawnableComponent.h"
|
#include "PS_Editor_SpawnableComponent.h"
|
||||||
#include "PS_Editor_SplineActor.h"
|
#include "PS_Editor_SplineActor.h"
|
||||||
#include "PS_Editor_SplineEditable.h"
|
#include "PS_Editor_SplineEditable.h"
|
||||||
|
#include "PS_Editor_ChildMovable.h"
|
||||||
#include "PS_Editor_Pawn.h"
|
#include "PS_Editor_Pawn.h"
|
||||||
#include "PS_Editor_PointPlaceable.h"
|
#include "PS_Editor_PointPlaceable.h"
|
||||||
#include "Widgets/SBoxPanel.h"
|
#include "Widgets/SBoxPanel.h"
|
||||||
@@ -222,6 +223,39 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
|||||||
.ColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f))
|
.ColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f))
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
+ SHorizontalBox::Slot().AutoWidth().Padding(8.0f, 0.0f, 0.0f, 0.0f).VAlign(VAlign_Center)
|
||||||
|
[
|
||||||
|
SAssignNew(ChildEditButton, SButton)
|
||||||
|
.Visibility(EVisibility::Collapsed)
|
||||||
|
.OnClicked_Lambda([this]() -> FReply
|
||||||
|
{
|
||||||
|
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||||
|
{
|
||||||
|
if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement)
|
||||||
|
{
|
||||||
|
EditorPC->FinishPointPlacement();
|
||||||
|
}
|
||||||
|
else if (UPS_Editor_SelectionManager* SelMgr = SelectionManager.Get())
|
||||||
|
{
|
||||||
|
for (const TWeakObjectPtr<AActor>& Weak : SelMgr->GetSelectedActors())
|
||||||
|
{
|
||||||
|
if (Weak.IsValid() && Weak->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
EditorPC->BeginPointPlacementAppend(Weak.Get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FReply::Handled();
|
||||||
|
})
|
||||||
|
[
|
||||||
|
SNew(STextBlock)
|
||||||
|
.Text(FText::FromString(TEXT("Edit Children")))
|
||||||
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 10))
|
||||||
|
.ColorAndOpacity(FLinearColor(0.3f, 0.8f, 1.0f))
|
||||||
|
]
|
||||||
|
]
|
||||||
+ SHorizontalBox::Slot().AutoWidth().Padding(8.0f, 0.0f, 0.0f, 0.0f).VAlign(VAlign_Center)
|
+ SHorizontalBox::Slot().AutoWidth().Padding(8.0f, 0.0f, 0.0f, 0.0f).VAlign(VAlign_Center)
|
||||||
[
|
[
|
||||||
SAssignNew(SplineDeletePointButton, SButton)
|
SAssignNew(SplineDeletePointButton, SButton)
|
||||||
@@ -831,6 +865,7 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Notify all spawned actors: leaving editor mode
|
// Notify all spawned actors: leaving editor mode
|
||||||
|
UPS_Editor_SceneLoader::bIsEditorModeActive = false;
|
||||||
if (UPS_Editor_SpawnManager* PlaySM2 = EditorPC->GetSpawnManager())
|
if (UPS_Editor_SpawnManager* PlaySM2 = EditorPC->GetSpawnManager())
|
||||||
{
|
{
|
||||||
PlaySM2->NotifyEditorModeChanged(false);
|
PlaySM2->NotifyEditorModeChanged(false);
|
||||||
@@ -1425,8 +1460,9 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if a spline is selected (for Edit button)
|
// Determine if a spline or child-movable is selected (for Edit buttons)
|
||||||
bool bSplineSelected = false;
|
bool bSplineSelected = false;
|
||||||
|
bool bChildMovableSelected = false;
|
||||||
if (CurrentEditorMode == EPS_Editor_EditorMode::Normal && SM->IsAnythingSelected())
|
if (CurrentEditorMode == EPS_Editor_EditorMode::Normal && SM->IsAnythingSelected())
|
||||||
{
|
{
|
||||||
for (const TWeakObjectPtr<AActor>& Weak : SM->GetSelectedActors())
|
for (const TWeakObjectPtr<AActor>& Weak : SM->GetSelectedActors())
|
||||||
@@ -1436,6 +1472,11 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
bSplineSelected = true;
|
bSplineSelected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (Weak.IsValid() && Weak->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
|
{
|
||||||
|
bChildMovableSelected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1461,9 +1502,22 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
HelpBarText->SetColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f));
|
HelpBarText->SetColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f));
|
||||||
}
|
}
|
||||||
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
|
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(bPointSelected ? EVisibility::Visible : EVisibility::Collapsed);
|
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(bPointSelected ? EVisibility::Visible : EVisibility::Collapsed);
|
||||||
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible);
|
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible);
|
||||||
}
|
}
|
||||||
|
else if (bChildMovableSelected)
|
||||||
|
{
|
||||||
|
if (HelpBarText.IsValid())
|
||||||
|
{
|
||||||
|
HelpBarText->SetText(FText::FromString(TEXT("Child-movable selected — click Edit Children to move sub-elements")));
|
||||||
|
HelpBarText->SetColorAndOpacity(FLinearColor(0.3f, 0.8f, 1.0f));
|
||||||
|
}
|
||||||
|
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
|
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Visible);
|
||||||
|
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(EVisibility::Collapsed);
|
||||||
|
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Collapsed);
|
||||||
|
}
|
||||||
else if (bSplineSelected)
|
else if (bSplineSelected)
|
||||||
{
|
{
|
||||||
if (HelpBarText.IsValid())
|
if (HelpBarText.IsValid())
|
||||||
@@ -1483,6 +1537,7 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
HelpBarText->SetColorAndOpacity(FLinearColor(0.6f, 0.6f, 0.6f));
|
HelpBarText->SetColorAndOpacity(FLinearColor(0.6f, 0.6f, 0.6f));
|
||||||
}
|
}
|
||||||
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
|
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(EVisibility::Collapsed);
|
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Collapsed);
|
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Collapsed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ private:
|
|||||||
TSharedPtr<SWidget> SplineFinishButton;
|
TSharedPtr<SWidget> SplineFinishButton;
|
||||||
TSharedPtr<SWidget> SplineEditButton;
|
TSharedPtr<SWidget> SplineEditButton;
|
||||||
TSharedPtr<SWidget> SplineDeletePointButton;
|
TSharedPtr<SWidget> SplineDeletePointButton;
|
||||||
|
TSharedPtr<SWidget> ChildEditButton;
|
||||||
|
|
||||||
// Simulate button
|
// Simulate button
|
||||||
TSharedPtr<STextBlock> SimulateButtonText;
|
TSharedPtr<STextBlock> SimulateButtonText;
|
||||||
|
|||||||
Reference in New Issue
Block a user