From e7cb47db5bf89ad00d23b9a06fe0919b4047d4a4 Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 10 Oct 2025 17:33:39 -0400 Subject: [PATCH] Successful implementation of UlxScriptedAnimations --- .../Mannequins/Animations/ABP_Manny.uasset | 4 +- .../Mannequins/Meshes/SK_Mannequin.uasset | 4 +- Content/Tangibles/TAN_Character.uasset | 4 +- Source/Integration/AnimQueue.h | 3 +- Source/Integration/UtilityLibrary.cpp | 116 ++++++++++++++++++ Source/Integration/UtilityLibrary.h | 112 ++++++++++++++++- 6 files changed, 235 insertions(+), 8 deletions(-) diff --git a/Content/Characters/Mannequins/Animations/ABP_Manny.uasset b/Content/Characters/Mannequins/Animations/ABP_Manny.uasset index 4ef68f56..af2b0f80 100644 --- a/Content/Characters/Mannequins/Animations/ABP_Manny.uasset +++ b/Content/Characters/Mannequins/Animations/ABP_Manny.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98d9b579c4c057c48d424014106f648617e450d10d91f9ebd02936e0835d27e3 -size 340019 +oid sha256:10ac0617f269247f7a496f04f058f16f2c52a80e8207b9030126b5cecce10695 +size 430672 diff --git a/Content/Characters/Mannequins/Meshes/SK_Mannequin.uasset b/Content/Characters/Mannequins/Meshes/SK_Mannequin.uasset index 23923c08..b6224697 100644 --- a/Content/Characters/Mannequins/Meshes/SK_Mannequin.uasset +++ b/Content/Characters/Mannequins/Meshes/SK_Mannequin.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a261395ebcfba6e0ed3bf00434249038b78a7c801b859b5642703c167566e289 -size 158148 +oid sha256:1cfb7ca3b738e8abdd1c75c8ae055db0589ccfb9fb5d89b1ce3100dc40ba9a05 +size 190376 diff --git a/Content/Tangibles/TAN_Character.uasset b/Content/Tangibles/TAN_Character.uasset index 13e6a21e..2ae6d9c3 100644 --- a/Content/Tangibles/TAN_Character.uasset +++ b/Content/Tangibles/TAN_Character.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfc033e1fa7fa768a973220586c861a216892d9f5112843ae9dde60b4490f89d -size 332627 +oid sha256:cddebb69e3efcb0fcb326fd24c7d8200e392bb9e9a80836679a02018beb1c058 +size 345222 diff --git a/Source/Integration/AnimQueue.h b/Source/Integration/AnimQueue.h index 2d53dd0a..40de2df0 100644 --- a/Source/Integration/AnimQueue.h +++ b/Source/Integration/AnimQueue.h @@ -4,6 +4,7 @@ #include "CoreUObject.h" #include "StringDecoder.h" #include "Containers/Deque.h" +#include "Kismet/BlueprintFunctionLibrary.h" #include "AnimQueue.generated.h" @@ -61,7 +62,7 @@ public: //////////////////////////////////////////////// UCLASS() -class INTEGRATION_API UlxAnimationStepLibrary : public UObject +class INTEGRATION_API UlxAnimationStepLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index 6fd2a954..08f987e2 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -10,6 +10,7 @@ #include "Components/GridPanel.h" #include "InputMappingContext.h" #include "EnhancedInputComponent.h" +#include "Animation/AnimSequenceBase.h" #define LOCTEXT_NAMESPACE "Luprex Utility" @@ -212,6 +213,121 @@ void UlxUtilityLibrary::GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, } } +void UlxScriptedAnimations::Keep(int n) +{ + if (n < 0) n = 0; + if (Animations.Num() > n) + { + Animations.SetNum(n); + } +} + +void UlxScriptedAnimations::AddAnimation( + UObject* WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime, double FadeOutTime) +{ + check(KeepCount >= 1); + FlxScriptedAnimation Result; + + // Get World Time + UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject); + double WorldTime = World ? World->GetTimeSeconds() : 0.0; + + // Fill the static setup fields + Result.Sequence = Sequence; + Result.FadeIn = FadeInTime; + Result.FadeOut = FadeOutTime; + Result.StartTime = WorldTime; + Result.AdjustedLength = (Result.Sequence ? static_cast(Result.Sequence->GetPlayLength()) : 0.0); + + Keep(KeepCount - 1); + Animations.Insert(Result, 0); +} + +void UlxScriptedAnimations::FadeOutAll(UObject *WorldContextObject) +{ + +} + +FlxScriptedAnimationProgress UlxUtilityLibrary::ScriptedAnimationProgress(const FlxScriptedAnimation &Animation, double CurrentTime) +{ + FlxScriptedAnimationProgress Progress(Animation); + + // Store the world time of the last update. + Progress.UpdateTime = CurrentTime; + + // Compute time relationships + Progress.EndTime = Animation.StartTime + Progress.AdjustedLength; + Progress.ElapsedTime = FMath::Max(0.0, Progress.UpdateTime - Progress.StartTime); + Progress.TimeLeft = FMath::Max(0.0, Progress.EndTime - Progress.UpdateTime); + + // Determine fade-in / fade-out blend + Progress.FadeInAlpha = 1.0; + Progress.FadeOutAlpha = 1.0; + if (Progress.FadeIn > 0.0) Progress.FadeInAlpha = FMath::Clamp(Progress.ElapsedTime / Progress.FadeIn, 0.0, 1.0); + if (Progress.FadeOut > 0.0) Progress.FadeOutAlpha = FMath::Clamp(Progress.TimeLeft / Progress.FadeOut, 0.0, 1.0); + Progress.FadeAlpha = FMath::Min(Progress.FadeInAlpha, Progress.FadeOutAlpha); + + return Progress; +} + +void UlxUtilityLibrary::ScriptedAnimationEvaluatorData(const UlxScriptedAnimations *Animations, double CurrentTime, + UAnimSequenceBase *&Sequence0, float &ExplicitTime0, + UAnimSequenceBase *&Sequence1, float &ExplicitTime1, + UAnimSequenceBase *&Sequence2, float &ExplicitTime2, + float &BaseAlpha, float &Sequence0Alpha, float &Sequence1Alpha, float &Sequence2Alpha) +{ + Sequence0 = nullptr; + Sequence1 = nullptr; + Sequence2 = nullptr; + ExplicitTime0 = 0.0; + ExplicitTime1 = 0.0; + ExplicitTime2 = 0.0; + BaseAlpha = 0.0; + Sequence0Alpha = 0.0; + Sequence1Alpha = 0.0; + Sequence2Alpha = 0.0; + + if (Animations != nullptr) + { + const TArray &Anims = Animations->GetAnimations(); + if (Anims.Num() > 0) + { + FlxScriptedAnimationProgress Progress = ScriptedAnimationProgress(Anims[0], CurrentTime); + Sequence0 = Progress.Sequence; + ExplicitTime0 = Progress.ElapsedTime; + Sequence0Alpha = Progress.FadeAlpha; + } + if (Anims.Num() > 1) + { + FlxScriptedAnimationProgress Progress = ScriptedAnimationProgress(Anims[1], CurrentTime); + Sequence1 = Progress.Sequence; + ExplicitTime1 = Progress.ElapsedTime; + Sequence1Alpha = Progress.FadeAlpha; + } + if (Anims.Num() > 2) + { + FlxScriptedAnimationProgress Progress = ScriptedAnimationProgress(Anims[2], CurrentTime); + Sequence2 = Progress.Sequence; + ExplicitTime2 = Progress.ElapsedTime; + Sequence2Alpha = Progress.FadeAlpha; + } + } + + double AlphaTotal = Sequence0Alpha + Sequence1Alpha + Sequence2Alpha; + if (AlphaTotal > 1.0) + { + double Scale = 1.0 / AlphaTotal; + Sequence0Alpha *= Scale; + Sequence1Alpha *= Scale; + Sequence2Alpha *= Scale; + BaseAlpha = 0.0; + } + else + { + BaseAlpha = 1.0 - AlphaTotal; + } +} + ElxUsedOrNotUsed UlxUtilityLibrary::IsKeyUsedByMappingContext(const FKey &Key, const UInputMappingContext *MappingContext) { if (!MappingContext) diff --git a/Source/Integration/UtilityLibrary.h b/Source/Integration/UtilityLibrary.h index adbc5eda..07fa9e79 100644 --- a/Source/Integration/UtilityLibrary.h +++ b/Source/Integration/UtilityLibrary.h @@ -6,9 +6,96 @@ #include "Kismet/KismetSystemLibrary.h" #include "Input/Events.h" #include "CommonTypes.h" +#include "Kismet/BlueprintFunctionLibrary.h" + #include "UtilityLibrary.generated.h" class UEnhancedInputLocalPlayerSubsystem; +class UAnimSequenceBase; + + +USTRUCT(BlueprintType) +struct INTEGRATION_API FlxScriptedAnimation +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UAnimSequenceBase *Sequence; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double FadeIn = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double FadeOut = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double StartTime = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double AdjustedLength = 0.0; +}; + +USTRUCT(BlueprintType) +struct INTEGRATION_API FlxScriptedAnimationProgress : public FlxScriptedAnimation +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double UpdateTime = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double EndTime = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double ElapsedTime = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double TimeLeft = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double FadeInAlpha = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double FadeOutAlpha = 0.0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay) + double FadeAlpha = 0.0; +}; + + +UCLASS(BlueprintType) +class INTEGRATION_API UlxScriptedAnimations : public UObject +{ + GENERATED_BODY() + +private: + UPROPERTY() + int KeepCount = 3; + + UPROPERTY() + TArray Animations; + + // If the number of elements in the array exceeds the count, discard. + // + void Keep(int n); + +public: + // Allow read-only access anywhere. + // + const TArray &GetAnimations() const { return Animations; } + + // Add a scripted animation to the end of the array. + // + UFUNCTION(BlueprintCallable, Category = "Luprex|Scripted Animations", meta=(WorldContext = "WorldContextObject")) + void AddAnimation( + UObject *WorldContextObject, UAnimSequenceBase* Sequence, double FadeInTime = 0.2, double FadeOutTime = 0.2); + + // Truncate all current animations: force them all to begin fading out. + // + UFUNCTION(BlueprintCallable, Category = "Luprex|Scripted Animations", meta=(WorldContext = "WorldContextObject")) + void FadeOutAll(UObject *WorldContextObject); +}; + /** * @@ -18,7 +105,7 @@ class UEnhancedInputLocalPlayerSubsystem; * */ UCLASS() -class INTEGRATION_API UlxUtilityLibrary : public UObject +class INTEGRATION_API UlxUtilityLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() @@ -133,6 +220,29 @@ public: UFUNCTION(BlueprintPure, Category="Widget") static void GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY); + // Calculate the progress of a Scripted Animation. + // + // Given a scripted animation and the current time, calculates + // how much time has elapsed, how much time is left, and several + // other parameters pertaining to the passage of time for + // this animation. + // + UFUNCTION(BlueprintPure, Category = "Luprex|Scripted Animations", meta=(BlueprintThreadSafe)) + static FlxScriptedAnimationProgress ScriptedAnimationProgress(const FlxScriptedAnimation &Animation, double CurrentTime); + + // 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, + UAnimSequenceBase *&Sequence0, float &ExplicitTime0, + UAnimSequenceBase *&Sequence1, float &ExplicitTime1, + UAnimSequenceBase *&Sequence2, float &ExplicitTime2, + float &BaseAlpha, float &Sequence0Alpha, float &Sequence1Alpha, float &Sequence2Alpha); + // Check if a given key is used by the specified mapping context. // // This is true if the key is mapped to anything at all within