Files
UnrealEngine/Engine/Source/Editor/AnimGraph/Private/AnimStateNodeBase.cpp
2025-05-18 13:04:45 +08:00

223 lines
6.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
AnimStateNodeBase.cpp
=============================================================================*/
#include "AnimStateNodeBase.h"
#include "UObject/FrameworkObjectVersion.h"
#include "Animation/AnimBlueprint.h"
#include "Animation/AnimClassInterface.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "AnimationStateMachineGraph.h"
#include "AnimationStateMachineSchema.h"
#include "Kismet2/Kismet2NameValidators.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "AnimStateTransitionNode.h"
/////////////////////////////////////////////////////
// FAnimStateNodeNameValidator
class FAnimStateNodeNameValidator : public FStringSetNameValidator
{
public:
FAnimStateNodeNameValidator(const UAnimStateNodeBase* InStateNode)
: FStringSetNameValidator(FString())
{
Names.Add(TEXT("None"));
TArray<UAnimStateNodeBase*> Nodes;
UAnimationStateMachineGraph* StateMachine = CastChecked<UAnimationStateMachineGraph>(InStateNode->GetOuter());
StateMachine->GetNodesOfClass<UAnimStateNodeBase>(Nodes);
for (auto NodeIt = Nodes.CreateIterator(); NodeIt; ++NodeIt)
{
auto Node = *NodeIt;
if (Node != InStateNode)
{
Names.Add(Node->GetStateName());
}
}
// Include the name of animation layers
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(StateMachine);
if (Blueprint)
{
UClass* TargetClass = *Blueprint->SkeletonGeneratedClass;
if (TargetClass)
{
IAnimClassInterface* AnimClassInterface = IAnimClassInterface::GetFromClass(TargetClass);
for (const FAnimBlueprintFunction& AnimBlueprintFunction : AnimClassInterface->GetAnimBlueprintFunctions())
{
if (AnimBlueprintFunction.Name != UEdGraphSchema_K2::GN_AnimGraph)
{
Names.Add(AnimBlueprintFunction.Name.ToString());
}
}
}
}
}
};
/////////////////////////////////////////////////////
// UAnimStateNodeBase
UAnimStateNodeBase::UAnimStateNodeBase(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UAnimStateNodeBase::PostPasteNode()
{
Super::PostPasteNode();
for(UEdGraph* SubGraph : GetSubGraphs())
{
if(SubGraph)
{
// Add the new graph as a child of our parent graph
UEdGraph* ParentGraph = GetGraph();
if(ParentGraph->SubGraphs.Find(SubGraph) == INDEX_NONE)
{
ParentGraph->SubGraphs.Add(SubGraph);
}
for (UEdGraphNode* SubGraphNode : SubGraph->Nodes)
{
SubGraphNode->CreateNewGuid();
SubGraphNode->PostPasteNode();
SubGraphNode->ReconstructNode();
}
// Restore transactional flag that is lost during copy/paste process
SubGraph->SetFlags(RF_Transactional);
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph);
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
}
}
}
UObject* UAnimStateNodeBase::GetJumpTargetForDoubleClick() const
{
TArray<UEdGraph*> SubGraphs = GetSubGraphs();
check(SubGraphs.Num() > 0);
return SubGraphs[0];
}
bool UAnimStateNodeBase::CanJumpToDefinition() const
{
return GetJumpTargetForDoubleClick() != nullptr;
}
void UAnimStateNodeBase::JumpToDefinition() const
{
if (UObject* HyperlinkTarget = GetJumpTargetForDoubleClick())
{
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(HyperlinkTarget);
}
}
bool UAnimStateNodeBase::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const
{
return Schema->IsA(UAnimationStateMachineSchema::StaticClass());
}
void UAnimStateNodeBase::OnRenameNode(const FString& NewName)
{
TArray<UEdGraph*> SubGraphs = GetSubGraphs();
check(SubGraphs.Num() > 0);
FBlueprintEditorUtils::RenameGraph(SubGraphs[0], NewName);
}
TSharedPtr<class INameValidatorInterface> UAnimStateNodeBase::MakeNameValidator() const
{
return MakeShareable(new FAnimStateNodeNameValidator(this));
}
FString UAnimStateNodeBase::GetDocumentationLink() const
{
return TEXT("Shared/GraphNodes/AnimationStateMachine");
}
void UAnimStateNodeBase::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar.UsingCustomVersion(FFrameworkObjectVersion::GUID);
}
void UAnimStateNodeBase::PostLoad()
{
Super::PostLoad();
const int32 CustomVersion = GetLinkerCustomVersion(FFrameworkObjectVersion::GUID);
if(CustomVersion < FFrameworkObjectVersion::FixNonTransactionalPins)
{
int32 BrokenPinCount = 0;
for(UEdGraphPin_Deprecated* Pin : DeprecatedPins)
{
if(!Pin->HasAnyFlags(RF_Transactional))
{
++BrokenPinCount;
Pin->SetFlags(Pin->GetFlags() | RF_Transactional);
}
}
if(BrokenPinCount > 0)
{
UE_LOG(LogAnimation, Log, TEXT("Fixed %d non-transactional pins in %s"), BrokenPinCount, *GetName());
}
}
}
void UAnimStateNodeBase::GetTransitionList(TArray<UAnimStateTransitionNode*>& OutTransitions, bool bWantSortedList /*= false*/) const
{
// Normal transitions
for (int32 LinkIndex = 0; LinkIndex < Pins[1]->LinkedTo.Num(); ++LinkIndex)
{
UEdGraphNode* TargetNode = Pins[1]->LinkedTo[LinkIndex]->GetOwningNode();
if (UAnimStateTransitionNode* Transition = Cast<UAnimStateTransitionNode>(TargetNode))
{
OutTransitions.Add(Transition);
}
}
// Bidirectional transitions where we are the 'backwards' link.
// Conduits and other states types that don't support bidirectional transitions should hide it from the details panel.
for (int32 LinkIndex = 0; LinkIndex < Pins[0]->LinkedTo.Num(); ++LinkIndex)
{
UEdGraphNode* TargetNode = Pins[0]->LinkedTo[LinkIndex]->GetOwningNode();
if (UAnimStateTransitionNode* Transition = Cast<UAnimStateTransitionNode>(TargetNode))
{
// Anim state nodes that don't support bidirectional transitions should hide this property in FAnimTransitionNodeDetails::CustomizeDetails
if (Transition->Bidirectional)
{
OutTransitions.Add(Transition);
}
}
}
// Sort the transitions by priority order, lower numbers are higher priority
if (bWantSortedList)
{
struct FCompareTransitionsByPriority
{
FORCEINLINE bool operator()(const UAnimStateTransitionNode& A, const UAnimStateTransitionNode& B) const
{
return A.PriorityOrder < B.PriorityOrder;
}
};
OutTransitions.Sort(FCompareTransitionsByPriority());
}
}
UAnimBlueprint* UAnimStateNodeBase::GetAnimBlueprint() const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(this);
return CastChecked<UAnimBlueprint>(Blueprint);
}