Files
UnrealEngine/Engine/Source/Runtime/UMG/Private/Animation/WidgetAnimationState.cpp
2025-05-18 13:04:45 +08:00

742 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Animation/WidgetAnimationState.h"
#include "Animation/UMGSequencePlayer.h"
#include "Animation/UMGSequenceTickManager.h"
#include "Animation/WidgetAnimation.h"
#include "Animation/WidgetAnimationHandle.h"
#include "Blueprint/UserWidget.h"
#include "EntitySystem/MovieSceneEntitySystemLinker.h"
#include "EntitySystem/MovieSceneEntitySystemRunner.h"
#include "Evaluation/MovieSceneEvaluationState.h"
#include "Evaluation/MovieScenePlayback.h"
#include "Evaluation/MovieSceneSequenceHierarchy.h"
#include "MovieScene.h"
#include "MovieSceneFwd.h"
#include "MovieSceneLegacyPlayer.h"
#include "MovieSceneTimeHelpers.h"
#include "ProfilingDebugging/CsvProfiler.h"
#include "UMGPrivate.h"
namespace UE::UMG
{
bool GVarAnimationDetailedLogging = false;
FAutoConsoleVariableRef CVarAnimationDetailedLogging(
TEXT("UMG.AnimationDetailedLogging"),
GVarAnimationDetailedLogging,
TEXT("(Default: false) Whether to print out detailed information about UMG animations.")
);
bool GVarAnimationMarkers = false;
FAutoConsoleVariableRef CVarAnimationMarkers(
TEXT("UMG.AnimationMarkers"),
GVarAnimationMarkers,
TEXT("(Default: false) Whether to emit profiling frame markers for starting and stopping UMG animations.")
);
struct FUMGLegacyPlayerProvider : public UE::MovieScene::ILegacyPlayerProviderPlaybackCapability
{
FWidgetAnimationState* State = nullptr;
FUMGLegacyPlayerProvider(FWidgetAnimationState* InState)
: State(InState)
{
check(State);
}
virtual IMovieScenePlayer* CreateLegacyPlayer(TSharedRef<UE::MovieScene::FSharedPlaybackState> InSharedPlaybackState) override
{
return State->GetOrCreateLegacyPlayer();
}
};
} // namespace UE::UMG
FWidgetAnimationState::FWidgetAnimationState()
{
UserTag = NAME_None;
bRestoreState = false;
bIsBeginningPlay = false;
bIsStopping = false;
bIsPendingDelete = false;
}
void FWidgetAnimationState::Initialize(UWidgetAnimation* InAnimation, UUserWidget* InUserWidget, int32 InIndex, int32 InSerialNumber)
{
using namespace UE::UMG;
using namespace UE::MovieScene;
check(InAnimation && InUserWidget);
Animation = InAnimation;
WeakUserWidget = InUserWidget;
StateIndex = InIndex;
SerialNumber = InSerialNumber;
check(InUserWidget->AnimationTickManager);
const bool bNeedsLegacyPlayer = NeedsLegacyPlayer();
const bool bNeedsPrivateLinker = EnumHasAnyFlags(InAnimation->GetFlags(), EMovieSceneSequenceFlags::BlockingEvaluation);
UMovieSceneEntitySystemLinker* Linker = InUserWidget->AnimationTickManager->GetLinker();
if (bNeedsPrivateLinker)
{
Linker = UMovieSceneEntitySystemLinker::CreateLinker(InUserWidget->GetWorld(), UE::MovieScene::EEntitySystemLinkerRole::UMG);
PrivateLinker = Linker;
}
FInstanceRegistry* InstanceRegistry = Linker->GetInstanceRegistry();
FRootInstanceHandle RootInstanceHandle = InstanceRegistry->AllocateRootInstance(*InAnimation, InUserWidget);
{
FSequenceInstance& RootInstance = InstanceRegistry->MutateInstance(RootInstanceHandle);
TSharedRef<FSharedPlaybackState> SharedPlaybackState = RootInstance.GetSharedPlaybackState();
WeakPlaybackState = SharedPlaybackState;
SharedPlaybackState->AddCapability<FMovieSceneEvaluationState>();
if (bNeedsLegacyPlayer)
{
GetOrCreateLegacyPlayer();
check(LegacyPlayer);
SharedPlaybackState->AddCapability<FPlayerIndexPlaybackCapability>(LegacyPlayer->GetUniqueIndex());
}
else
{
SharedPlaybackState->AddCapability<FUMGLegacyPlayerProvider>(this);
}
RootInstance.Initialize();
}
PlaybackManager.Initialize(InAnimation);
PlaybackManager.SetDissectLooping(EMovieSceneLoopDissection::DissectOne);
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Initializing '%s' [%d], instance [%d]%s"),
*GetNameSafe(Animation), StateIndex, RootInstanceHandle.InstanceID,
(LegacyPlayer ? TEXT(", with legacy player") : TEXT("")));
}
#endif
}
bool FWidgetAnimationState::NeedsLegacyPlayer() const
{
UUserWidget* UserWidget = WeakUserWidget.Get();
if (!UserWidget)
{
return false;
}
const ERequiresLegacyPlayer Requirement = UserWidget->GetLegacyPlayerRequirement();
if (Requirement == ERequiresLegacyPlayer::AutoDetect)
{
// Require a legacy player if the widget has a native C++ sub-class. This is because that
// sub-class could override one of the virtual methods that takes a legacy player (there is
// no way for us to detect that at compile time, sadly).
// If we only have Blueprint sub-classes, they can't use the legacy player except via
// lazy-creation on the FWidgetAnimationHandle, so we don't need to create one right away.
UClass* CurrentClass = UserWidget->GetClass();
while (CurrentClass && CurrentClass != UUserWidget::StaticClass())
{
if (EnumHasAnyFlags(CurrentClass->GetClassFlags(), CLASS_Native))
{
return true;
}
CurrentClass = CurrentClass->GetSuperClass();
}
return false;
}
else
{
return Requirement == ERequiresLegacyPlayer::Yes;
}
}
void FWidgetAnimationState::FlushIfPrivateLinker()
{
using namespace UE::MovieScene;
if (PrivateLinker)
{
TSharedPtr<FMovieSceneEntitySystemRunner> EntitySystemRunner = PrivateLinker->GetRunner();
EntitySystemRunner->Flush();
}
}
void FWidgetAnimationState::Tick(float InDeltaSeconds)
{
using namespace UE::MovieScene;
UUserWidget* UserWidget = WeakUserWidget.Get();
TSharedPtr<FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin();
if (!UserWidget || !SharedPlaybackState)
{
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Ticking '%s' [%d] aborted, invalid state."),
*GetNameSafe(Animation), StateIndex);
}
#endif
return;
}
const EMovieScenePlayerStatus::Type PreviousPlaybackStatus = PlaybackManager.GetPlaybackStatus();
if (PreviousPlaybackStatus == EMovieScenePlayerStatus::Playing)
{
// Update root transform in case it has changed.
if (const FMovieSceneSequenceHierarchy* Hierarchy = SharedPlaybackState->GetHierarchy())
{
PlaybackManager.SetPlaybackTimeTransform(Hierarchy->GetRootTransform());
}
// Get the evaluation contexts for this tick.
FMovieScenePlaybackManager::FContexts TickContexts;
PlaybackManager.Update(InDeltaSeconds, TickContexts);
const EMovieScenePlayerStatus::Type NextPlaybackStatus = PlaybackManager.GetPlaybackStatus();
const bool bNeedsFinalUpdate = (NextPlaybackStatus == EMovieScenePlayerStatus::Stopped);
// Queue up the evalutions as needed. If we finished playback, setup our OnStopped callback
// to tear things down.
TSharedPtr<FMovieSceneEntitySystemRunner> EntitySystemRunner = SharedPlaybackState->GetRunner();
for (int32 Index = 0; Index < TickContexts.Num(); ++Index)
{
FMovieSceneContext& TickContext(TickContexts[Index]);
FSimpleDelegate OnFlushDelegate;
ERunnerUpdateFlags UpdateFlags = ERunnerUpdateFlags::None;
if (bNeedsFinalUpdate && Index == TickContexts.Num() - 1)
{
// WARNING: widget animation states are held inside a TSparseArray by UUserWidget. As this array
// gets re-allocated, the states also get re-allocated. So we don't want to capture the "this"
// pointer in a callback that could be invoked when that pointer has moved. Instead, we use a
// static method as the callback, with our handle as an argument. This also handles the case of
// a callback invoked on a state that has been removed.
FWidgetAnimationHandle ThisHandle = GetAnimationHandle();
OnFlushDelegate = FSimpleDelegate::CreateStatic(&FWidgetAnimationState::OnStopped, ThisHandle);
UpdateFlags = ERunnerUpdateFlags::Flush;
bIsStopping = true;
}
EntitySystemRunner->QueueUpdate(
TickContext, SharedPlaybackState->GetRootInstanceHandle(),
MoveTemp(OnFlushDelegate),
UpdateFlags);
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Ticking '%s' [%d], Time=%s, Status=%s, Direction=%s (update %d)"),
*GetNameSafe(Animation), StateIndex,
*LexToString(TickContext.GetTime()),
*UEnum::GetValueAsString(TickContext.GetStatus()),
((TickContext.GetDirection() == EPlayDirection::Forwards) ? TEXT("Forwards") : TEXT("Backwards")),
Index);
}
#endif
}
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Ticking done '%s' [%d], NumLoopsCompleted=%d, PlaybackSpeed=%f"),
*GetNameSafe(Animation), StateIndex,
PlaybackManager.GetNumLoopsCompleted(),
PlaybackManager.GetPlayRate());
}
#endif
FlushIfPrivateLinker();
}
else
{
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Ticking '%s' [%d] skipped, state not playing."),
*GetNameSafe(Animation), StateIndex);
}
#endif
}
}
void FWidgetAnimationState::OnBegunPlay(FWidgetAnimationHandle InHandle)
{
if (FWidgetAnimationState* State = InHandle.GetAnimationState())
{
State->OnBegunPlay();
}
}
void FWidgetAnimationState::OnStopped(FWidgetAnimationHandle InHandle)
{
if (FWidgetAnimationState* State = InHandle.GetAnimationState())
{
State->OnStopped();
}
}
void FWidgetAnimationState::OnBegunPlay()
{
using namespace UE::MovieScene;
bIsBeginningPlay = false;
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: OnBegunPlay '%s' [%d]"),
*GetNameSafe(Animation), StateIndex);
}
#endif
}
void FWidgetAnimationState::OnStopped()
{
using namespace UE::MovieScene;
TSharedPtr<FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin();
if (SharedPlaybackState)
{
TSharedPtr<FMovieSceneEntitySystemRunner> EntitySystemRunner = SharedPlaybackState->GetRunner();
const bool bNeedsFinalFlush = EntitySystemRunner->QueueFinalUpdate(SharedPlaybackState->GetRootInstanceHandle());
// Even if our request to Finish the instance was queued, we can wait until the next flush for those effects to be seen
// This will most likely happen immediately anyway since the runner will keep looping until its queue is empty,
// And we are already inside an active evaluation
if (bNeedsFinalFlush)
{
FlushIfPrivateLinker();
}
}
if (SharedPlaybackState && bRestoreState)
{
SharedPlaybackState->GetPreAnimatedState().RestorePreAnimatedState();
}
bIsStopping = false;
bIsPendingDelete = true;
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
UE_LOG(LogUMG, Log, TEXT("Animation: OnStopped '%s' [%d]%s%s"),
*GetNameSafe(Animation), StateIndex,
OnWidgetAnimationFinishedEvent.IsBound() ? TEXT(", OnWidgetAnimationFinishedEvent bound") : TEXT(""),
(LegacyPlayer && LegacyPlayer->OnSequenceFinishedPlaying().IsBound()) ? TEXT(", OnSequenceFinishedPlaying bound") : TEXT(""));
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
#endif
FWidgetAnimationHandle ThisHandle = GetAnimationHandle();
UUserWidget* UserWidget = WeakUserWidget.Get();
if (UserWidget)
{
if (UE::UMG::GVarAnimationMarkers && Animation)
{
CSV_EVENT_GLOBAL(TEXT("Stop Animation [%s::%s]"), *GetNameSafe(UserWidget), *GetNameSafe(Animation));
}
UserWidget->BroadcastAnimationFinishedPlaying(*this);
}
// WARNING
// Don't access any more member fields after this... the call to BroadcastAnimationFinishedPlaying
// may have started new animations, which might have reallocated our parent widget's ActiveAnimations
// array, and rendering us invalid.
if (FWidgetAnimationState* NewThis = ThisHandle.GetAnimationState())
{
NewThis->OnWidgetAnimationFinishedEvent.Broadcast(*NewThis);
if (NewThis->LegacyPlayer)
{
NewThis->LegacyPlayer->BroadcastSequenceFinishedPlaying();
}
}
}
UUMGSequencePlayer* FWidgetAnimationState::GetOrCreateLegacyPlayer()
{
if (!LegacyPlayer)
{
if (UUserWidget* UserWidget = WeakUserWidget.Get())
{
LegacyPlayer = NewObject<UUMGSequencePlayer>(UserWidget);
LegacyPlayer->InitSequencePlayer(*this);
}
}
return LegacyPlayer;
}
UUMGSequencePlayer* FWidgetAnimationState::GetLegacyPlayer() const
{
return LegacyPlayer;
}
FWidgetAnimationHandle FWidgetAnimationState::GetAnimationHandle() const
{
return FWidgetAnimationHandle(WeakUserWidget.Get(), StateIndex, SerialNumber);
}
bool FWidgetAnimationState::IsPlayingForward() const
{
return PlaybackManager.IsPlayingForward();
}
EMovieScenePlayerStatus::Type FWidgetAnimationState::GetPlaybackStatus() const
{
return PlaybackManager.GetPlaybackStatus();
}
void FWidgetAnimationState::SetPlaybackStatus(EMovieScenePlayerStatus::Type InPlaybackStatus)
{
PlaybackManager.SetPlaybackStatus(InPlaybackStatus);
}
FQualifiedFrameTime FWidgetAnimationState::GetCurrentTime() const
{
const FFrameTime CurrentPosition = PlaybackManager.GetCurrentTime();
return FQualifiedFrameTime(CurrentPosition, PlaybackManager.GetDisplayRate());
}
void FWidgetAnimationState::SetCurrentTime(float InTime)
{
const FFrameTime JumpFrameTime = PlaybackManager.GetDisplayRate().AsFrameTime(InTime);
PlaybackManager.SetCurrentTime(JumpFrameTime);
}
void FWidgetAnimationState::SetNumLoopsToPlay(int32 InNumLoopsToPlay)
{
// For UMG animations, we treat a whole ping-pong as a loop.
if (PlayMode == EUMGSequencePlayMode::PingPong)
{
PlaybackManager.SetNumLoopsToPlay(2 * InNumLoopsToPlay);
}
else
{
PlaybackManager.SetNumLoopsToPlay(InNumLoopsToPlay);
}
}
void FWidgetAnimationState::SetPlaybackSpeed(float InPlaybackSpeed)
{
PlaybackManager.SetPlayRate(InPlaybackSpeed);
}
void FWidgetAnimationState::Play(const FWidgetAnimationStatePlayParams& PlayParams)
{
using namespace UE::MovieScene;
TSharedPtr<FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin();
if (!ensure(SharedPlaybackState))
{
return;
}
UUserWidget* UserWidget = WeakUserWidget.Get();
if (UE::UMG::GVarAnimationMarkers && Animation && UserWidget)
{
CSV_EVENT_GLOBAL(TEXT("Play Animation [%s::%s]"), *GetNameSafe(UserWidget), *GetNameSafe(Animation));
}
bRestoreState = PlayParams.bRestoreState;
if (bRestoreState)
{
SharedPlaybackState->GetPreAnimatedState().EnableGlobalPreAnimatedStateCapture();
}
const FFrameRate DisplayRate = PlaybackManager.GetDisplayRate();
PlaybackManager.SetStartOffset(FFrameTime(0));
if (PlayParams.StartOffset.IsSet())
{
const FFrameTime StartOffsetTime = DisplayRate.AsFrameTime(PlayParams.StartOffset.GetValue());
PlaybackManager.SetStartOffset(StartOffsetTime);
}
PlaybackManager.SetEndOffset(FFrameTime(0));
if (PlayParams.EndAtTime.IsSet())
{
const FFrameTime EndFrameTime = DisplayRate.AsFrameTime(PlayParams.EndAtTime.GetValue());
PlaybackManager.SetEndOffsetAsTime(EndFrameTime);
}
if (PlayParams.EndOffset.IsSet())
{
const FFrameTime EndOffsetTime = DisplayRate.AsFrameTime(PlayParams.EndOffset.GetValue());
PlaybackManager.SetEndOffset(PlaybackManager.GetEndOffset() + EndOffsetTime);
}
PlayMode = PlayParams.PlayMode;
if (PlayMode == EUMGSequencePlayMode::Reverse)
{
// When playing in reverse, subtract the start time from the end.
const FFrameTime StartFrameTime = DisplayRate.AsFrameTime(PlayParams.StartAtTime);
const FFrameTime ReverseStartFrameTime = PlaybackManager.GetEffectiveEndTime() - StartFrameTime;
PlaybackManager.SetCurrentTime(ReverseStartFrameTime);
}
else
{
const FFrameTime StartFrameTime = DisplayRate.AsFrameTime(PlayParams.StartAtTime);
PlaybackManager.SetCurrentTime(StartFrameTime);
}
PlaybackManager.SetPlayDirection(EPlayDirection::Forwards);
PlaybackManager.SetPingPongPlayback(false);
switch (PlayMode)
{
case EUMGSequencePlayMode::Forward:
break;
case EUMGSequencePlayMode::PingPong:
PlaybackManager.SetPingPongPlayback(true);
break;
case EUMGSequencePlayMode::Reverse:
PlaybackManager.SetPlayDirection(EPlayDirection::Backwards);
break;
}
// For UMG animations, a whole ping-pong is a single loop.
PlaybackManager.SetNumLoopsToPlay(
(PlayMode == EUMGSequencePlayMode::PingPong) ?
2 * PlayParams.NumLoopsToPlay :
PlayParams.NumLoopsToPlay);
PlaybackManager.ResetNumLoopsCompleted();
PlaybackManager.SetPlayRate(PlayParams.PlaybackSpeed);
PlaybackManager.SetPlaybackStatus(EMovieScenePlayerStatus::Playing);
// Setup time warping.
if (const FMovieSceneSequenceHierarchy* Hierarchy = SharedPlaybackState->GetHierarchy())
{
PlaybackManager.SetPlaybackTimeTransform(Hierarchy->GetRootTransform());
PlaybackManager.SetTransformPlaybackTime(true);
}
else
{
PlaybackManager.SetTransformPlaybackTime(false);
}
// TODO: we shouldn't have to queue an update right away but we preserve the old behavior for now.
TSharedPtr<FMovieSceneEntitySystemRunner> Runner = SharedPlaybackState->GetRunner();
if (Runner)
{
// See WARNING in Tick() method.
FWidgetAnimationHandle ThisHandle = GetAnimationHandle();
const FMovieSceneContext FirstContext = PlaybackManager.UpdateAtCurrentTime();
Runner->QueueUpdate(
FirstContext,
SharedPlaybackState->GetRootInstanceHandle(),
FSimpleDelegate::CreateStatic(&FWidgetAnimationState::OnBegunPlay, ThisHandle),
ERunnerUpdateFlags::Flush);
}
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Play '%s' [%d], StartTime=%s, NumLoopsToPlay=%d, PlaybackSpeed=%f, PlayMode=%s%s"),
*GetNameSafe(Animation), StateIndex,
*LexToString(PlaybackManager.GetCurrentTime()),
PlaybackManager.GetNumLoopsToPlay(),
PlaybackManager.GetPlayRate(),
PlaybackManager.GetPlayDirection() == EPlayDirection::Forwards ? TEXT("Forwards") : TEXT("Backwards"),
PlaybackManager.IsPingPongPlayback() ? TEXT(", PingPong") : TEXT(""));
}
#endif
FlushIfPrivateLinker();
}
void FWidgetAnimationState::Stop()
{
using namespace UE::MovieScene;
if (PlaybackManager.GetPlaybackStatus() == EMovieScenePlayerStatus::Stopped)
{
return;
}
PlaybackManager.SetPlaybackStatus(EMovieScenePlayerStatus::Stopped);
// TODO: we should use PlaybackManager.GetEffectiveStartTime(), but the old behavior always used frame zero.
PlaybackManager.SetCurrentTime(0);
bool bFlushPrivateLinker = false;
if (TSharedPtr<FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin())
{
const FRootInstanceHandle& RootInstanceHandle = SharedPlaybackState->GetRootInstanceHandle();
UMovieSceneEntitySystemLinker* Linker = SharedPlaybackState->GetLinker();
const FSequenceInstance& RootInstance = Linker->GetInstanceRegistry()->GetInstance(RootInstanceHandle);
if (RootInstance.HasEverUpdated())
{
TSharedPtr<FMovieSceneEntitySystemRunner> Runner = SharedPlaybackState->GetRunner();
// See WARNING in Tick() method.
FWidgetAnimationHandle ThisHandle = GetAnimationHandle();
const FMovieSceneContext ReturnToStartContext = PlaybackManager.UpdateAtCurrentTime();
Runner->QueueUpdate(
ReturnToStartContext,
SharedPlaybackState->GetRootInstanceHandle(),
FSimpleDelegate::CreateStatic(&FWidgetAnimationState::OnStopped, ThisHandle),
ERunnerUpdateFlags::Flush);
bIsStopping = true;
bFlushPrivateLinker = true;
}
else
{
OnStopped();
}
}
else
{
OnStopped();
}
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Stop '%s' [%d]"),
*GetNameSafe(Animation), StateIndex);
}
#endif
if (bFlushPrivateLinker)
{
FlushIfPrivateLinker();
}
}
void FWidgetAnimationState::Pause()
{
using namespace UE::MovieScene;
// Should be Paused but old behavior was to set to Stopped.
PlaybackManager.SetPlaybackStatus(EMovieScenePlayerStatus::Stopped);
// Evaluate the sequence at its current time, with a status of 'Stopped' to ensure that animated state
// pauses correctly. (ie. audio sounds should stop/pause)
if (TSharedPtr<FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin())
{
TSharedPtr<FMovieSceneEntitySystemRunner> Runner = SharedPlaybackState->GetRunner();
const FMovieSceneContext PauseContext = PlaybackManager.UpdateAtCurrentTime();
Runner->QueueUpdate(
PauseContext,
SharedPlaybackState->GetRootInstanceHandle(),
ERunnerUpdateFlags::Flush);
}
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: Pause '%s' [%d], PauseTime=%s"),
*GetNameSafe(Animation), StateIndex,
*LexToString(PlaybackManager.GetCurrentTime()));
}
#endif
FlushIfPrivateLinker();
}
void FWidgetAnimationState::Reverse()
{
PlaybackManager.ReversePlayDirection();
}
void FWidgetAnimationState::RemoveEvaluationData()
{
using namespace UE::MovieScene;
if (TSharedPtr<const FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin())
{
UMovieSceneEntitySystemLinker* Linker = SharedPlaybackState->GetLinker();
FRootInstanceHandle RootInstanceHandle = SharedPlaybackState->GetRootInstanceHandle();
FSequenceInstance& RootInstance = Linker->GetInstanceRegistry()->MutateInstance(RootInstanceHandle);
TSharedRef<FMovieSceneEntitySystemRunner> Runner = Linker->GetRunner();
if (Runner->IsCurrentlyEvaluating())
{
Runner->FlushOutstanding();
}
RootInstance.Ledger.UnlinkEverything(Linker);
RootInstance.InvalidateCachedData();
}
if (LegacyPlayer)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
LegacyPlayer->GetEvaluationTemplate().ResetDirectorInstances();
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: RemoveEvaluationData '%s' [%d]"),
*GetNameSafe(Animation), StateIndex);
}
#endif
}
void FWidgetAnimationState::TearDown()
{
using namespace UE::MovieScene;
FRootInstanceHandle RootInstanceHandle;
UMovieSceneEntitySystemLinker* EntitySystemLinker = nullptr;
if (TSharedPtr<const FSharedPlaybackState> SharedPlaybackState = WeakPlaybackState.Pin())
{
RootInstanceHandle = SharedPlaybackState->GetRootInstanceHandle();
EntitySystemLinker = SharedPlaybackState->GetLinker();
}
if (LegacyPlayer)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FMovieSceneRootEvaluationTemplateInstance& EvaluationTemplate = LegacyPlayer->GetEvaluationTemplate();
EvaluationTemplate.TearDown();
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
LegacyPlayer = nullptr;
WeakPlaybackState.Reset();
if (EntitySystemLinker && RootInstanceHandle.IsValid())
{
EntitySystemLinker->DestroyInstanceImmediately(RootInstanceHandle);
}
#if !NO_LOGGING
if (UE::UMG::GVarAnimationDetailedLogging)
{
UE_LOG(LogUMG, Log, TEXT("Animation: TearDown '%s' [%d]"),
*GetNameSafe(Animation), StateIndex);
}
#endif
}
bool FWidgetAnimationState::IsValid() const
{
return WeakPlaybackState.IsValid();
}
void FWidgetAnimationState::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(Animation);
Collector.AddReferencedObject(LegacyPlayer);
Collector.AddReferencedObject(PrivateLinker);
}