Refactor Scripted Animations to provide an option for which clock to use

This commit is contained in:
2025-10-27 18:18:35 -04:00
parent 0a3a7b9a62
commit 297cd2f068
6 changed files with 169 additions and 117 deletions

Binary file not shown.

View File

@@ -30,7 +30,7 @@ public:
// The hash of the animation step, a 63-bit unique identifier. // The hash of the animation step, a 63-bit unique identifier.
// //
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UPROPERTY()
int64 Hash; int64 Hash;
// The Body contains all the key-value pairs in an encoded form. To // The Body contains all the key-value pairs in an encoded form. To
@@ -74,7 +74,7 @@ class INTEGRATION_API UlxAnimationStepLibrary : public UBlueprintFunctionLibrary
GENERATED_BODY() GENERATED_BODY()
public: public:
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static FString AnimationStepDebugString(const FlxAnimationStep& step); static FString AnimationStepDebugString(const FlxAnimationStep& step);
// Stores the key-value pairs in properties of the target object. // Stores the key-value pairs in properties of the target object.
@@ -101,26 +101,29 @@ public:
UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Step") UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Animation Step")
static void UnpackAnimationStep(bool &bChanged, FString &Action, const FlxAnimationStep& step, UObject* target, const FString& VariableNamePrefix = TEXT("aq")); static void UnpackAnimationStep(bool &bChanged, FString &Action, const FlxAnimationStep& step, UObject* target, const FString& VariableNamePrefix = TEXT("aq"));
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static bool AnimationStepEqual(const FlxAnimationStep &StepA, const FlxAnimationStep &StepB); static bool AnimationStepEqual(const FlxAnimationStep &StepA, const FlxAnimationStep &StepB);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static bool AnimationStepIsIdle(const FlxAnimationStep &step); static bool AnimationStepIsIdle(const FlxAnimationStep &step);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name); static FVector AnimationStepGetVector(const FlxAnimationStep& step, const FString& name);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static double AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name); static double AnimationStepGetFloat(const FlxAnimationStep& step, const FString& name);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static FString AnimationStepGetString(const FlxAnimationStep& step, const FString& name); static FString AnimationStepGetString(const FlxAnimationStep& step, const FString& name);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static FName AnimationStepGetName(const FlxAnimationStep& step, const FString& name); static FName AnimationStepGetName(const FlxAnimationStep& step, const FString& name);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Animation Step") UFUNCTION(BlueprintPure, Category = "Luprex|Animation Step")
static bool AnimationStepGetBool(const FlxAnimationStep& step, const FString& name); static bool AnimationStepGetBool(const FlxAnimationStep& step, const FString& name);
UFUNCTION(BlueprintPure, meta = (BlueprintAutocast), Category = "Luprex|Animation Step")
static int64 AnimationStepID(const FlxAnimationStep& step) { return step.Hash; }
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////

View File

