diff --git a/Unreal/Content/test.umap b/Unreal/Content/test.umap index 17beaff..ef3cdbe 100644 Binary files a/Unreal/Content/test.umap and b/Unreal/Content/test.umap differ diff --git a/Unreal/Plugins/PS_Editor/Content/NewPS_Editor_SpawnCatalog.uasset b/Unreal/Plugins/PS_Editor/Content/NewPS_Editor_SpawnCatalog.uasset deleted file mode 100644 index f78e256..0000000 Binary files a/Unreal/Plugins/PS_Editor/Content/NewPS_Editor_SpawnCatalog.uasset and /dev/null differ diff --git a/Unreal/Plugins/PS_Editor/Content/PS_Editor_PlayerController.uasset b/Unreal/Plugins/PS_Editor/Content/PS_Editor_PlayerController.uasset index 8459e9f..55c6b3c 100644 Binary files a/Unreal/Plugins/PS_Editor/Content/PS_Editor_PlayerController.uasset and b/Unreal/Plugins/PS_Editor/Content/PS_Editor_PlayerController.uasset differ diff --git a/Unreal/Plugins/PS_Editor/Content/PS_MasterCatalog.uasset b/Unreal/Plugins/PS_Editor/Content/PS_MasterCatalog.uasset new file mode 100644 index 0000000..5e99609 Binary files /dev/null and b/Unreal/Plugins/PS_Editor/Content/PS_MasterCatalog.uasset differ diff --git a/Unreal/Plugins/PS_Editor/Content/PS_PropsCatalog.uasset b/Unreal/Plugins/PS_Editor/Content/PS_PropsCatalog.uasset new file mode 100644 index 0000000..696b501 Binary files /dev/null and b/Unreal/Plugins/PS_Editor/Content/PS_PropsCatalog.uasset differ diff --git a/Unreal/Plugins/PS_Editor/Content/PS_SplineEnnemy.uasset b/Unreal/Plugins/PS_Editor/Content/PS_SplineEnnemy.uasset new file mode 100644 index 0000000..a850748 Binary files /dev/null and b/Unreal/Plugins/PS_Editor/Content/PS_SplineEnnemy.uasset differ diff --git a/Unreal/Plugins/PS_Editor/Content/PS_ToolsCatalog.uasset b/Unreal/Plugins/PS_Editor/Content/PS_ToolsCatalog.uasset new file mode 100644 index 0000000..9e56d44 Binary files /dev/null and b/Unreal/Plugins/PS_Editor/Content/PS_ToolsCatalog.uasset differ diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp index 0cadbe7..b988290 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_Pawn.cpp @@ -577,62 +577,93 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) } } - // Check for spline point handle hit → select point, move gizmo to it (only in SplineEditing mode) + // Check for spline point handle hit → select point, move gizmo to it (SplineEditing or SplinePlacement) if (APS_Editor_PlayerController* EdPC = Cast(GetController())) { - if (EdPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing) + const EPS_Editor_EditorMode Mode = EdPC->GetEditorMode(); + if (Mode == EPS_Editor_EditorMode::SplinePlacement || Mode == EPS_Editor_EditorMode::SplinePlacement) { + // Collect candidate splines: selected actors + active placement actor + TArray CandidateSplines; if (UPS_Editor_SelectionManager* SM = EdPC->GetSelectionManager()) { for (const TWeakObjectPtr& Weak : SM->GetSelectedActors()) { - if (APS_Editor_SplineActor* Spline = Cast(Weak.Get())) + if (APS_Editor_SplineActor* S = Cast(Weak.Get())) { - // First check: did we hit the center cube? If so, skip handle detection - // (let the click fall through to normal selection → gizmo at actor pivot) - FHitResult SplineHit; - FCollisionQueryParams SplineParams; - SplineParams.AddIgnoredActor(this); - const FVector TraceEnd = RayOrigin + RayDir * 100000.0f; - if (GetWorld()->LineTraceSingleByChannel(SplineHit, RayOrigin, TraceEnd, ECC_Visibility, SplineParams)) + CandidateSplines.AddUnique(S); + } + } + } + if (APS_Editor_SplineActor* ActiveSpline = Cast(EdPC->GetActivePlacementActor())) + { + CandidateSplines.AddUnique(ActiveSpline); + } + + for (APS_Editor_SplineActor* Spline : CandidateSplines) + { + // Center cube check (skip in SplinePlacement — no center cube interaction during creation) + if (Mode == EPS_Editor_EditorMode::SplinePlacement) + { + FHitResult SplineHit; + FCollisionQueryParams SplineParams; + SplineParams.AddIgnoredActor(this); + const FVector TraceEnd = RayOrigin + RayDir * 100000.0f; + if (GetWorld()->LineTraceSingleByChannel(SplineHit, RayOrigin, TraceEnd, ECC_Visibility, SplineParams)) + { + if (Spline->IsCenterCube(SplineHit.GetComponent())) { - if (Spline->IsCenterCube(SplineHit.GetComponent())) - { - // Center cube hit → reset point selection, let normal flow handle it - ActiveSplinePointIndex = -1; - DraggedSplineActor = nullptr; - Spline->ClearHandleHighlight(); - break; - } - } - - const int32 HandleIdx = Spline->GetHandleIndexUnderCursor(RayOrigin, RayDir); - if (HandleIdx >= 0) - { - ActiveSplinePointIndex = HandleIdx; - DraggedSplineActor = Spline; - Spline->HighlightHandle(HandleIdx); - - if (APS_Editor_Gizmo* Gizmo = SM->GetGizmo()) - { - Gizmo->SetActorLocation(Spline->GetPointLocation(HandleIdx)); - } - - SM->SetTransformMode(EPS_Editor_TransformMode::Translate); - return; + ActiveSplinePointIndex = -1; + DraggedSplineActor = nullptr; + Spline->ClearHandleHighlight(); + break; } } } + + const int32 HandleIdx = Spline->GetHandleIndexUnderCursor(RayOrigin, RayDir); + if (HandleIdx >= 0) + { + ActiveSplinePointIndex = HandleIdx; + DraggedSplineActor = Spline; + Spline->HighlightHandle(HandleIdx); + + if (UPS_Editor_SelectionManager* SM = EdPC->GetSelectionManager()) + { + if (APS_Editor_Gizmo* Gizmo = SM->GetGizmo()) + { + // Force gizmo active at this point (even if spline not in SelectionManager yet) + Gizmo->SetGizmoActive(true, Spline->GetPointLocation(HandleIdx)); + } + SM->SetTransformMode(EPS_Editor_TransformMode::Translate); + } + return; + } } } } - // In SplineEditing 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(GetController())) { - if (EdPC2->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing) + if (EdPC2->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) { + // Collect candidate splines: selected + active placement actor + TArray TubeCandidates; if (UPS_Editor_SelectionManager* SM2 = EdPC2->GetSelectionManager()) + { + for (const TWeakObjectPtr& Weak : SM2->GetSelectedActors()) + { + if (APS_Editor_SplineActor* S = Cast(Weak.Get())) + TubeCandidates.AddUnique(S); + } + } + if (APS_Editor_SplineActor* ActiveSpline = Cast(EdPC2->GetActivePlacementActor())) + { + TubeCandidates.AddUnique(ActiveSpline); + } + + if (TubeCandidates.Num() > 0) { FHitResult LineHit; FCollisionQueryParams LineParams; @@ -641,10 +672,8 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) if (GetWorld()->LineTraceSingleByChannel(LineHit, RayOrigin, TraceEnd, ECC_Visibility, LineParams)) { - for (const TWeakObjectPtr& Weak : SM2->GetSelectedActors()) + for (APS_Editor_SplineActor* Spline : TubeCandidates) { - APS_Editor_SplineActor* Spline = Cast(Weak.Get()); - if (!Spline) continue; if (Spline->IsSplineMesh(LineHit.GetComponent())) { @@ -668,11 +697,14 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) DraggedSplineActor = Spline; Spline->HighlightHandle(InsertIdx); - if (APS_Editor_Gizmo* G = SM2->GetGizmo()) + if (UPS_Editor_SelectionManager* InsertSM = EdPC2->GetSelectionManager()) { - G->SetActorLocation(Spline->GetPointLocation(InsertIdx)); + if (APS_Editor_Gizmo* G = InsertSM->GetGizmo()) + { + G->SetGizmoActive(true, Spline->GetPointLocation(InsertIdx)); + } + InsertSM->SetTransformMode(EPS_Editor_TransformMode::Translate); } - SM2->SetTransformMode(EPS_Editor_TransformMode::Translate); UE_LOG(LogTemp, Log, TEXT("PS_Editor: Inserted spline point at index %d"), InsertIdx); return; @@ -683,6 +715,63 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value) } } + // In SplineEditing mode: click outside tube/handles → add point at end + if (APS_Editor_PlayerController* EdPC3 = Cast(GetController())) + { + if (EdPC3->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) + { + if (APS_Editor_SplineActor* Spline = Cast(EdPC3->GetActivePlacementActor())) + { + // Raycast to get world click position + FHitResult AddHit; + FCollisionQueryParams AddParams; + AddParams.AddIgnoredActor(this); + AddParams.AddIgnoredActor(Spline); + const FVector AddEnd = RayOrigin + RayDir * 100000.0f; + + FVector ClickPos; + if (GetWorld()->LineTraceSingleByChannel(AddHit, RayOrigin, AddEnd, ECC_Visibility, AddParams)) + { + ClickPos = AddHit.ImpactPoint + FVector(0.0f, 0.0f, 5.0f); + } + else + { + ClickPos = RayOrigin + RayDir * 1000.0f; + } + + TArray OldPoints = Spline->GetAllPointLocations(); + Spline->AddPoint(ClickPos); + + if (UPS_Editor_UndoManager* UM = EdPC3->GetUndoManager()) + { + TSharedPtr Action = MakeShared(); + Action->SplineActor = Spline; + Action->OldPoints = OldPoints; + Action->NewPoints = Spline->GetAllPointLocations(); + Action->Description = TEXT("Add spline point"); + UM->RecordAction(Action); + } + + // Select the new point + const int32 NewIdx = Spline->GetNumPoints() - 1; + ActiveSplinePointIndex = NewIdx; + DraggedSplineActor = Spline; + Spline->HighlightHandle(NewIdx); + + if (UPS_Editor_SelectionManager* SM3 = EdPC3->GetSelectionManager()) + { + if (APS_Editor_Gizmo* G = SM3->GetGizmo()) + { + G->SetActorLocation(Spline->GetPointLocation(NewIdx)); + } + } + + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Added spline point at end (index %d)"), NewIdx); + return; + } + } + } + // Clicking elsewhere resets spline point selection ActiveSplinePointIndex = -1; DraggedSplineActor = nullptr; @@ -809,15 +898,6 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value) void APS_Editor_Pawn::HandleRightClickStarted(const FInputActionValue& Value) { - // In spline placement mode, right-click finishes the spline - if (APS_Editor_PlayerController* EditorPC = Cast(GetController())) - { - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) - { - EditorPC->FinishPointPlacement(); - return; - } - } SetCameraMode(EPS_Editor_CameraMode::Fly); } @@ -889,7 +969,7 @@ void APS_Editor_Pawn::HandleDelete(const FInputActionValue& Value) if (!EditorPC) return; // In SplineEditing mode with a point selected: delete that point - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing + if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement && ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid()) { APS_Editor_SplineActor* Spline = Cast(DraggedSplineActor.Get()); @@ -1005,21 +1085,34 @@ void APS_Editor_Pawn::HandleSnapToGround(const FInputActionValue& Value) if (CameraMode != EPS_Editor_CameraMode::Idle) return; if (APS_Editor_PlayerController* EditorPC = Cast(GetController())) { - // If a spline point is selected, snap that point to ground + // If a spline point is selected (in edit or placement mode), snap that point to ground if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid()) { if (APS_Editor_SplineActor* Spline = Cast(DraggedSplineActor.Get())) { const FVector PointLoc = Spline->GetPointLocation(ActiveSplinePointIndex); - const FVector TraceStart = PointLoc + FVector(0.0f, 0.0f, 500.0f); + const FVector TraceStart = PointLoc + FVector(0.0f, 0.0f, 1.0f); // Just above current position const FVector TraceEnd = PointLoc - FVector(0.0f, 0.0f, 10000.0f); FHitResult Hit; FCollisionQueryParams Params; Params.AddIgnoredActor(Spline); + Params.AddIgnoredActor(this); + if (UPS_Editor_SelectionManager* SnapSM = EditorPC->GetSelectionManager()) + { + if (APS_Editor_Gizmo* SnapGizmo = SnapSM->GetGizmo()) + { + Params.AddIgnoredActor(SnapGizmo); + } + } if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params)) { + UE_LOG(LogTemp, Log, TEXT("PS_Editor: SnapToGround - PointLoc=%s, HitZ=%.2f, HitActor=%s, HitComp=%s"), + *PointLoc.ToString(), Hit.ImpactPoint.Z, + Hit.GetActor() ? *Hit.GetActor()->GetName() : TEXT("null"), + Hit.GetComponent() ? *Hit.GetComponent()->GetName() : TEXT("null")); + // Store state for undo TArray OldPoints = Spline->GetAllPointLocations(); @@ -1103,21 +1196,23 @@ void APS_Editor_Pawn::HandleSplineMode(const FInputActionValue& Value) } else { - // If a spline is selected, append points to it instead of creating a new one + // S key only appends to a selected point-placeable actor (no new creation from shortcut) if (UPS_Editor_SelectionManager* SM = EditorPC->GetSelectionManager()) { for (const TWeakObjectPtr& Weak : SM->GetSelectedActors()) { - if (APS_Editor_SplineActor* Spline = Cast(Weak.Get())) + if (AActor* Actor = Weak.Get()) { - ActiveSplinePointIndex = -1; - DraggedSplineActor = nullptr; - EditorPC->BeginPointPlacementAppend(Spline); - return; + if (Actor->Implements()) + { + ActiveSplinePointIndex = -1; + DraggedSplineActor = nullptr; + EditorPC->BeginPointPlacementAppend(Actor); + return; + } } } } - EditorPC->BeginPointPlacement(); } } } @@ -1141,7 +1236,7 @@ void APS_Editor_Pawn::HandleCancel(const FInputActionValue& Value) { EditorPC->CancelPointPlacement(); } - else if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing) + else if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) { EditorPC->FinishSplineEditing(); } diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp index 51783e7..5b1b617 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.cpp @@ -2,6 +2,7 @@ #include "PS_Editor_SelectionManager.h" #include "PS_Editor_UndoManager.h" #include "PS_Editor_SpawnManager.h" +#include "PS_Editor_SpawnCatalog.h" #include "PS_Editor_SceneSerializer.h" #include "PS_Editor_Gizmo.h" #include "PS_Editor_Pawn.h" @@ -26,10 +27,26 @@ void APS_Editor_PlayerController::BeginPlay() UndoManager = NewObject(this); - // Load spawn catalog and create spawn manager + // Create spawn manager and load catalogs from MasterCatalog SpawnManager = NewObject(this); - UPS_Editor_SpawnCatalog* LoadedCatalog = SpawnCatalogAsset.LoadSynchronous(); - SpawnManager->Initialize(this, LoadedCatalog); + SpawnManager->Initialize(this); + + if (UPS_Editor_MasterCatalog* Master = MasterCatalogAsset.LoadSynchronous()) + { + for (const TSoftObjectPtr& CatalogRef : Master->Catalogs) + { + if (UPS_Editor_SpawnCatalog* Catalog = CatalogRef.LoadSynchronous()) + { + SpawnManager->AddCatalog(Catalog); + } + } + UE_LOG(LogTemp, Log, TEXT("PS_Editor: MasterCatalog loaded with %d sub-catalogs, %d total entries"), + Master->Catalogs.Num(), SpawnManager->GetResolvedEntries().Num()); + } + else + { + UE_LOG(LogTemp, Warning, TEXT("PS_Editor: No MasterCatalog assigned.")); + } SceneSerializer = NewObject(this); SceneSerializer->SetSelectionManager(SelectionManager); @@ -121,67 +138,21 @@ void APS_Editor_PlayerController::BeginPointPlacementAppend(AActor* ExistingActo void APS_Editor_PlayerController::FinishPointPlacement() { - IPS_Editor_PointPlaceable* Placeable = Cast(ActivePlacementActor); - if (!ActivePlacementActor || !Placeable || Placeable->GetCurrentPointCount() < Placeable->GetMinimumPoints()) + // Just exit the mode — actor is already tracked, all edits already have undo + if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) { - CancelPointPlacement(); - return; + Spline->SetHandlesVisible(false); + Spline->ClearHandleHighlight(); } - Placeable->OnPlacementFinished(); - - if (bAppendingToActor) + if (ActivePlacementActor && SelectionManager) { - // Appending: record undo for the added points - if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) - { - Spline->SetHandlesVisible(false); - if (UndoManager) - { - TSharedPtr Action = MakeShared(); - Action->SplineActor = Spline; - Action->OldPoints = AppendInitialPoints; - Action->NewPoints = Spline->GetAllPointLocations(); - Action->Description = FString::Printf(TEXT("Add %d point(s)"), - Spline->GetNumPoints() - AppendInitialPoints.Num()); - UndoManager->RecordAction(Action); - } - } - if (SelectionManager) - { - SelectionManager->ClearSelection(); - SelectionManager->SelectActor(ActivePlacementActor); - } - } - else - { - // New actor: track and record spawn undo - if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) - { - Spline->SetHandlesVisible(false); - } - - if (SpawnManager) - { - SpawnManager->TrackSpawnedActor(ActivePlacementActor); - } - - if (UndoManager) - { - TSharedPtr Action = MakeShared(); - Action->Actors.Add(ActivePlacementActor); - Action->Description = FString::Printf(TEXT("Create %s (%d points)"), - *ActivePlacementActor->GetClass()->GetName(), Placeable->GetCurrentPointCount()); - UndoManager->RecordAction(Action); - } - - if (SelectionManager) - { - SelectionManager->SelectActor(ActivePlacementActor); - } + SelectionManager->ClearSelection(); + SelectionManager->SelectActor(ActivePlacementActor); } - UE_LOG(LogTemp, Log, TEXT("PS_Editor: Placement finished (%d points)"), Placeable->GetCurrentPointCount()); + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Exited placement mode")); + ActivePlacementActor = nullptr; bAppendingToActor = false; AppendInitialPoints.Empty(); @@ -190,57 +161,8 @@ void APS_Editor_PlayerController::FinishPointPlacement() void APS_Editor_PlayerController::CancelPointPlacement() { - if (ActivePlacementActor) - { - IPS_Editor_PointPlaceable* Placeable = Cast(ActivePlacementActor); - if (Placeable) Placeable->OnPlacementCancelled(); - - if (bAppendingToActor) - { - if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) - { - Spline->SetAllPointLocations(AppendInitialPoints); - Spline->SetHandlesVisible(false); - } - if (SelectionManager) - { - SelectionManager->ClearSelection(); - SelectionManager->SelectActor(ActivePlacementActor); - } - } - else - { - ActivePlacementActor->Destroy(); - } - ActivePlacementActor = nullptr; - } - bAppendingToActor = false; - AppendInitialPoints.Empty(); - EditorMode = EPS_Editor_EditorMode::Normal; - UE_LOG(LogTemp, Log, TEXT("PS_Editor: Placement cancelled")); -} - -void APS_Editor_PlayerController::BeginSplineEditing(APS_Editor_SplineActor* Spline) -{ - if (!Spline) return; - - ActivePlacementActor = Spline; - EditorMode = EPS_Editor_EditorMode::SplineEditing; - Spline->SetHandlesVisible(true); - - UE_LOG(LogTemp, Log, TEXT("PS_Editor: Spline editing mode started (%d points)"), Spline->GetNumPoints()); -} - -void APS_Editor_PlayerController::FinishSplineEditing() -{ - if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) - { - Spline->SetHandlesVisible(false); - Spline->ClearHandleHighlight(); - } - ActivePlacementActor = nullptr; - EditorMode = EPS_Editor_EditorMode::Normal; - UE_LOG(LogTemp, Log, TEXT("PS_Editor: Spline editing mode ended")); + // Same as finish — just exit mode. Undo handles reverting if needed. + FinishPointPlacement(); } void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bAdditive) @@ -270,19 +192,77 @@ void APS_Editor_PlayerController::HandleEditorClick(FVector2D ScreenPos, bool bA if (!ActivePlacementActor) { - // First click: spawn the actor + // First click: spawn the actor, track immediately, record undo FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; UClass* ClassToSpawn = PlacementActorClass ? PlacementActorClass.Get() : APS_Editor_SplineActor::StaticClass(); ActivePlacementActor = GetWorld()->SpawnActor(ClassToSpawn, ClickWorldPos, FRotator::ZeroRotator, SpawnParams); + + if (ActivePlacementActor) + { + // Track immediately (for outliner, save/load, scene clear) + if (SpawnManager) + { + SpawnManager->TrackSpawnedActor(ActivePlacementActor); + } + + // Record spawn undo immediately + if (UndoManager) + { + TSharedPtr Action = MakeShared(); + Action->Actors.Add(ActivePlacementActor); + Action->Description = TEXT("Create placeable actor"); + UndoManager->RecordAction(Action); + } + + // Now we're "appending" to the tracked actor + bAppendingToActor = true; + } } - // Add point via the interface - if (IPS_Editor_PointPlaceable* Placeable = Cast(ActivePlacementActor)) + // Check if we clicked on the existing spline tube → insert instead of append + if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) + { + if (Spline->GetNumPoints() >= 2) + { + // Raycast to check if we hit the tube mesh + FHitResult TubeHit; + FCollisionQueryParams TubeParams; + TubeParams.AddIgnoredActor(GetPawn()); + const FVector TubeEnd = WorldOrigin + WorldDirection * 100000.0f; + + if (GetWorld()->LineTraceSingleByChannel(TubeHit, WorldOrigin, TubeEnd, ECC_Visibility, TubeParams)) + { + if (Spline->IsSplineMesh(TubeHit.GetComponent())) + { + const int32 InsertIdx = Spline->FindInsertIndex(TubeHit.ImpactPoint); + Spline->InsertPoint(InsertIdx, TubeHit.ImpactPoint + FVector(0.0f, 0.0f, 5.0f)); + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Inserted point at index %d during placement"), InsertIdx); + return; + } + } + } + } + + // Default: add point at end via the interface (with undo) + if (APS_Editor_SplineActor* Spline = Cast(ActivePlacementActor)) + { + TArray OldPoints = Spline->GetAllPointLocations(); + Spline->AddPoint(ClickWorldPos); + + if (UndoManager) + { + TSharedPtr Action = MakeShared(); + Action->SplineActor = Spline; + Action->OldPoints = OldPoints; + Action->NewPoints = Spline->GetAllPointLocations(); + Action->Description = TEXT("Add spline point"); + UndoManager->RecordAction(Action); + } + } + else if (IPS_Editor_PointPlaceable* Placeable = Cast(ActivePlacementActor)) { Placeable->AddPlacementPoint(ClickWorldPos); - UE_LOG(LogTemp, Log, TEXT("PS_Editor: Point %d added at %s"), - Placeable->GetCurrentPointCount(), *ClickWorldPos.ToString()); } return; } diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h index c3a3b5e..1d80762 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/GameMode/PS_Editor_PlayerController.h @@ -9,6 +9,7 @@ class UPS_Editor_SelectionManager; class UPS_Editor_UndoManager; class UPS_Editor_SpawnManager; class UPS_Editor_SpawnCatalog; +class UPS_Editor_MasterCatalog; class UPS_Editor_SceneSerializer; class APS_Editor_SplineActor; class IPS_Editor_PointPlaceable; @@ -37,9 +38,9 @@ public: 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. */ + /** Master catalog grouping all sub-catalogs (Tools, Characters, Props...). */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor") - TSoftObjectPtr SpawnCatalogAsset; + TSoftObjectPtr MasterCatalogAsset; // ---- Editor Mode (Normal / SplinePlacement) ---- @@ -61,14 +62,17 @@ public: /** Get the actor currently being placed/edited (nullptr if not in placement mode). */ AActor* GetActivePlacementActor() const { return ActivePlacementActor; } + /** True if we're appending to an existing actor (editing), false if creating new. */ + bool IsAppendingToActor() const { return bAppendingToActor; } + // Legacy convenience (for spline-specific code like point editing) APS_Editor_SplineActor* GetActiveSplineActor() const; - /** Enter spline point editing mode for a selected spline. */ - void BeginSplineEditing(APS_Editor_SplineActor* Spline); + /** Convenience: enter point placement for an existing spline. */ + void BeginSplineEditing(AActor* Spline) { BeginPointPlacementAppend(Spline); } - /** Exit spline editing mode. */ - void FinishSplineEditing(); + /** Convenience: exit spline editing. */ + void FinishSplineEditing() { FinishPointPlacement(); } protected: virtual void BeginPlay() override; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp index 935269f..6e6da03 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionManager.cpp @@ -225,7 +225,7 @@ void UPS_Editor_SelectionManager::DeselectActor(AActor* Actor) { if (APS_Editor_PlayerController* EditorPC = Cast(PC)) { - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing && EditorPC->GetActivePlacementActor() == Actor) + if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement && EditorPC->GetActivePlacementActor() == Actor) { EditorPC->FinishSplineEditing(); } @@ -268,7 +268,7 @@ void UPS_Editor_SelectionManager::ClearSelection() { if (APS_Editor_PlayerController* EditorPC = Cast(PC)) { - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing) + if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) { EditorPC->FinishSplineEditing(); } diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionTypes.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionTypes.h index 9752d07..6fe7f7b 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionTypes.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Selection/PS_Editor_SelectionTypes.h @@ -39,6 +39,5 @@ UENUM(BlueprintType) enum class EPS_Editor_EditorMode : uint8 { Normal, // Standard select/transform behavior - SplinePlacement, // Clicks add points to current spline - SplineEditing // Editing individual points of a selected spline + SplinePlacement // Creating or editing a point-placeable actor (spline, etc.) }; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h index c546003..b89a627 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalog.h @@ -43,3 +43,19 @@ public: void CaptureAllThumbnails(); #endif }; + +/** + * Master catalog that groups multiple SpawnCatalogs. + * Assign this to the PlayerController. Each sub-catalog can represent a domain + * (Tools, Characters, Props, Vehicles...). + */ +UCLASS(BlueprintType) +class PS_EDITOR_API UPS_Editor_MasterCatalog : public UPrimaryDataAsset +{ + GENERATED_BODY() + +public: + /** Sub-catalogs to load. Each one contributes its entries to the runtime spawn UI. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor") + TArray> Catalogs; +}; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.cpp index 7e0759c..dd78432 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.cpp @@ -22,6 +22,25 @@ FText UPS_Editor_SpawnCatalogFactory::GetDisplayName() const return LOCTEXT("SpawnCatalogDisplayName", "PS Editor Spawn Catalog"); } +// ---- Master Catalog Factory ---- + +UPS_Editor_MasterCatalogFactory::UPS_Editor_MasterCatalogFactory() +{ + SupportedClass = UPS_Editor_MasterCatalog::StaticClass(); + bCreateNew = true; + bEditAfterNew = true; +} + +UObject* UPS_Editor_MasterCatalogFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + return NewObject(InParent, InClass, InName, Flags); +} + +FText UPS_Editor_MasterCatalogFactory::GetDisplayName() const +{ + return LOCTEXT("MasterCatalogDisplayName", "PS Editor Master Catalog"); +} + #undef LOCTEXT_NAMESPACE #endif diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.h index 1074972..8f3f523 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnCatalogFactory.h @@ -19,4 +19,17 @@ public: virtual FText GetDisplayName() const override; }; +UCLASS(HideCategories = Object) +class UPS_Editor_MasterCatalogFactory : public UFactory +{ + GENERATED_BODY() + +public: + UPS_Editor_MasterCatalogFactory(); + + virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + virtual bool ShouldShowInNewMenu() const override { return true; } + virtual FText GetDisplayName() const override; +}; + #endif diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp index 22fe3e4..12faf7e 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.cpp @@ -5,32 +5,18 @@ #include "GameFramework/PlayerController.h" #include "Engine/World.h" -void UPS_Editor_SpawnManager::Initialize(APlayerController* InOwnerPC, UPS_Editor_SpawnCatalog* InCatalog) +void UPS_Editor_SpawnManager::Initialize(APlayerController* InOwnerPC) { OwnerPC = InOwnerPC; - Catalog = InCatalog; - - if (Catalog) - { - ResolveCatalog(); - UE_LOG(LogTemp, Log, TEXT("PS_Editor: SpawnCatalog loaded with %d entries"), ResolvedEntries.Num()); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("PS_Editor: No SpawnCatalog assigned. Spawn catalogue will be empty.")); - } } -void UPS_Editor_SpawnManager::ResolveCatalog() +void UPS_Editor_SpawnManager::AddCatalog(UPS_Editor_SpawnCatalog* InCatalog) { - ResolvedEntries.Empty(); + if (!InCatalog) return; - if (!Catalog) - { - return; - } + const int32 BeforeCount = ResolvedEntries.Num(); - for (const FPS_Editor_SpawnEntry& Entry : Catalog->Entries) + for (const FPS_Editor_SpawnEntry& Entry : InCatalog->Entries) { // Synchronously load the class UClass* LoadedClass = Entry.ActorClass.LoadSynchronous(); @@ -84,6 +70,9 @@ void UPS_Editor_SpawnManager::ResolveCatalog() ResolvedEntries.Add(Resolved); } + + UE_LOG(LogTemp, Log, TEXT("PS_Editor: Added catalog with %d entries (total: %d)"), + ResolvedEntries.Num() - BeforeCount, ResolvedEntries.Num()); } AActor* UPS_Editor_SpawnManager::SpawnFromCatalog(int32 Index) diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.h index 9b29f18..15c929c 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spawn/PS_Editor_SpawnManager.h @@ -36,8 +36,11 @@ class PS_EDITOR_API UPS_Editor_SpawnManager : public UObject GENERATED_BODY() public: - /** Initialize with owning controller and load catalog. */ - void Initialize(APlayerController* InOwnerPC, UPS_Editor_SpawnCatalog* InCatalog); + /** Initialize with owning controller. */ + void Initialize(APlayerController* InOwnerPC); + + /** Load and add a catalog. Can be called multiple times for multiple catalogs. */ + void AddCatalog(UPS_Editor_SpawnCatalog* InCatalog); /** Spawn the actor at the given catalog index, positioned in front of the camera. Returns the spawned actor. */ AActor* SpawnFromCatalog(int32 Index); @@ -70,9 +73,6 @@ private: UPROPERTY() TWeakObjectPtr OwnerPC; - UPROPERTY() - TObjectPtr Catalog; - UPROPERTY() TArray ResolvedEntries; @@ -80,8 +80,6 @@ private: UPROPERTY() TArray> SpawnedActors; - /** Resolve all catalog entries (load classes, read SpawnableComponent metadata). */ - void ResolveCatalog(); /** Compute spawn location in front of the camera. */ FVector ComputeSpawnLocation() const; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp index 85842e1..1d6d417 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.cpp @@ -1,4 +1,6 @@ #include "PS_Editor_SplineActor.h" +#include "PS_Editor_SpawnableComponent.h" +#include "PS_Editor_EditableComponent.h" #include "Components/SplineComponent.h" #include "ProceduralMeshComponent.h" #include "Components/StaticMeshComponent.h" @@ -32,6 +34,17 @@ APS_Editor_SplineActor::APS_Editor_SplineActor() // Handle parent HandleRoot = CreateDefaultSubobject(TEXT("HandleRoot")); HandleRoot->SetupAttachment(Root); + + // Spawnable: makes this actor appear in the catalogue + SpawnableComp = CreateDefaultSubobject(TEXT("PS_Editor_Spawnable")); + SpawnableComp->DisplayName = TEXT("Spline"); + SpawnableComp->Category = TEXT("Tools"); + + // Editable: exposes SplineColor and SplineTag in the runtime property panel + EditableComp = CreateDefaultSubobject(TEXT("PS_Editor_Editable")); + EditableComp->EditableProperties.Add(FName(TEXT("SplineColor"))); + EditableComp->EditableProperties.Add(FName(TEXT("SplineSelectedColor"))); + EditableComp->EditableProperties.Add(FName(TEXT("SplineTag"))); } void APS_Editor_SplineActor::BeginPlay() @@ -100,9 +113,7 @@ void APS_Editor_SplineActor::BeginPlay() void APS_Editor_SplineActor::AddPoint(const FVector& WorldPosition) { - // Convert world to local space - const FVector LocalPos = GetActorTransform().InverseTransformPosition(WorldPosition); - SplineComp->AddSplinePoint(LocalPos, ESplineCoordinateSpace::Local, true); + SplineComp->AddSplinePoint(WorldPosition, ESplineCoordinateSpace::World, true); // Create handle UStaticMeshComponent* Handle = CreatePointHandle(WorldPosition); @@ -115,8 +126,7 @@ void APS_Editor_SplineActor::MovePoint(int32 Index, const FVector& NewWorldPosit { if (Index < 0 || Index >= GetNumPoints()) return; - const FVector LocalPos = GetActorTransform().InverseTransformPosition(NewWorldPosition); - SplineComp->SetLocationAtSplinePoint(Index, LocalPos, ESplineCoordinateSpace::Local, true); + SplineComp->SetLocationAtSplinePoint(Index, NewWorldPosition, ESplineCoordinateSpace::World, true); if (PointHandles.IsValidIndex(Index) && PointHandles[Index]) { @@ -174,8 +184,7 @@ void APS_Editor_SplineActor::SetAllPointLocations(const TArray& Points) SplineComp->ClearSplinePoints(false); for (const FVector& WorldPos : Points) { - const FVector LocalPos = GetActorTransform().InverseTransformPosition(WorldPos); - SplineComp->AddSplinePoint(LocalPos, ESplineCoordinateSpace::Local, false); + SplineComp->AddSplinePoint(WorldPos, ESplineCoordinateSpace::World, false); } SplineComp->UpdateSpline(); @@ -246,7 +255,11 @@ void APS_Editor_SplineActor::UpdateVisualization() } SplineMeshComp->ClearAllMeshSections(); - SplineMeshComp->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, TArray(), TArray(), false); + SplineMeshComp->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, TArray(), TArray(), true); + + // Force collision and bounds update immediately + SplineMeshComp->UpdateBounds(); + SplineMeshComp->MarkRenderStateDirty(); // Apply the current material (normal or selected) // Check if handles are visible as a proxy for "selected" state diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.h b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.h index 1c6aa77..3928c99 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.h +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/Spline/PS_Editor_SplineActor.h @@ -7,6 +7,8 @@ class USplineComponent; class UProceduralMeshComponent; +class UPS_Editor_SpawnableComponent; +class UPS_Editor_EditableComponent; /** * Runtime spline actor for path/route editing in PS_Editor. @@ -157,6 +159,12 @@ private: UPROPERTY() TObjectPtr CenterCubeMat; + UPROPERTY(VisibleAnywhere) + TObjectPtr SpawnableComp; + + UPROPERTY(VisibleAnywhere) + TObjectPtr EditableComp; + /** Tube rendering settings. */ float TubeRadius = 2.0f; int32 TubeSegments = 8; diff --git a/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp b/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp index 16bc331..c9301be 100644 --- a/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp +++ b/Unreal/Plugins/PS_Editor/Source/PS_Editor/UI/Widgets/PS_Editor_MainWidget.cpp @@ -144,7 +144,7 @@ TSharedRef UPS_Editor_MainWidget::RebuildWidget() { if (APS_Editor_PlayerController* EditorPC = Cast(GetOwningPlayer())) { - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing) + if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) { EditorPC->FinishSplineEditing(); } @@ -199,7 +199,7 @@ TSharedRef UPS_Editor_MainWidget::RebuildWidget() { if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) EditorPC->FinishPointPlacement(); - else if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplineEditing) + else if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) EditorPC->FinishSplineEditing(); } return FReply::Handled(); @@ -321,31 +321,6 @@ TSharedRef UPS_Editor_MainWidget::BuildToolbar() }) ] ] - // New Spline button - + SHorizontalBox::Slot().AutoWidth().Padding(8.0f, 0.0f, 2.0f, 0.0f) - [ - SNew(SButton) - .OnClicked_Lambda([this]() -> FReply - { - if (APS_Editor_PlayerController* EditorPC = Cast(GetOwningPlayer())) - { - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) - { - EditorPC->FinishPointPlacement(); - } - else - { - EditorPC->BeginPointPlacement(); - } - } - return FReply::Handled(); - }) - [ - SNew(STextBlock) - .Text(FText::FromString(TEXT("New Spline (S)"))) - .Font(FCoreStyle::GetDefaultFontStyle("Regular", 10)) - ] - ] ]; } @@ -784,73 +759,6 @@ void UPS_Editor_MainWidget::PopulateSpawnCatalog() ]; } - // ---- Tools section (built-in tools) ---- - SpawnListContainer->AddSlot() - .AutoHeight() - .Padding(0.0f, 10.0f, 0.0f, 2.0f) - [ - SNew(STextBlock) - .Text(FText::FromString(TEXT("Tools"))) - .Font(FCoreStyle::GetDefaultFontStyle("Bold", 9)) - .ColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f)) - ]; - - // Spline tool - SpawnListContainer->AddSlot() - .AutoHeight() - .Padding(4.0f, 4.0f) - [ - SNew(SButton) - .OnClicked_Lambda([this]() -> FReply - { - if (APS_Editor_PlayerController* EditorPC = Cast(GetOwningPlayer())) - { - if (EditorPC->GetEditorMode() == EPS_Editor_EditorMode::SplinePlacement) - { - EditorPC->FinishPointPlacement(); - } - else - { - // If a spline is selected, append to it - if (UPS_Editor_SelectionManager* SelMgr = SelectionManager.Get()) - { - for (const TWeakObjectPtr& Weak : SelMgr->GetSelectedActors()) - { - if (APS_Editor_SplineActor* Spline = Cast(Weak.Get())) - { - EditorPC->BeginPointPlacementAppend(Spline); - return FReply::Handled(); - } - } - } - EditorPC->BeginPointPlacement(); - } - } - return FReply::Handled(); - }) - [ - SNew(SBox) - .MinDesiredWidth(88.0f) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot().AutoHeight().HAlign(HAlign_Center) - [ - SNew(STextBlock) - .Text(FText::FromString(TEXT("~"))) - .Font(FCoreStyle::GetDefaultFontStyle("Bold", 28)) - .ColorAndOpacity(FLinearColor(1.0f, 0.4f, 0.0f)) - ] - + SVerticalBox::Slot().AutoHeight().HAlign(HAlign_Center).Padding(0.0f, 2.0f, 0.0f, 0.0f) - [ - SNew(STextBlock) - .Text(FText::FromString(TEXT("Spline"))) - .Font(FCoreStyle::GetDefaultFontStyle("Regular", 9)) - .Justification(ETextJustify::Center) - .ColorAndOpacity(FLinearColor::White) - ] - ] - ] - ]; } void UPS_Editor_MainWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) @@ -914,18 +822,6 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection() // Update help bar text and button visibility if (CurrentEditorMode == EPS_Editor_EditorMode::SplinePlacement) { - if (HelpBarText.IsValid()) - { - HelpBarText->SetText(FText::FromString(TEXT("SPLINE: LMB to add points | RMB or Enter to finish | Escape to cancel"))); - HelpBarText->SetColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f)); - } - if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed); - if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(EVisibility::Collapsed); - if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible); - } - else if (CurrentEditorMode == EPS_Editor_EditorMode::SplineEditing) - { - // Check if a specific point is selected (for Delete Point button) bool bPointSelected = false; if (APS_Editor_Pawn* Pawn = Cast(GetOwningPlayerPawn())) { @@ -936,11 +832,11 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection() { if (bPointSelected) { - HelpBarText->SetText(FText::FromString(TEXT("EDIT SPLINE: Drag gizmo to move point | Del: delete point | Click line to insert | Escape: done"))); + HelpBarText->SetText(FText::FromString(TEXT("SPLINE: Drag gizmo to move | Del: delete point | Click line: insert | Click outside: add | Esc/Done: finish"))); } else { - HelpBarText->SetText(FText::FromString(TEXT("EDIT SPLINE: Click handle to select point | Click line to insert | End: snap to ground | Escape: done"))); + HelpBarText->SetText(FText::FromString(TEXT("SPLINE: Click to add points | Click handle to select | Click line to insert | End: snap | Esc/Done: finish"))); } HelpBarText->SetColorAndOpacity(FLinearColor(1.0f, 0.6f, 0.0f)); }