#pragma once #include "CoreMinimal.h" #include "WingServer.h" #include "WingHandler.h" #include "WingFetcher.h" #include "WingUtils.h" #include "EdGraph/EdGraph.h" #include "Kismet2/KismetEditorUtilities.h" #include "Animation/AnimBlueprint.h" #include "Animation/AnimSequence.h" #include "AnimGraphNode_SequencePlayer.h" #include "AnimStateNode.h" #include "AnimationStateMachineGraph.h" #include "StateMachine_AddState.generated.h" // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- UCLASS() class UWing_StateMachine_AddState : public UObject, public IWingHandler { GENERATED_BODY() public: UPROPERTY(meta=(Description="Animation Blueprint package path")) FString Blueprint; UPROPERTY(meta=(Description="State machine graph name")) FString Graph; UPROPERTY(meta=(Description="Name for the new state")) FString StateName; UPROPERTY(meta=(Optional, Description="X position of the new state node")) int32 PosX = 200; UPROPERTY(meta=(Optional, Description="Y position of the new state node")) int32 PosY = 0; UPROPERTY(meta=(Optional, Description="Animation asset package path to assign to the state")) FString AnimationAsset; virtual FString GetDescription() const override { return TEXT("Add a new state to an animation state machine graph. " "Optionally assign an animation asset to the state."); } virtual void Handle() override { // Resolve the anim blueprint WingFetcher F; UAnimBlueprint* AnimBP = F.Walk(Blueprint).Cast(); if (!AnimBP) return; // Find the state machine graph UAnimationStateMachineGraph* SMGraph = WingUtils::FindStateMachineGraph(AnimBP, Graph); if (!SMGraph) { UWingServer::Printf(TEXT("ERROR: State machine graph '%s' not found in %s\n"), *Graph, *WingUtils::FormatName(AnimBP)); return; } // Check for duplicate state name if (WingUtils::FindStateByName(SMGraph, StateName)) { UWingServer::Printf(TEXT("ERROR: State '%s' already exists in %s\n"), *StateName, *WingUtils::FormatName(SMGraph)); return; } // Create the state node UAnimStateNode* NewState = NewObject(SMGraph); NewState->CreateNewGuid(); NewState->NodePosX = PosX; NewState->NodePosY = PosY; // Set the state name via the bound graph NewState->PostPlacedNewNode(); NewState->AllocateDefaultPins(); // Rename the bound graph to set the state name if (NewState->GetBoundGraph()) { NewState->GetBoundGraph()->Rename(*StateName, nullptr); } SMGraph->AddNode(NewState, false, false); NewState->SetFlags(RF_Transactional); // Optionally set animation asset if (!AnimationAsset.IsEmpty() && NewState->GetBoundGraph()) { WingFetcher F2; UAnimSequence* AnimSeq = F2.Asset(AnimationAsset).Cast(); if (!AnimSeq) return; UAnimGraphNode_SequencePlayer* SeqNode = NewObject(NewState->GetBoundGraph()); SeqNode->CreateNewGuid(); SeqNode->PostPlacedNewNode(); SeqNode->AllocateDefaultPins(); SeqNode->SetAnimationAsset(AnimSeq); SeqNode->NodePosX = 0; SeqNode->NodePosY = 0; NewState->GetBoundGraph()->AddNode(SeqNode, false, false); } // Compile FKismetEditorUtilities::CompileBlueprint(AnimBP); UWingServer::Printf(TEXT("Created state '%s' in %s\n"), *StateName, *WingUtils::FormatName(SMGraph)); UWingServer::Printf(TEXT(" node: %s\n"), *WingUtils::FormatName(NewState)); } };