@@ -1,59 +1,5 @@
#include "ScriptedAnimation.h" #include "ScriptedAnimation.h"
#include "Engine/Engine.h"
////////////////////////////////////////////////////////////
//
// Routines to calculate the current state of an FlxScriptedAnimation.
//
////////////////////////////////////////////////////////////
double FlxScriptedAnimation::CalculateFade(double Offset, double Fade)
{
if (Offset < 0.0)
{
return 0.0;
}
if (Fade > 0.001)
{
return FMath::Min(Offset / Fade, 1.0);
}
return 1.0;
}
double FlxScriptedAnimation::UnclampedElapsedTime(double CurrentTime) const
{
return CurrentTime - StartTime;
}
double FlxScriptedAnimation::UnclampedTimeLeft(double CurrentTime) const
{
return EndTime - CurrentTime;
}
double FlxScriptedAnimation::ClampedElapsedTime(double CurrentTime) const
{
return FMath::Max(0.0, CurrentTime - StartTime);
}
double FlxScriptedAnimation::ClampedTimeLeft(double CurrentTime) const
{
return FMath::Max(0.0, EndTime - CurrentTime);
}
double FlxScriptedAnimation::CalcFadeInAlpha(double CurrentTime) const
{
return CalculateFade(UnclampedElapsedTime(CurrentTime), FadeInDuration);
}
double FlxScriptedAnimation::CalcFadeOutAlpha(double CurrentTime) const
{
return CalculateFade(UnclampedTimeLeft(CurrentTime), FadeOutDuration);
}
double FlxScriptedAnimation::CalcFadeInOutAlpha(double CurrentTime) const
{
return FMath::Min(CalcFadeInAlpha(CurrentTime), CalcFadeOutAlpha(CurrentTime));
}
void FlxScriptedAnimation::InitiateFadeOut(double CurrentTime, double AllowedFade) void FlxScriptedAnimation::InitiateFadeOut(double CurrentTime, double AllowedFade)
{ {
@@ -81,43 +27,48 @@ void UlxScriptedAnimations::Keep(int n)
} }
void UlxScriptedAnimations::AddAnimation( void UlxScriptedAnimations::AddAnimation(
UObject* WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime, double FadeOutTime, int64 AqHash) UObject* WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime, double FadeOutTime,
int64 AnimationStepID, bool ContinueWhenPaused)
{ {
check(KeepCount >= 1); check(KeepCount >= 1);
FlxScriptedAnimation Result; FlxScriptedAnimation Result;
// Get World Time // Get World Time
UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject); FlxWorldClocks Clocks = UlxScriptedAnimationLibrary::GetAllWorldClocks(WorldContextObject);
double WorldTime = World ? World->GetTimeSeconds() : 0.0; double CurrentTime = ContinueWhenPaused ? Clocks.RealTime : Clocks.WorldTime;
// Get the animation Length. // Get the animation Length.
double Length = (Sequence ? static_cast<double>(Sequence->GetPlayLength()) : 0.0); double Length = (Sequence ? static_cast<double>(Sequence->GetPlayLength()) : 0.0);
// Fill the static setup fields // Fill the static setup fields
Result.Sequence = Sequence; Result.Sequence = Sequence;
Result.AqHash = AqHash; Result.ContinueWhenPaused = ContinueWhenPaused;
Result.AnimationStepID = AnimationStepID;
Result.FadeInDuration = FadeInTime; Result.FadeInDuration = FadeInTime;
Result.FadeOutDuration = FadeOutTime; Result.FadeOutDuration = FadeOutTime;
Result.StartTime = WorldTime; Result.StartTime = CurrentTime;
Result.EndTime = WorldTime + Length; Result.EndTime = CurrentTime + Length;
Keep(KeepCount - 1); Keep(KeepCount - 1);
Animations.Insert(Result, 0); Animations.Insert(Result, 0);
} }
void UlxScriptedAnimations::FadeGarbage(TArray<int32> Hashes, double CurrentTime) void UlxScriptedAnimations::FadeGarbage(const UObject *WorldContextObject, TArray<int64> KeepIDs)
{ {
FlxWorldClocks Clocks = UlxScriptedAnimationLibrary::GetAllWorldClocks(WorldContextObject);
for (int i = 0; i < Animations.Num(); i++) for (int i = 0; i < Animations.Num(); i++)
{ {
FlxScriptedAnimation &Anim = Animations[i]; FlxScriptedAnimation &Anim = Animations[i];
if ((Anim.AqHash != 0) && (!Hashes.Contains(Anim.AqHash))) if ((Anim.AnimationStepID > 0) && (!KeepIDs.Contains(Anim.AnimationStepID)))
{ {
Anim.InitiateFadeOut(CurrentTime, 0.2); Anim.InitiateFadeOut(Anim.ChooseCorrectClock(Clocks), 0.2);
} }
} }
} }
void UlxScriptedAnimationLibrary::ScriptedAnimationEvaluatorData(const UlxScriptedAnimations *Animations, double CurrentTime, void UlxScriptedAnimationLibrary::ScriptedAnimationEvaluatorData(
const UlxScriptedAnimations *Animations,
const FlxWorldClocks &WorldClocks,
UAnimSequenceBase *&Sequence0, float &ExplicitTime0, UAnimSequenceBase *&Sequence0, float &ExplicitTime0,
UAnimSequenceBase *&Sequence1, float &ExplicitTime1, UAnimSequenceBase *&Sequence1, float &ExplicitTime1,
UAnimSequenceBase *&Sequence2, float &ExplicitTime2, UAnimSequenceBase *&Sequence2, float &ExplicitTime2,
@@ -140,6 +91,7 @@ void UlxScriptedAnimationLibrary::ScriptedAnimationEvaluatorData(const UlxScript
if (Anims.Num() > 0) if (Anims.Num() > 0)
{ {
const FlxScriptedAnimation &Anim = Anims[0]; const FlxScriptedAnimation &Anim = Anims[0];
double CurrentTime = Anim.ChooseCorrectClock(WorldClocks);
Sequence0 = Anim.Sequence; Sequence0 = Anim.Sequence;
ExplicitTime0 = Anim.ClampedElapsedTime(CurrentTime); ExplicitTime0 = Anim.ClampedElapsedTime(CurrentTime);
Sequence0Alpha = Anim.CalcFadeInOutAlpha(CurrentTime); Sequence0Alpha = Anim.CalcFadeInOutAlpha(CurrentTime);
@@ -147,6 +99,7 @@ void UlxScriptedAnimationLibrary::ScriptedAnimationEvaluatorData(const UlxScript
if (Anims.Num() > 1) if (Anims.Num() > 1)
{ {
const FlxScriptedAnimation &Anim = Anims[1]; const FlxScriptedAnimation &Anim = Anims[1];
double CurrentTime = Anim.ChooseCorrectClock(WorldClocks);
Sequence1 = Anim.Sequence; Sequence1 = Anim.Sequence;
ExplicitTime1 = Anim.ClampedElapsedTime(CurrentTime); ExplicitTime1 = Anim.ClampedElapsedTime(CurrentTime);
Sequence1Alpha = Anim.CalcFadeInOutAlpha(CurrentTime); Sequence1Alpha = Anim.CalcFadeInOutAlpha(CurrentTime);
@@ -154,6 +107,7 @@ void UlxScriptedAnimationLibrary::ScriptedAnimationEvaluatorData(const UlxScript
if (Anims.Num() > 2) if (Anims.Num() > 2)
{ {
const FlxScriptedAnimation &Anim = Anims[2]; const FlxScriptedAnimation &Anim = Anims[2];
double CurrentTime = Anim.ChooseCorrectClock(WorldClocks);
Sequence2 = Anim.Sequence; Sequence2 = Anim.Sequence;
ExplicitTime2 = Anim.ClampedElapsedTime(CurrentTime); ExplicitTime2 = Anim.ClampedElapsedTime(CurrentTime);
Sequence2Alpha = Anim.CalcFadeInOutAlpha(CurrentTime); Sequence2Alpha = Anim.CalcFadeInOutAlpha(CurrentTime);
@@ -175,3 +129,14 @@ void UlxScriptedAnimationLibrary::ScriptedAnimationEvaluatorData(const UlxScript
} }
} }
FlxWorldClocks UlxScriptedAnimationLibrary::GetAllWorldClocks(const UObject *WorldContextObject)
{
UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject);
FlxWorldClocks Result;
if (World != nullptr)
{
Result.WorldTime = World->GetTimeSeconds();
Result.RealTime = World->GetRealTimeSeconds();
}
return Result;
}

View File

@@ -2,9 +2,9 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
// #include "Kismet/KismetSystemLibrary.h" // #include "Kismet/KismetSystemLibrary.h"
// #include "CommonTypes.h" // #include "CommonTypes.h"
// #include "Kismet/BlueprintFunctionLibrary.h"
#include "ScriptedAnimation.generated.h" #include "ScriptedAnimation.generated.h"
@@ -12,6 +12,18 @@ class UEnhancedInputLocalPlayerSubsystem;
class UAnimSequenceBase; class UAnimSequenceBase;
USTRUCT(BlueprintType)
struct INTEGRATION_API FlxWorldClocks
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double WorldTime = 0.0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double RealTime = 0.0;
};
USTRUCT(BlueprintType) USTRUCT(BlueprintType)
struct INTEGRATION_API FlxScriptedAnimation struct INTEGRATION_API FlxScriptedAnimation
{ {
@@ -20,13 +32,16 @@ struct INTEGRATION_API FlxScriptedAnimation
UPROPERTY(EditAnywhere, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite)
UAnimSequenceBase *Sequence = nullptr; UAnimSequenceBase *Sequence = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) UPROPERTY(EditAnywhere, BlueprintReadWrite)
int64 AqHash = 0; bool ContinueWhenPaused = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) UPROPERTY(EditAnywhere, BlueprintReadWrite)
int64 AnimationStepID = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double FadeInDuration = 0.0; double FadeInDuration = 0.0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) UPROPERTY(EditAnywhere, BlueprintReadWrite)
double FadeOutDuration = 0.0; double FadeOutDuration = 0.0;
// StartTime and EndTime. // StartTime and EndTime.
@@ -38,55 +53,94 @@ struct INTEGRATION_API FlxScriptedAnimation
// may be reduced to implement the truncation. // may be reduced to implement the truncation.
// //
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) UPROPERTY(EditAnywhere, BlueprintReadWrite)
double StartTime = 0.0; double StartTime = 0.0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) UPROPERTY(EditAnywhere, BlueprintReadWrite)
double EndTime = 0.0; double EndTime = 0.0;
// Calculate the progress of a fade. // Calculate the progress of a fade.
// //
static double CalculateFade(double Offset, double Fade); inline static double CalculateFade(double Offset, double Fade)
{
if (Offset < 0.0)
{
return 0.0;
}
if (Fade > 0.001)
{
return FMath::Min(Offset / Fade, 1.0);
}
return 1.0;
}
// Given an FGameTime, extract the correct clock to use for this animation.
//
inline double ChooseCorrectClock(const FlxWorldClocks &WorldClocks) const
{
return ContinueWhenPaused ? WorldClocks.RealTime : WorldClocks.WorldTime;
}
// Calculate Elapsed Time (unclamped) // Calculate Elapsed Time (unclamped)
// //
// If current time is before the animation's start time, then // If current time is before the animation's start time, then
// elapsed time will be negative. // elapsed time will be negative.
// //
double UnclampedElapsedTime(double CurrentTime) const; inline double UnclampedElapsedTime(double CurrentTime) const
{
return CurrentTime - StartTime;
}
// Calculate Time Left (unclamped) // Calculate Time Left (unclamped)
// //
// If current time is after the animation's end time, then // If current time is after the animation's end time, then
// time left will be negative. // time left will be negative.
// //
double UnclampedTimeLeft(double CurrentTime) const; inline double UnclampedTimeLeft(double CurrentTime) const
{
return EndTime - CurrentTime;
}
// Calculate Elapsed Time (clamped) // Calculate Elapsed Time (clamped)
// //
// If current time is before the animation's start time, then // If current time is before the animation's start time, then
// elapsed time will be zero. // elapsed time will be zero.
// //
double ClampedElapsedTime(double CurrentTime) const; inline double ClampedElapsedTime(double CurrentTime) const
{
return FMath::Max(0.0, CurrentTime - StartTime);
}
// Calculate Time Left (clamped) // Calculate Time Left (clamped)
// //
// If current time is after the animation's end time, then // If current time is after the animation's end time, then
// time left will be zero. // time left will be zero.
// //
double ClampedTimeLeft(double CurrentTime) const; inline double ClampedTimeLeft(double CurrentTime) const
{
return FMath::Max(0.0, EndTime - CurrentTime);
}
// Calculate the progress of the fadein. // Calculate the progress of the fadein.
// //
double CalcFadeInAlpha(double CurrentTime) const; inline double CalcFadeInAlpha(double CurrentTime) const
{
return CalculateFade(UnclampedElapsedTime(CurrentTime), FadeInDuration);
}
// Calculate the progress of the fadeout. // Calculate the progress of the fadeout.
// //
double CalcFadeOutAlpha(double CurrentTime) const; inline double CalcFadeOutAlpha(double CurrentTime) const
{
return CalculateFade(UnclampedTimeLeft(CurrentTime), FadeOutDuration);
}
// Calculate the combined alpha of the fade-in-out. // Calculate the combined alpha of the fade-in-out.
// //
double CalcFadeInOutAlpha(double CurrentTime) const; inline double CalcFadeInOutAlpha(double CurrentTime) const
{
return FMath::Min(CalcFadeInAlpha(CurrentTime), CalcFadeOutAlpha(CurrentTime));
}
// Cause an animation to start fading right away. // Cause an animation to start fading right away.
// //
@@ -130,25 +184,38 @@ public:
// in the array is always the most recent. All other steps are shifted // in the array is always the most recent. All other steps are shifted
// back. If the array size exceeds the KeepCount, it is truncated. // back. If the array size exceeds the KeepCount, it is truncated.
// //
// The Aq Hash parameter can be used to associate a scripted animation // The AnimationStepID parameter can be used to associate a scripted
// with a step in the animation queue. This makes it feasible to garbage // animation with a step in the animation queue. Typically, you would
// collect the scripted animation if the animation queue step disappears. // set the scripted animation ID equal to the animation queue step ID.
// This makes it feasible to garbage collect the scripted animation
// if the animation queue step disappears.
//
// If 'ContinueWhenPaused' is true, then the animation keeps playing
// even when the game is paused. Otherwise, it freezes when the game
// is paused.
// //
UFUNCTION(BlueprintCallable, Category = "Luprex|Scripted Animations", meta=(WorldContext = "WorldContextObject")) UFUNCTION(BlueprintCallable, Category = "Luprex|Scripted Animations", meta=(WorldContext = "WorldContextObject"))
void AddAnimation( void AddAnimation(
UObject *WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime = 0.2, double FadeOutTime = 0.2, int64 AqHash=0); UObject *WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime = 0.2, double FadeOutTime = 0.2,
int64 AnimationStepID=0, bool ContinueWhenPaused=false);
// Fade animations whose corresponding animation queue step is dead. // Fade all animations whose IDs are not in the 'Keep' list.
// //
// Sometimes, predictive reexecution causes an animation step to // Sometimes, predictive reexecution causes an animation step to
// vanish from the animation queue. When that happens, it is usually // vanish from the animation queue. When that happens, it is
// desirable to terminate any scripted animations that were launched // desirable to terminate any scripted animations that were launched
// by that animation step. You don't want to cut them off abruptly, // by that animation step. You don't want to cut them off abruptly,
// you want to fade them out. This function causes all animations that // you want to fade them out.
// are owned by a dead animation step to begin fading.
// //
UFUNCTION(BlueprintCallable, Category = "Luprex|Scripted Animations", meta=(WorldContext = "WorldContextObject")) // Whenever luprex calls 'Animation Queue Changed' in a blueprint,
void FadeGarbage(TArray<int32> Hashes, double CurrentTime); // it also automatically runs this garbage collection routine on the
// scripted animations.
//
// Scripted animations with nonpositive IDs are immune to garbage
// collection. So if you want an animation step to be not affected,
// give it an ID of zero or a negative number.
//
void FadeGarbage(const UObject *WorldContextObject, TArray<int64> KeepIDs);
}; };
UCLASS() UCLASS()
@@ -156,10 +223,12 @@ class INTEGRATION_API UlxScriptedAnimationLibrary : public UBlueprintFunctionLib
{ {
GENERATED_BODY() GENERATED_BODY()
private:
public: public:
// Get all the major 'World Clocks' in a single struct.
//
UFUNCTION(BlueprintPure, Category = "Utilities|Time", meta=(WorldContext = "WorldContextObject"))
static FlxWorldClocks GetAllWorldClocks(const UObject *WorldContextObject);
// Get the data to drive Sequence Evaluators and Multi Blend // Get the data to drive Sequence Evaluators and Multi Blend
// //
// To apply scripted animations in an Anim Graph, you will need // To apply scripted animations in an Anim Graph, you will need
@@ -167,7 +236,8 @@ public:
// function outputs the input parameters for all of those nodes. // function outputs the input parameters for all of those nodes.
// //
UFUNCTION(BlueprintPure, Category = "Luprex|Scripted Animations", meta=(BlueprintThreadSafe)) UFUNCTION(BlueprintPure, Category = "Luprex|Scripted Animations", meta=(BlueprintThreadSafe))
static void ScriptedAnimationEvaluatorData(const UlxScriptedAnimations *Animations, double CurrentTime, static void ScriptedAnimationEvaluatorData(const UlxScriptedAnimations *Animations,
const FlxWorldClocks &WorldClocks,
UAnimSequenceBase *&Sequence0, float &ExplicitTime0, UAnimSequenceBase *&Sequence0, float &ExplicitTime0,
UAnimSequenceBase *&Sequence1, float &ExplicitTime1, UAnimSequenceBase *&Sequence1, float &ExplicitTime1,
UAnimSequenceBase *&Sequence2, float &ExplicitTime2, UAnimSequenceBase *&Sequence2, float &ExplicitTime2,

View File

@@ -123,7 +123,8 @@ void UlxTangible::MaybeExecuteAnimStateChanged() {
bool AnyChange = AnimTracker.IsChanged(); bool AnyChange = AnimTracker.IsChanged();
while (AnimTracker.IsChanged()) { while (AnimTracker.IsChanged())
{
if (limit == 0) break; if (limit == 0) break;
limit -= 1; limit -= 1;
AnimTracker.ClearChanged(); AnimTracker.ClearChanged();
@@ -164,11 +165,22 @@ void UlxTangible::MaybeExecuteAnimStateChanged() {
FString DS = UlxAnimationStepLibrary::AnimationStepDebugString(*Step); FString DS = UlxAnimationStepLibrary::AnimationStepDebugString(*Step);
UE_LOG(LogLuprex, Warning, TEXT("Timeout - blueprint didn't finish animation: %s"), *DS); UE_LOG(LogLuprex, Warning, TEXT("Timeout - blueprint didn't finish animation: %s"), *DS);
AnimTracker.FinishedAnimation(PendingAnimationHash); AnimTracker.FinishedAnimation(PendingAnimationHash);
AnyChange = true;
} }
AutoUpdatePosition(); AutoUpdatePosition();
PendingAnimationHash = 0; PendingAnimationHash = 0;
PendingAnimationTimeout = 0.0; PendingAnimationTimeout = 0.0;
} }
// The following code garbage collects any scripted animations that
// were created by animation steps that are no longer in the animation
// queue. This is intended to handle the case that predictive reexecution
// incorrectly predicts an animation step, and then the animation step
// goes away during of difference transmission.
if (AnyChange && (ScriptedAnimations != nullptr))
{
ScriptedAnimations->FadeGarbage(this, AnimTracker.GetHashes());
}
} }
FVector UlxTangible::GetLocation() const FVector UlxTangible::GetLocation() const
@@ -233,7 +245,8 @@ void UlxTangible::AutoUpdatePosition()
} }
} }
void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool AutoUpdate) { void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step, bool AutoUpdate)
{
UlxTangible *tan = GetActorTangibleOrLog(target); UlxTangible *tan = GetActorTangibleOrLog(target);
if (tan == nullptr) return; if (tan == nullptr) return;
tan->AnimTracker.FinishedAnimation(step.Hash); tan->AnimTracker.FinishedAnimation(step.Hash);
@@ -242,7 +255,8 @@ void UlxTangible::FinishedAnimation(AActor *target, const FlxAnimationStep &step
UE_LOG(LogLuprex, Display, TEXT("Animation Finished: %s"), *DebugString); UE_LOG(LogLuprex, Display, TEXT("Animation Finished: %s"), *DebugString);
} }
bool UlxTangible::AnimationStepIsFinished(AActor *target, const FlxAnimationStep &step) { bool UlxTangible::AnimationStepIsFinished(AActor *target, const FlxAnimationStep &step)
{
UlxTangible *tan = GetActorTangibleOrLog(target); UlxTangible *tan = GetActorTangibleOrLog(target);
if (tan == nullptr) return true; if (tan == nullptr) return true;
return tan->AnimTracker.IsFinished(step.Hash); return tan->AnimTracker.IsFinished(step.Hash);