I copied 4 files that makes PlayMontage node:
PlayMontageCallbackProxy.h/cpp
K2Node_PlayMontage.h/cpp
I just changed the names to make my own and added a little code to actual functionality.
The problem now is: I can’t package it. It requires BlueprintGraph module, when i’m adding to Public(or Private doesn’t matter) modules list, then it complains:
UATHelper: Packaging (Windows): Unable to instantiate module 'UnrealEd': Unable to instantiate UnrealEd module for non-editor targets.
UATHelper: Packaging (Windows): (referenced via Target -> SkyCraft.Build.cs -> BlueprintGraph.Build.cs -> KismetCompiler.Build.cs)
BlueprintGraph module is Editor only, so i’m putting in condition for editor-only:
if (Target.bBuildEditor)
{
PrivateDependencyModuleNames.AddRange(new string[]
{
"BlueprintGraph"
});
}
Now it complains this:
UATHelper: Packaging (Windows): C:\Unreal Engine Projects\SkyCraft\Source\SkyCraft\K2Node_AdianPlayMontage.h(9): Error: Unable to find parent class type for 'UK2Node_AdianPlayMontage' named 'UK2Node_BaseAsyncTask'
PackagingResults: Error: Unable to find parent class type for 'UK2Node_AdianPlayMontage' named 'UK2Node_BaseAsyncTask'
What the hell? I tried numerous methods… I don’t know what to do now!
For clarity, just the same node:
K2Node_AdianPlayMontage.h
// ADIAN Copyrighted
#pragma once
#include "CoreMinimal.h"
#include "K2Node_BaseAsyncTask.h"
#include "K2Node_AdianPlayMontage.generated.h"
UCLASS()
class UK2Node_AdianPlayMontage : public UK2Node_BaseAsyncTask
{
GENERATED_UCLASS_BODY()
//~ Begin UEdGraphNode Interface
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
//~ End UEdGraphNode Interface
//~ Begin UK2Node Interface
virtual FText GetMenuCategory() const override;
//~ End UK2Node Interface
};
K2Node_AdianPlayMontage.cpp
// ADIAN Copyrighted
#include "K2Node_AdianPlayMontage.h"
#include "SkyCraft/AdianPlayMontageCallbackProxy.h"
UK2Node_AdianPlayMontage::UK2Node_AdianPlayMontage(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ProxyFactoryFunctionName = GET_FUNCTION_NAME_CHECKED(UAdianPlayMontageCallbackProxy, CreateProxyObjectForPlayMontage);
ProxyFactoryClass = UAdianPlayMontageCallbackProxy::StaticClass();
ProxyClass = UAdianPlayMontageCallbackProxy::StaticClass();
}
FText UK2Node_AdianPlayMontage::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return FText::FromString("Adian Play Montage");
}
FText UK2Node_AdianPlayMontage::GetMenuCategory() const
{
return FText::FromString("Animation|Montage");
}
AdianPlayMontageCallbackProxy.h
// ADIAN Copyrighted
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "UObject/ScriptMacros.h"
#include "Animation/AnimInstance.h"
#include "AdianPlayMontageCallbackProxy.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMontagePlayDelegate, FName, NotifyName);
UCLASS(MinimalAPI)
class UAdianPlayMontageCallbackProxy : public UObject
{
GENERATED_UCLASS_BODY()
// Called when Montage finished playing and wasn't interrupted
UPROPERTY(BlueprintAssignable)
FOnMontagePlayDelegate OnCompleted;
// Called when Montage starts blending out and is not interrupted
UPROPERTY(BlueprintAssignable)
FOnMontagePlayDelegate OnBlendOut;
// Called when Montage has been interrupted (or failed to play)
UPROPERTY(BlueprintAssignable)
FOnMontagePlayDelegate OnInterrupted;
UPROPERTY(BlueprintAssignable)
FOnMontagePlayDelegate OnNotifyBegin;
UPROPERTY(BlueprintAssignable)
FOnMontagePlayDelegate OnNotifyEnd;
// Called to perform the query internally
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UAdianPlayMontageCallbackProxy* CreateProxyObjectForPlayMontage(
class USkeletalMeshComponent* InSkeletalMeshComponent,
class UAnimMontage* MontageToPlay,
float PlayRate = 1.f,
float StartingPosition = 0.f,
FName StartingSection = NAME_None,
bool bShouldStopAllMontages = true);
public:
//~ Begin UObject Interface
virtual void BeginDestroy() override;
//~ End UObject Interface
protected:
UFUNCTION()
void OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted);
UFUNCTION()
void OnMontageEnded(UAnimMontage* Montage, bool bInterrupted);
UFUNCTION()
void OnNotifyBeginReceived(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload);
UFUNCTION()
void OnNotifyEndReceived(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload);
private:
TWeakObjectPtr<UAnimInstance> AnimInstancePtr;
int32 MontageInstanceID;
uint32 bInterruptedCalledBeforeBlendingOut : 1;
bool IsNotifyValid(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload) const;
void UnbindDelegates();
FOnMontageBlendingOutStarted BlendingOutDelegate;
FOnMontageEnded MontageEndedDelegate;
protected:
// Attempts to play a montage with the specified settings. Returns whether it started or not.
bool AdianPlayMontage(
class USkeletalMeshComponent* InSkeletalMeshComponent,
class UAnimMontage* MontageToPlay,
float PlayRate = 1.f,
float StartingPosition = 0.f,
FName StartingSection = NAME_None,
bool bShouldStopAllMontages = true);
};
AdianPlayMontageCallbackProxy.cpp
// ADIAN Copyrighted
#include "AdianPlayMontageCallbackProxy.h"
#include "Animation/AnimMontage.h"
#include "AssetUserData/AUD_MontageSettings.h"
#include "Components/SkeletalMeshComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AdianPlayMontageCallbackProxy)
//////////////////////////////////////////////////////////////////////////
// UPlayMontageCallbackProxy
UAdianPlayMontageCallbackProxy::UAdianPlayMontageCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, MontageInstanceID(INDEX_NONE)
, bInterruptedCalledBeforeBlendingOut(false)
{
}
UAdianPlayMontageCallbackProxy* UAdianPlayMontageCallbackProxy::CreateProxyObjectForPlayMontage(
class USkeletalMeshComponent* InSkeletalMeshComponent,
class UAnimMontage* MontageToPlay,
float PlayRate,
float StartingPosition,
FName StartingSection,
bool bShouldStopAllMontages)
{
UAdianPlayMontageCallbackProxy* Proxy = NewObject<UAdianPlayMontageCallbackProxy>();
Proxy->SetFlags(RF_StrongRefOnFrame);
Proxy->AdianPlayMontage(InSkeletalMeshComponent, MontageToPlay, PlayRate, StartingPosition, StartingSection, bShouldStopAllMontages);
return Proxy;
}
bool UAdianPlayMontageCallbackProxy::AdianPlayMontage(class USkeletalMeshComponent* InSkeletalMeshComponent,
class UAnimMontage* MontageToPlay,
float PlayRate,
float StartingPosition,
FName StartingSection,
bool bShouldStopAllMontages)
{
bool bPlayedSuccessfully = false;
if (InSkeletalMeshComponent)
{
if (UAnimInstance* AnimInstance = InSkeletalMeshComponent->GetAnimInstance())
{
const float MontageLength = AnimInstance->Montage_Play(MontageToPlay, PlayRate, EMontagePlayReturnType::MontageLength, StartingPosition, bShouldStopAllMontages);
bPlayedSuccessfully = (MontageLength > 0.f);
if (bPlayedSuccessfully)
{
AnimInstancePtr = AnimInstance;
if (FAnimMontageInstance* MontageInstance = AnimInstance->GetActiveInstanceForMontage(MontageToPlay))
{
MontageInstanceID = MontageInstance->GetInstanceID();
}
if (StartingSection != NAME_None)
{
AnimInstance->Montage_JumpToSection(StartingSection, MontageToPlay);
}
BlendingOutDelegate.BindUObject(this, &UAdianPlayMontageCallbackProxy::OnMontageBlendingOut);
AnimInstance->Montage_SetBlendingOutDelegate(BlendingOutDelegate, MontageToPlay);
MontageEndedDelegate.BindUObject(this, &UAdianPlayMontageCallbackProxy::OnMontageEnded);
AnimInstance->Montage_SetEndDelegate(MontageEndedDelegate, MontageToPlay);
AnimInstance->OnPlayMontageNotifyBegin.AddDynamic(this, &UAdianPlayMontageCallbackProxy::OnNotifyBeginReceived);
AnimInstance->OnPlayMontageNotifyEnd.AddDynamic(this, &UAdianPlayMontageCallbackProxy::OnNotifyEndReceived);
}
}
}
if (!bPlayedSuccessfully)
{
OnInterrupted.Broadcast(NAME_None);
}
return bPlayedSuccessfully;
}
bool UAdianPlayMontageCallbackProxy::IsNotifyValid(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload) const
{
return ((MontageInstanceID != INDEX_NONE) && (BranchingPointNotifyPayload.MontageInstanceID == MontageInstanceID));
}
void UAdianPlayMontageCallbackProxy::OnNotifyBeginReceived(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload)
{
if (IsNotifyValid(NotifyName, BranchingPointNotifyPayload))
{
OnNotifyBegin.Broadcast(NotifyName);
}
}
void UAdianPlayMontageCallbackProxy::OnNotifyEndReceived(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload)
{
if (IsNotifyValid(NotifyName, BranchingPointNotifyPayload))
{
OnNotifyEnd.Broadcast(NotifyName);
}
}
void UAdianPlayMontageCallbackProxy::OnMontageBlendingOut(UAnimMontage* Montage, bool bInterrupted)
{
UAUD_MontageSettings* MontageSettings = Montage->GetAssetUserData<UAUD_MontageSettings>();
if (MontageSettings && MontageSettings->bEndOnBlendOut)
{
if (!bInterrupted)
{
OnBlendOut.Broadcast(NAME_None);
}
else
{
OnInterrupted.Broadcast(NAME_None);
}
UnbindDelegates();
}
else
{
if (bInterrupted)
{
OnInterrupted.Broadcast(NAME_None);
bInterruptedCalledBeforeBlendingOut = true;
}
else
{
OnBlendOut.Broadcast(NAME_None);
}
}
}
void UAdianPlayMontageCallbackProxy::OnMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
UAUD_MontageSettings* MontageSettings = Montage->GetAssetUserData<UAUD_MontageSettings>();
if (MontageSettings && MontageSettings->bEndOnBlendOut)
{
if (!bInterrupted) OnCompleted.Broadcast(NAME_None);
return;
}
if (!bInterrupted)
{
OnCompleted.Broadcast(NAME_None);
}
else if (!bInterruptedCalledBeforeBlendingOut)
{
OnInterrupted.Broadcast(NAME_None);
}
UnbindDelegates();
}
void UAdianPlayMontageCallbackProxy::UnbindDelegates()
{
if (UAnimInstance* AnimInstance = AnimInstancePtr.Get())
{
AnimInstance->OnPlayMontageNotifyBegin.RemoveDynamic(this, &UAdianPlayMontageCallbackProxy::OnNotifyBeginReceived);
AnimInstance->OnPlayMontageNotifyEnd.RemoveDynamic(this, &UAdianPlayMontageCallbackProxy::OnNotifyEndReceived);
}
}
void UAdianPlayMontageCallbackProxy::BeginDestroy()
{
UnbindDelegates();
Super::BeginDestroy();
}
I’m using AdianPlayMontage in numerous runtime blueprints.
How can i avoid this dependency issue?