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

View File

@@ -2,9 +2,9 @@
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
// #include "Kismet/KismetSystemLibrary.h"
// #include "CommonTypes.h"
// #include "Kismet/BlueprintFunctionLibrary.h"
#include "ScriptedAnimation.generated.h"
@@ -12,6 +12,18 @@ class UEnhancedInputLocalPlayerSubsystem;
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)
struct INTEGRATION_API FlxScriptedAnimation
{
@@ -20,13 +32,16 @@ struct INTEGRATION_API FlxScriptedAnimation
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UAnimSequenceBase *Sequence = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay)
int64 AqHash = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool ContinueWhenPaused = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int64 AnimationStepID = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double FadeInDuration = 0.0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double FadeOutDuration = 0.0;
// StartTime and EndTime.
@@ -38,55 +53,94 @@ struct INTEGRATION_API FlxScriptedAnimation
// may be reduced to implement the truncation.
//
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double StartTime = 0.0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
double EndTime = 0.0;
// 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)
//
// If current time is before the animation's start time, then
// elapsed time will be negative.
//
double UnclampedElapsedTime(double CurrentTime) const;
inline double UnclampedElapsedTime(double CurrentTime) const
{
return CurrentTime - StartTime;
}
// Calculate Time Left (unclamped)
//
// If current time is after the animation's end time, then
// time left will be negative.
//
double UnclampedTimeLeft(double CurrentTime) const;
inline double UnclampedTimeLeft(double CurrentTime) const
{
return EndTime - CurrentTime;
}
// Calculate Elapsed Time (clamped)
//
// If current time is before the animation's start time, then
// 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)
//
// If current time is after the animation's end time, then
// 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.
//
double CalcFadeInAlpha(double CurrentTime) const;
inline double CalcFadeInAlpha(double CurrentTime) const
{
return CalculateFade(UnclampedElapsedTime(CurrentTime), FadeInDuration);
}
// 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.
//
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.
//
@@ -130,25 +184,38 @@ public:
// in the array is always the most recent. All other steps are shifted
// back. If the array size exceeds the KeepCount, it is truncated.
//
// The Aq Hash parameter can be used to associate a scripted animation
// with a step in the animation queue. This makes it feasible to garbage
// collect the scripted animation if the animation queue step disappears.
// The AnimationStepID parameter can be used to associate a scripted
// animation with a step in the animation queue. Typically, you would
// 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"))
void AddAnimation(
UObject *WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime = 0.2, double FadeOutTime = 0.2, int64 AqHash=0);
// Fade animations whose corresponding animation queue step is dead.
UObject *WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime = 0.2, double FadeOutTime = 0.2,
int64 AnimationStepID=0, bool ContinueWhenPaused=false);
// Fade all animations whose IDs are not in the 'Keep' list.
//
// 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
// 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
// are owned by a dead animation step to begin fading.
// you want to fade them out.
//
// Whenever luprex calls 'Animation Queue Changed' in a blueprint,
// 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.
//
UFUNCTION(BlueprintCallable, Category = "Luprex|Scripted Animations", meta=(WorldContext = "WorldContextObject"))
void FadeGarbage(TArray<int32> Hashes, double CurrentTime);
void FadeGarbage(const UObject *WorldContextObject, TArray<int64> KeepIDs);
};
UCLASS()
@@ -156,18 +223,21 @@ class INTEGRATION_API UlxScriptedAnimationLibrary : public UBlueprintFunctionLib
{
GENERATED_BODY()
private:
public:
// Get the data to drive Sequence Evaluators and Multi Blend
// 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
//
// To apply scripted animations in an Anim Graph, you will need
// three sequence evaluators and a multi-blend node. This
// function outputs the input parameters for all of those nodes.
//
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 *&Sequence1, float &ExplicitTime1,
UAnimSequenceBase *&Sequence2, float &ExplicitTime2,