IPS_Editor_SplineEditable interface, spline visibility, snap-to-ground, Play without BaseLevel

Refactor spline system to use IPS_Editor_SplineEditable interface:
- New interface in Spline/PS_Editor_SplineEditable.h with all editing methods
- APS_Editor_SplineActor and APS_BehaviorEditor_AISpline both implement it
- All ~40 Cast<APS_Editor_SplineActor> replaced with Cast<IPS_Editor_SplineEditable>
- UndoManager SplinePointAction uses TWeakObjectPtr<AActor> + interface cast
- Any actor implementing the interface works with full editor spline editing

Spline visibility:
- SetVisualizationVisible() hides tube/handles/cube in gameplay (data stays for AI)
- SceneLoader calls it when stripping editor components
- SplineMatOccluded material added (unused for now, PP occlusion planned)

Other improvements:
- Snap spline CP to ground on placement (downward raytrace)
- Play button works without BaseLevel (uses current editor map)
- AISpline triggers SplineNetwork::RebuildNetwork on BeginPlay (next tick)
- PS_BehaviorEditor marked EnabledByDefault: false (only active in PROSERVE)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 18:05:53 +02:00
parent 21e39ad3fa
commit 24e50ab919
15 changed files with 290 additions and 130 deletions

View File

@@ -6,6 +6,7 @@
"Description": "Bridge plugin: AI Behavior splines editable via PS_Editor runtime editor.",
"Category": "PS Tools",
"CreatedBy": "Asterion",
"EnabledByDefault": false,
"Modules": [
{
"Name": "PS_BehaviorEditor",

View File

@@ -6,6 +6,7 @@
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "PS_AI_Behavior_SplineNetwork.h"
APS_BehaviorEditor_AISpline::APS_BehaviorEditor_AISpline()
{
@@ -82,6 +83,9 @@ void APS_BehaviorEditor_AISpline::BeginPlay()
CenterCubeMat = UMaterialInstanceDynamic::Create(GizmoMat, this);
CenterCubeMat->SetVectorParameterValue(TEXT("Color"), SplineColor);
SplineMatOccluded = UMaterialInstanceDynamic::Create(GizmoMat, this);
SplineMatOccluded->SetVectorParameterValue(TEXT("Color"), FLinearColor(0.3f, 0.3f, 0.3f));
}
// Center cube (selection handle)
@@ -102,6 +106,18 @@ void APS_BehaviorEditor_AISpline::BeginPlay()
{
CenterCube->SetMaterial(0, CenterCubeMat);
}
// Rebuild SplineNetwork next tick (all splines from scene load will be ready)
GetWorld()->GetTimerManager().SetTimerForNextTick([WeakWorld = TWeakObjectPtr<UWorld>(GetWorld())]()
{
if (UWorld* World = WeakWorld.Get())
{
if (UPS_AI_Behavior_SplineNetwork* Network = World->GetSubsystem<UPS_AI_Behavior_SplineNetwork>())
{
Network->RebuildNetwork();
}
}
});
}
// ---- Point manipulation ----
@@ -406,6 +422,23 @@ void APS_BehaviorEditor_AISpline::ClearHandleHighlight()
}
}
// ---- Visibility ----
void APS_BehaviorEditor_AISpline::SetVisualizationVisible(bool bVisible)
{
if (SplineMeshComp) SplineMeshComp->SetVisibility(bVisible);
if (CenterCube) CenterCube->SetVisibility(bVisible);
if (HandleRoot) HandleRoot->SetVisibility(bVisible, true);
for (UStaticMeshComponent* Handle : PointHandles)
{
if (Handle)
{
Handle->SetVisibility(bVisible);
Handle->SetCollisionEnabled(bVisible ? ECollisionEnabled::QueryOnly : ECollisionEnabled::NoCollision);
}
}
}
// ---- Serialization ----
void APS_BehaviorEditor_AISpline::ExportToCustomProperties(TMap<FString, FString>& OutProperties) const

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h"
#include "PS_AI_Behavior_SplinePath.h"
#include "PS_Editor_PointPlaceable.h"
#include "PS_Editor_SplineEditable.h"
#include "PS_BehaviorEditor_AISpline.generated.h"
class UProceduralMeshComponent;
@@ -19,7 +20,7 @@ class UPS_Editor_EditableComponent;
* Includes tube visualization + handles from PS_Editor's spline system.
*/
UCLASS(BlueprintType, Blueprintable, meta = (DisplayName = "AI Spline (Editor)"))
class PS_BEHAVIOREDITOR_API APS_BehaviorEditor_AISpline : public APS_AI_Behavior_SplinePath, public IPS_Editor_PointPlaceable
class PS_BEHAVIOREDITOR_API APS_BehaviorEditor_AISpline : public APS_AI_Behavior_SplinePath, public IPS_Editor_PointPlaceable, public IPS_Editor_SplineEditable
{
GENERATED_BODY()
@@ -39,40 +40,46 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spline|Visual")
FLinearColor SplineSelectedColor = FLinearColor(0.4f, 0.8f, 1.0f);
// ---- Point manipulation ----
// ---- Point manipulation (IPS_Editor_SplineEditable) ----
void AddPoint(const FVector& WorldPosition);
void MovePoint(int32 Index, const FVector& NewWorldPosition);
void RemovePoint(int32 Index);
int32 GetNumPoints() const;
FVector GetPointLocation(int32 Index) const;
TArray<FVector> GetAllPointLocations() const;
void SetAllPointLocations(const TArray<FVector>& Points);
void InsertPoint(int32 Index, const FVector& WorldPosition);
int32 FindInsertIndex(const FVector& WorldLocation) const;
FVector GetCentroid() const;
virtual void AddPoint(const FVector& WorldPosition) override;
virtual void MovePoint(int32 Index, const FVector& NewWorldPosition) override;
virtual void RemovePoint(int32 Index) override;
virtual int32 GetNumPoints() const override;
virtual FVector GetPointLocation(int32 Index) const override;
virtual TArray<FVector> GetAllPointLocations() const override;
virtual void SetAllPointLocations(const TArray<FVector>& Points) override;
virtual void InsertPoint(int32 Index, const FVector& WorldPosition) override;
virtual int32 FindInsertIndex(const FVector& WorldLocation) const override;
virtual FVector GetCentroid() const override;
// ---- Visualization ----
// ---- Visualization (IPS_Editor_SplineEditable) ----
void UpdateVisualization();
virtual void UpdateVisualization() override;
UFUNCTION(BlueprintCallable, Category = "PS Editor|Spline")
void UpdateSplineMaterials();
virtual void UpdateSplineMaterials() override;
// ---- Handle interaction ----
// ---- Handle interaction (IPS_Editor_SplineEditable) ----
int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const;
void SetHandlesVisible(bool bVisible);
void SetSelected(bool bSelected);
bool IsCenterCube(const UPrimitiveComponent* Comp) const;
bool IsSplineMesh(const UPrimitiveComponent* Comp) const;
void HighlightHandle(int32 Index);
void ClearHandleHighlight();
virtual int32 GetHandleIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection, float SphereRadius = 15.0f) const override;
virtual void SetHandlesVisible(bool bVisible) override;
virtual void SetSelected(bool bSelected) override;
virtual bool IsCenterCube(const UPrimitiveComponent* Comp) const override;
virtual bool IsSplineMesh(const UPrimitiveComponent* Comp) const override;
virtual void HighlightHandle(int32 Index) override;
virtual void ClearHandleHighlight() override;
// ---- Serialization ----
// ---- Visibility (IPS_Editor_SplineEditable) ----
virtual void SetVisualizationVisible(bool bVisible) override;
void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const;
void ImportFromCustomProperties(const TMap<FString, FString>& Properties);
// ---- Serialization (IPS_Editor_SplineEditable) ----
virtual void ExportToCustomProperties(TMap<FString, FString>& OutProperties) const override;
virtual void ImportFromCustomProperties(const TMap<FString, FString>& Properties) override;
// ---- Access (IPS_Editor_SplineEditable) ----
virtual USplineComponent* GetSplineComponent() const override { return SplineComp; }
protected:
virtual void BeginPlay() override;
@@ -95,6 +102,8 @@ private:
TObjectPtr<UMaterialInstanceDynamic> HandleMat;
UPROPERTY()
TObjectPtr<UMaterialInstanceDynamic> HandleHighlightMat;
UPROPERTY()
TObjectPtr<UMaterialInstanceDynamic> SplineMatOccluded;
UPROPERTY()
TObjectPtr<UStaticMeshComponent> CenterCube;