Files
UnrealEngine/Engine/Plugins/Experimental/SceneState/Source/SceneStateMachineGraph/Private/Nodes/SceneStateMachineNode.cpp
2025-05-18 13:04:45 +08:00

241 lines
5.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Nodes/SceneStateMachineNode.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Nodes/SceneStateMachineTransitionNode.h"
#include "SceneStateMachineGraph.h"
#include "SceneStateMachineGraphSchema.h"
#include "SceneStateMachineGraphUtils.h"
#include "SceneStateMachineNodeNameValidator.h"
TArray<USceneStateMachineTransitionNode*> USceneStateMachineNode::GatherTransitions(bool bInSortList) const
{
TArray<USceneStateMachineTransitionNode*> Transitions;
UEdGraphPin* OutputPin = GetOutputPin();
check(OutputPin);
// Worst case: All output links are transitions
Transitions.Reserve(OutputPin->LinkedTo.Num());
// Normal transitions (can only go out)
for (UEdGraphPin* Link : OutputPin->LinkedTo)
{
check(Link);
if (USceneStateMachineTransitionNode* Transition = Cast<USceneStateMachineTransitionNode>(Link->GetOwningNode()))
{
checkSlow(Transition->GetSourceNode() == this);
Transitions.Add(Transition);
}
}
// Sort the transitions by priority order, lower numbers are higher priority
if (bInSortList)
{
Transitions.StableSort(
[](const USceneStateMachineTransitionNode& A, const USceneStateMachineTransitionNode& B)
{
return A.GetPriority() < B.GetPriority();
});
}
return Transitions;
}
UEdGraph* USceneStateMachineNode::GetBoundGraph() const
{
if (BoundGraphs.IsEmpty())
{
return nullptr;
}
return BoundGraphs[0];
}
bool USceneStateMachineNode::ConditionallyCreateBoundGraph()
{
CleanInvalidBoundGraphs();
// Don't create bound graph if there's already an existing valid bound graph
if (!BoundGraphs.IsEmpty())
{
return false;
}
UEdGraph* BoundGraph = CreateBoundGraphInternal();
if (!BoundGraph)
{
return false;
}
BoundGraphs.Add(BoundGraph);
// Initialize the Graph
const UEdGraphSchema* Schema = BoundGraph->GetSchema();
check(Schema);
Schema->CreateDefaultNodesForGraph(*BoundGraph);
// Add the new graph as a child of our parent graph
UEdGraph* ParentGraph = GetGraph();
check(ParentGraph);
ParentGraph->SubGraphs.AddUnique(BoundGraph);
return true;
}
void USceneStateMachineNode::CleanInvalidBoundGraphs()
{
BoundGraphs.RemoveAll(
[This = this](UEdGraph* InBoundGraph)
{
// Remove nulls
if (!InBoundGraph)
{
return true;
}
// Remove graphs that aren't outered to this node
if (InBoundGraph->GetOuter() != This)
{
if (UEdGraph* ParentGraph = InBoundGraph->GetTypedOuter<UEdGraph>())
{
ParentGraph->SubGraphs.Remove(InBoundGraph);
}
return true;
}
return false;
});
}
void USceneStateMachineNode::PostPasteNode()
{
Super::PostPasteNode();
CleanInvalidBoundGraphs();
for (UEdGraph* BoundGraph : BoundGraphs)
{
for (UEdGraphNode* GraphNode : BoundGraph->Nodes)
{
GraphNode->CreateNewGuid();
GraphNode->PostPasteNode();
GraphNode->ReconstructNode();
}
// Add the new graph as a child of our parent graph
if (UEdGraph* ParentGraph = GetGraph())
{
ParentGraph->SubGraphs.AddUnique(BoundGraph);
}
// Restore transactional flag that is lost during copy/paste process
BoundGraph->SetFlags(RF_Transactional);
}
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
}
void USceneStateMachineNode::OnRenameNode(const FString& InNodeName)
{
NodeName = *InNodeName;
}
void USceneStateMachineNode::AutowireNewNode(UEdGraphPin* InSourcePin)
{
if (!InSourcePin)
{
return;
}
UEdGraphPin* const InputPin = GetInputPin();
if (!InputPin)
{
return;
}
const UEdGraphSchema* Schema = GetSchema();
if (Schema && Schema->TryCreateConnection(InSourcePin, InputPin))
{
NodeConnectionListChanged();
}
}
FText USceneStateMachineNode::GetNodeTitle(ENodeTitleType::Type InTitleType) const
{
return FText::FromName(NodeName);
}
void USceneStateMachineNode::DestroyNode()
{
Super::DestroyNode();
for (UEdGraph* BoundGraph : BoundGraphs)
{
UE::SceneState::Graph::RemoveGraph(BoundGraph);
}
BoundGraphs.Reset();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
}
TArray<UEdGraph*> USceneStateMachineNode::GetSubGraphs() const
{
return BoundGraphs;
}
UObject* USceneStateMachineNode::GetJumpTargetForDoubleClick() const
{
return !BoundGraphs.IsEmpty() ? BoundGraphs[0] : nullptr;
}
bool USceneStateMachineNode::CanJumpToDefinition() const
{
return !!GetJumpTargetForDoubleClick();
}
void USceneStateMachineNode::JumpToDefinition() const
{
if (UObject* HyperlinkTarget = GetJumpTargetForDoubleClick())
{
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(HyperlinkTarget);
}
}
bool USceneStateMachineNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* InSchema) const
{
check(InSchema);
return InSchema->IsA<USceneStateMachineGraphSchema>();
}
TSharedPtr<INameValidatorInterface> USceneStateMachineNode::MakeNameValidator() const
{
return MakeShared<UE::SceneState::Graph::FStateMachineNodeNameValidator>(this);
}
void USceneStateMachineNode::PostLoad()
{
Super::PostLoad();
CleanInvalidBoundGraphs();
}
void USceneStateMachineNode::HidePins(TConstArrayView<FName> InPinNames)
{
for (FName PinName : InPinNames)
{
if (UEdGraphPin* Pin = FindPin(PinName))
{
Pin->bHidden = true;
}
}
}
void USceneStateMachineNode::GenerateNewNodeName()
{
const TSharedRef<INameValidatorInterface> NameValidator = FNameValidatorFactory::MakeValidator(this).ToSharedRef();
FString NewNodeName = NodeName.ToString();
NameValidator->FindValidString(NewNodeName);
NodeName = *NewNodeName;
}