It's often pretty useful to be able to access UMG-created animations from C++. Especially as you move more and more logic from Blueprints into C++, UMG animations can seem like the last hold-out.

New Method

UPDATE: The method I described in an old version of this tutorial is now not needed! There is now a meta property that does all the work for us!

BindWidget and BindWidgetOptional are undocumented meta properties that we can use to access our animations from C++.

Add variables to the header of your Blueprint's parent C++ class, as shown in the code below.

BUIUWWindow.h

UPROPERTY( Transient, meta = ( BindWidgetAnim ) )
UWidgetAnimation* RevealWindow;

UPROPERTY( Transient, meta = ( BindWidgetAnim ) )
UWidgetAnimation* HideWindow;

This works in very much the same way as the BindWidget meta property that we can use to hook up C++ to Blueprint widgets.

As of Unreal Engine 5.0 you must mark your properties as Transient.

Old Method

NOTE: This method is deprecated, you just need to do the BindWidget method above instead!

With a custom subclass of UUserWidget, we can create a map of FName to UWidgetAnimation* and access the animations by name from C++.

Let's see how to do this:

CustomUserWidget.h

#pragma once

#include "CustomUserWidget.generated.h"

UCLASS(Abstract)
class UCustomUserWidget : public UUserWidget
{
	GENERATED_BODY()

public:
	virtual void NativeConstruct() override;

	UWidgetAnimation* GetAnimationByName(FName AnimationName) const;

	bool PlayAnimationByName(FName AnimationName,
		float StartAtTime,
		int32 NumLoopsToPlay,
		EUMGSequencePlayMode::Type PlayMode,
		float PlaybackSpeed);

protected:
	TMap<FName, UWidgetAnimation*> AnimationsMap;

	void FillAnimationsMap();

};

Notes:

  • Our PlayAnimationByName isn't entirely needed, but it can be a helpful wrapper for the regular PlayAnimation function

CustomUserWidget.cpp

#include "CustomeUserWidget.h"

void UCustomUserWidget::NativeConstruct()
{
	FillAnimationsMap();

	// Call Blueprint Event Construct node
	Super::NativeConstruct();
}

void UCustomUserWidget::FillAnimationsMap()
{
	AnimationsMap.Empty();
	
	UProperty* Prop = GetClass()->PropertyLink;

	// Run through all properties of this class to find any widget animations
	while (Prop != nullptr)
	{
		// Only interested in object properties
		if (Prop->GetClass() == UObjectProperty::StaticClass())
		{
			UObjectProperty* ObjProp = Cast<UObjectProperty>(Prop);

			// Only want the properties that are widget animations
			if (ObjProp->PropertyClass == UWidgetAnimation::StaticClass())
			{
				UObject* Obj = ObjProp->GetObjectPropertyValue_InContainer(this);

				UWidgetAnimation* WidgetAnim = Cast<UWidgetAnimation>(Obj);

				if (WidgetAnim != nullptr && WidgetAnim->MovieScene != nullptr)
				{
					FName AnimName = WidgetAnim->MovieScene->GetFName();
					AnimationsMap.Add(AnimName, WidgetAnim);
				}
			}
		}

		Prop = Prop->PropertyLinkNext;
	}

UWidgetAnimation* UCustomUserWidget::GetAnimationByName(FName AnimationName) const
{
	UWidgetAnimation* const* WidgetAnim = AnimationsMap.Find(AnimationName);
	if (WidgetAnim)
	{
		return *WidgetAnim;
	}
	return nullptr;
}


bool UCustomUserWidget::PlayAnimationByName(FName AnimationName,
	float StartAtTime,
	int32 NumLoopsToPlay,
	EUMGSequencePlayMode::Type PlayMode,
	float PlaybackSpeed)
{
	UWidgetAnimation* WidgetAnim = GetAnimationByName(AnimationName);
	if (WidgetAnim)
	{
		PlayAnimation(WidgetAnim, StartAtTime, NumLoopsToPlay, PlayMode, PlaybackSpeed);
		return true;
	}
	return false;
}

Posted:

Updated: