ChildMovable: rotation support + ChildMovable-aware help bar + clear previous highlight
- IPS_Editor_ChildMovable gains GetChildRotation / RotateChild and the
GetAll/SetAll rotation pair for undo snapshots. BP impls can clamp axes
(e.g. Yaw-only) inside RotateChild.
- Pawn captures initial rotations at gizmo drag start and applies the
rotate gizmo's delta-quat onto the active child only (no longer
rotating every selected actor when a child handle is active).
- FPS_Editor_SplinePointAction carries optional Old/NewRotations arrays
applied alongside positions on Undo/Redo for ChildMovable actors.
- Pawn now calls ClearChildHighlight before HighlightChild so picking a
new child no longer leaves the previous one stuck in highlight state.
- MainWidget + Legacy widget detect when ActivePlacementActor is a
ChildMovable in SplinePlacement mode and swap the help bar text
("CHILDREN: ...") + hide the irrelevant Delete-point button.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -448,15 +448,31 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|||||||
const FQuat RotationQuat(AxisDir, FMath::DegreesToRadians(SnapAngle));
|
const FQuat RotationQuat(AxisDir, FMath::DegreesToRadians(SnapAngle));
|
||||||
const FVector GizmoLoc = GizmoDragPlaneOrigin;
|
const FVector GizmoLoc = GizmoDragPlaneOrigin;
|
||||||
|
|
||||||
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
// ChildMovable rotation: rotate only the active child instead of all selected actors.
|
||||||
|
// We compose the delta quat onto the initial rotation captured at drag start —
|
||||||
|
// reading the live value each frame would feed back through GetChildRotation and
|
||||||
|
// drift if the BP impl clamps axes.
|
||||||
|
if (ActiveSplinePointIndex >= 0
|
||||||
|
&& DraggedSplineActor.IsValid()
|
||||||
|
&& DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass())
|
||||||
|
&& SplinePointDragInitialRotations.IsValidIndex(ActiveSplinePointIndex))
|
||||||
{
|
{
|
||||||
if (AActor* Actor = Selected[i].Get())
|
const FQuat OrigRot = SplinePointDragInitialRotations[ActiveSplinePointIndex].Quaternion();
|
||||||
|
const FRotator NewRot = (RotationQuat * OrigRot).Rotator();
|
||||||
|
IPS_Editor_ChildMovable::Execute_RotateChild(DraggedSplineActor.Get(), ActiveSplinePointIndex, NewRot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
||||||
{
|
{
|
||||||
const FVector Offset = GizmoDragInitialTransforms[i].GetLocation() - GizmoLoc;
|
if (AActor* Actor = Selected[i].Get())
|
||||||
Actor->SetActorLocation(GizmoLoc + RotationQuat.RotateVector(Offset));
|
{
|
||||||
|
const FVector Offset = GizmoDragInitialTransforms[i].GetLocation() - GizmoLoc;
|
||||||
|
Actor->SetActorLocation(GizmoLoc + RotationQuat.RotateVector(Offset));
|
||||||
|
|
||||||
const FQuat OrigRot = GizmoDragInitialTransforms[i].GetRotation();
|
const FQuat OrigRot = GizmoDragInitialTransforms[i].GetRotation();
|
||||||
Actor->SetActorRotation((RotationQuat * OrigRot).Rotator());
|
Actor->SetActorRotation((RotationQuat * OrigRot).Rotator());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -680,6 +696,9 @@ void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
|
|||||||
{
|
{
|
||||||
ActiveSplinePointIndex = ChildIdx;
|
ActiveSplinePointIndex = ChildIdx;
|
||||||
DraggedSplineActor = ActiveActor;
|
DraggedSplineActor = ActiveActor;
|
||||||
|
// Clear any previous highlight before applying the new one — without this, picking
|
||||||
|
// a different child leaves the prior one stuck in highlight state.
|
||||||
|
IPS_Editor_ChildMovable::Execute_ClearChildHighlight(ActiveActor);
|
||||||
IPS_Editor_ChildMovable::Execute_HighlightChild(ActiveActor, ChildIdx);
|
IPS_Editor_ChildMovable::Execute_HighlightChild(ActiveActor, ChildIdx);
|
||||||
|
|
||||||
if (UPS_Editor_SelectionManager* SM = EdPC_CM->GetSelectionManager())
|
if (UPS_Editor_SelectionManager* SM = EdPC_CM->GetSelectionManager())
|
||||||
@@ -849,6 +868,7 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
if (ActiveSplinePointIndex >= 0 && DraggedSplineActor.IsValid())
|
||||||
{
|
{
|
||||||
TArray<FVector> NewPoints;
|
TArray<FVector> NewPoints;
|
||||||
|
TArray<FRotator> NewRotations;
|
||||||
FString Desc;
|
FString Desc;
|
||||||
|
|
||||||
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
if (IPS_Editor_SplineEditable* SplineActor = Cast<IPS_Editor_SplineEditable>(DraggedSplineActor.Get()))
|
||||||
@@ -859,7 +879,16 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
{
|
{
|
||||||
NewPoints = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(DraggedSplineActor.Get());
|
NewPoints = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(DraggedSplineActor.Get());
|
||||||
Desc = TEXT("Move child element");
|
// Snapshot rotations so the undo system restores both translation and rotation.
|
||||||
|
// The arrays may be empty if the BP didn't implement rotation — that's fine,
|
||||||
|
// the undo Execute/Undo only applies when both old and new are non-empty.
|
||||||
|
NewRotations = IPS_Editor_ChildMovable::Execute_GetAllChildRotations(DraggedSplineActor.Get());
|
||||||
|
bool bIsRotate = false;
|
||||||
|
if (UPS_Editor_SelectionManager* SMDesc = EditorPC->GetSelectionManager())
|
||||||
|
{
|
||||||
|
bIsRotate = (SMDesc->GetTransformMode() == EPS_Editor_TransformMode::Rotate);
|
||||||
|
}
|
||||||
|
Desc = bIsRotate ? TEXT("Rotate child element") : TEXT("Move child element");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NewPoints.Num() > 0)
|
if (NewPoints.Num() > 0)
|
||||||
@@ -868,6 +897,8 @@ void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
|
|||||||
Action->SplineActor = DraggedSplineActor.Get();
|
Action->SplineActor = DraggedSplineActor.Get();
|
||||||
Action->OldPoints = SplinePointDragInitialState;
|
Action->OldPoints = SplinePointDragInitialState;
|
||||||
Action->NewPoints = NewPoints;
|
Action->NewPoints = NewPoints;
|
||||||
|
Action->OldRotations = SplinePointDragInitialRotations;
|
||||||
|
Action->NewRotations = NewRotations;
|
||||||
Action->Description = Desc;
|
Action->Description = Desc;
|
||||||
UM->RecordAction(Action);
|
UM->RecordAction(Action);
|
||||||
}
|
}
|
||||||
@@ -1523,6 +1554,7 @@ void APS_Editor_Pawn::BeginGizmoDrag(EPS_Editor_GizmoAxis Axis)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store initial sub-element state if editing a point/child
|
// Store initial sub-element state if editing a point/child
|
||||||
|
SplinePointDragInitialRotations.Reset();
|
||||||
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()))
|
||||||
@@ -1532,6 +1564,9 @@ void APS_Editor_Pawn::BeginGizmoDrag(EPS_Editor_GizmoAxis Axis)
|
|||||||
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
else if (DraggedSplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
{
|
{
|
||||||
SplinePointDragInitialState = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(DraggedSplineActor.Get());
|
SplinePointDragInitialState = IPS_Editor_ChildMovable::Execute_GetAllChildLocations(DraggedSplineActor.Get());
|
||||||
|
// Snapshot rotations too so the rotate gizmo can compute deltas from a fixed
|
||||||
|
// reference and the undo system can restore them.
|
||||||
|
SplinePointDragInitialRotations = IPS_Editor_ChildMovable::Execute_GetAllChildRotations(DraggedSplineActor.Get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,9 +209,12 @@ private:
|
|||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UInputAction> IA_Cancel;
|
TObjectPtr<UInputAction> IA_Cancel;
|
||||||
|
|
||||||
// ---- Spline point drag state ----
|
// ---- Spline point / ChildMovable drag state ----
|
||||||
TWeakObjectPtr<AActor> DraggedSplineActor;
|
TWeakObjectPtr<AActor> DraggedSplineActor;
|
||||||
TArray<FVector> SplinePointDragInitialState;
|
TArray<FVector> SplinePointDragInitialState;
|
||||||
|
// Initial rotations captured at drag start when dragging a ChildMovable sub-element.
|
||||||
|
// Empty for spline points (which do not have a rotation concept).
|
||||||
|
TArray<FRotator> SplinePointDragInitialRotations;
|
||||||
FVector SplinePointDragPlaneOrigin;
|
FVector SplinePointDragPlaneOrigin;
|
||||||
FVector SplinePointDragPlaneNormal;
|
FVector SplinePointDragPlaneNormal;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ void FPS_Editor_SplinePointAction::Execute()
|
|||||||
else if (SplineActor.IsValid() && SplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
else if (SplineActor.IsValid() && SplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
{
|
{
|
||||||
IPS_Editor_ChildMovable::Execute_SetAllChildLocations(SplineActor.Get(), NewPoints);
|
IPS_Editor_ChildMovable::Execute_SetAllChildLocations(SplineActor.Get(), NewPoints);
|
||||||
|
// Apply rotations only if the BP captured them (otherwise we'd reset to identity).
|
||||||
|
if (NewRotations.Num() > 0)
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_SetAllChildRotations(SplineActor.Get(), NewRotations);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,6 +30,10 @@ void FPS_Editor_SplinePointAction::Undo()
|
|||||||
else if (SplineActor.IsValid() && SplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
else if (SplineActor.IsValid() && SplineActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass()))
|
||||||
{
|
{
|
||||||
IPS_Editor_ChildMovable::Execute_SetAllChildLocations(SplineActor.Get(), OldPoints);
|
IPS_Editor_ChildMovable::Execute_SetAllChildLocations(SplineActor.Get(), OldPoints);
|
||||||
|
if (OldRotations.Num() > 0)
|
||||||
|
{
|
||||||
|
IPS_Editor_ChildMovable::Execute_SetAllChildRotations(SplineActor.Get(), OldRotations);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -170,6 +170,11 @@ struct FPS_Editor_SplinePointAction : public FPS_Editor_Action
|
|||||||
TWeakObjectPtr<AActor> SplineActor;
|
TWeakObjectPtr<AActor> SplineActor;
|
||||||
TArray<FVector> OldPoints;
|
TArray<FVector> OldPoints;
|
||||||
TArray<FVector> NewPoints;
|
TArray<FVector> NewPoints;
|
||||||
|
// ChildMovable-only: rotation snapshots. Empty for spline points and for ChildMovable BPs
|
||||||
|
// that don't implement rotation. Execute/Undo only apply rotations when both arrays match
|
||||||
|
// the points array length.
|
||||||
|
TArray<FRotator> OldRotations;
|
||||||
|
TArray<FRotator> NewRotations;
|
||||||
FString Description;
|
FString Description;
|
||||||
|
|
||||||
virtual void Execute() override;
|
virtual void Execute() override;
|
||||||
|
|||||||
@@ -44,6 +44,26 @@ public:
|
|||||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
void SetAllChildLocations(const TArray<FVector>& Positions);
|
void SetAllChildLocations(const TArray<FVector>& Positions);
|
||||||
|
|
||||||
|
/** World rotation of the sub-element at the given index. Implement to constrain the
|
||||||
|
* rotation axes you actually expose (e.g. yaw only) — the plugin gizmo writes whatever
|
||||||
|
* the user drags, so any clamping must happen in RotateChild on the BP side. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
FRotator GetChildRotation(int32 Index) const;
|
||||||
|
|
||||||
|
/** Apply a new world rotation to the sub-element at the given index. The BP impl is the
|
||||||
|
* right place to drop unsupported axes (e.g. keep only Yaw, force Pitch/Roll to zero). */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void RotateChild(int32 Index, const FRotator& NewWorldRotation);
|
||||||
|
|
||||||
|
/** Return all sub-element rotations (used for undo snapshots). Empty array allowed if
|
||||||
|
* rotation is not supported on this actor — the undo system then ignores rotation. */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
TArray<FRotator> GetAllChildRotations() const;
|
||||||
|
|
||||||
|
/** Restore all sub-element rotations (used for undo). */
|
||||||
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
void SetAllChildRotations(const TArray<FRotator>& Rotations);
|
||||||
|
|
||||||
/** Index of the sub-element under the cursor ray (-1 if none).
|
/** 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. */
|
* Implement with sphere-ray intersection or any hit detection that suits your visuals. */
|
||||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS Editor|Child Movable")
|
||||||
|
|||||||
@@ -1821,9 +1821,33 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
bPointSelected = (Pawn->ActiveSplinePointIndex >= 0);
|
bPointSelected = (Pawn->ActiveSplinePointIndex >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SplinePlacement mode is shared between IPS_Editor_SplineEditable and
|
||||||
|
// IPS_Editor_ChildMovable — show ChildMovable-specific help when the active
|
||||||
|
// placement actor implements the child interface (no point insertion, no Del,
|
||||||
|
// rotation gizmo available via R key).
|
||||||
|
bool bChildMovableActive = false;
|
||||||
|
if (APS_Editor_PlayerController* EditorPC2 = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||||
|
{
|
||||||
|
if (AActor* ActiveActor = EditorPC2->GetActivePlacementActor())
|
||||||
|
{
|
||||||
|
bChildMovableActive = ActiveActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (HelpBarText.IsValid())
|
if (HelpBarText.IsValid())
|
||||||
{
|
{
|
||||||
if (bPointSelected)
|
if (bChildMovableActive)
|
||||||
|
{
|
||||||
|
if (bPointSelected)
|
||||||
|
{
|
||||||
|
HelpBarText->SetText(FText::FromString(TEXT("CHILDREN: Drag gizmo to move | R: rotate | T: translate | Esc/Done: finish")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HelpBarText->SetText(FText::FromString(TEXT("CHILDREN: Click a child to select | Esc/Done: finish")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bPointSelected)
|
||||||
{
|
{
|
||||||
HelpBarText->SetText(FText::FromString(TEXT("SPLINE: Drag gizmo to move | Del: delete point | Click line: insert | Click outside: add | Esc/Done: finish")));
|
HelpBarText->SetText(FText::FromString(TEXT("SPLINE: Drag gizmo to move | Del: delete point | Click line: insert | Click outside: add | Esc/Done: finish")));
|
||||||
}
|
}
|
||||||
@@ -1835,7 +1859,8 @@ void UPS_Editor_MainWidget::RefreshPropertiesFromSelection()
|
|||||||
}
|
}
|
||||||
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Collapsed);
|
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(bPointSelected ? EVisibility::Visible : EVisibility::Collapsed);
|
// Delete-point button is meaningless for ChildMovable (fixed child set)
|
||||||
|
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility((bPointSelected && !bChildMovableActive) ? EVisibility::Visible : EVisibility::Collapsed);
|
||||||
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible);
|
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible);
|
||||||
}
|
}
|
||||||
else if (bChildMovableSelected)
|
else if (bChildMovableSelected)
|
||||||
|
|||||||
@@ -1573,9 +1573,30 @@ void UPS_Editor_MainWidget_Legacy::RefreshPropertiesFromSelection()
|
|||||||
bPointSelected = (Pawn->ActiveSplinePointIndex >= 0);
|
bPointSelected = (Pawn->ActiveSplinePointIndex >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SplinePlacement mode is shared with IPS_Editor_ChildMovable — show the right help.
|
||||||
|
bool bChildMovableActive = false;
|
||||||
|
if (APS_Editor_PlayerController* EditorPC2 = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||||
|
{
|
||||||
|
if (AActor* ActiveActor = EditorPC2->GetActivePlacementActor())
|
||||||
|
{
|
||||||
|
bChildMovableActive = ActiveActor->GetClass()->ImplementsInterface(UPS_Editor_ChildMovable::StaticClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (HelpBarText.IsValid())
|
if (HelpBarText.IsValid())
|
||||||
{
|
{
|
||||||
if (bPointSelected)
|
if (bChildMovableActive)
|
||||||
|
{
|
||||||
|
if (bPointSelected)
|
||||||
|
{
|
||||||
|
HelpBarText->SetText(FText::FromString(TEXT("CHILDREN: Drag gizmo to move | R: rotate | T: translate | Esc/Done: finish")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HelpBarText->SetText(FText::FromString(TEXT("CHILDREN: Click a child to select | Esc/Done: finish")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bPointSelected)
|
||||||
{
|
{
|
||||||
HelpBarText->SetText(FText::FromString(TEXT("SPLINE: Drag gizmo to move | Del: delete point | Click line: insert | Click outside: add | Esc/Done: finish")));
|
HelpBarText->SetText(FText::FromString(TEXT("SPLINE: Drag gizmo to move | Del: delete point | Click line: insert | Click outside: add | Esc/Done: finish")));
|
||||||
}
|
}
|
||||||
@@ -1587,7 +1608,8 @@ void UPS_Editor_MainWidget_Legacy::RefreshPropertiesFromSelection()
|
|||||||
}
|
}
|
||||||
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
if (SplineEditButton.IsValid()) SplineEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Collapsed);
|
if (ChildEditButton.IsValid()) ChildEditButton->SetVisibility(EVisibility::Collapsed);
|
||||||
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility(bPointSelected ? EVisibility::Visible : EVisibility::Collapsed);
|
// Delete-point is meaningless for ChildMovable (fixed child set)
|
||||||
|
if (SplineDeletePointButton.IsValid()) SplineDeletePointButton->SetVisibility((bPointSelected && !bChildMovableActive) ? EVisibility::Visible : EVisibility::Collapsed);
|
||||||
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible);
|
if (SplineFinishButton.IsValid()) SplineFinishButton->SetVisibility(EVisibility::Visible);
|
||||||
}
|
}
|
||||||
else if (bChildMovableSelected)
|
else if (bChildMovableSelected)
|
||||||
|
|||||||
Reference in New Issue
Block a user