diff --git a/.gitignore b/.gitignore index efa39be..9219460 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ Binaries/* Build/* Intermediate/* +.vscode diff --git a/BUITween.uplugin b/BUITween.uplugin index ecef620..0fb1559 100644 --- a/BUITween.uplugin +++ b/BUITween.uplugin @@ -19,6 +19,11 @@ "Name": "BUITween", "Type": "Runtime", "LoadingPhase": "Default" - } + }, + { + "Name": "BUITweenBP", + "Type": "Runtime", + "LoadingPhase": "Default" + } ] } \ No newline at end of file diff --git a/Source/BUITween/Private/BUITweenInstance.cpp b/Source/BUITween/Private/BUITweenInstance.cpp index 9f2f9a8..b862bc1 100644 --- a/Source/BUITween/Private/BUITweenInstance.cpp +++ b/Source/BUITween/Private/BUITweenInstance.cpp @@ -25,26 +25,26 @@ void FBUITweenInstance::Begin() } // Set all the props to the existng state - TranslationProp.OnBegin( pWidget->RenderTransform.Translation ); - ScaleProp.OnBegin( pWidget->RenderTransform.Scale ); - RotationProp.OnBegin( pWidget->RenderTransform.Angle ); + TranslationProp.OnBegin( pWidget->GetRenderTransform().Translation ); + ScaleProp.OnBegin( pWidget->GetRenderTransform().Scale ); + RotationProp.OnBegin( pWidget->GetRenderTransform().Angle ); OpacityProp.OnBegin( pWidget->GetRenderOpacity() ); { UUserWidget* UW = Cast( pWidget ); if ( UW ) { - ColorProp.OnBegin( UW->ColorAndOpacity ); + ColorProp.OnBegin( UW->GetColorAndOpacity() ); } UImage* UI = Cast( pWidget ); if ( UI ) { - ColorProp.OnBegin( UI->ColorAndOpacity ); + ColorProp.OnBegin( UI->GetColorAndOpacity() ); } UBorder* Border = Cast( pWidget ); if ( Border ) { - ColorProp.OnBegin( Border->ContentColorAndOpacity ); + ColorProp.OnBegin( Border->GetContentColorAndOpacity() ); } } @@ -61,32 +61,32 @@ void FBUITweenInstance::Begin() if ( OverlaySlot ) { PaddingProp.OnBegin( FVector4( - OverlaySlot->Padding.Left, - OverlaySlot->Padding.Top, - OverlaySlot->Padding.Bottom, - OverlaySlot->Padding.Right ) ); + OverlaySlot->GetPadding().Left, + OverlaySlot->GetPadding().Top, + OverlaySlot->GetPadding().Bottom, + OverlaySlot->GetPadding().Right ) ); } else if ( HorizontalBoxSlot ) { PaddingProp.OnBegin( FVector4( - HorizontalBoxSlot->Padding.Left, - HorizontalBoxSlot->Padding.Top, - HorizontalBoxSlot->Padding.Bottom, - HorizontalBoxSlot->Padding.Right ) ); + HorizontalBoxSlot->GetPadding().Left, + HorizontalBoxSlot->GetPadding().Top, + HorizontalBoxSlot->GetPadding().Bottom, + HorizontalBoxSlot->GetPadding().Right ) ); } else if ( VerticalBoxSlot ) { PaddingProp.OnBegin( FVector4( - VerticalBoxSlot->Padding.Left, - VerticalBoxSlot->Padding.Top, - VerticalBoxSlot->Padding.Bottom, - VerticalBoxSlot->Padding.Right ) ); + VerticalBoxSlot->GetPadding().Left, + VerticalBoxSlot->GetPadding().Top, + VerticalBoxSlot->GetPadding().Bottom, + VerticalBoxSlot->GetPadding().Right ) ); } USizeBox* SizeBox = Cast( pWidget ); if ( SizeBox ) { - MaxDesiredHeightProp.OnBegin( SizeBox->MaxDesiredHeight ); + MaxDesiredHeightProp.OnBegin( SizeBox->GetMaxDesiredHeight() ); } // Apply the starting conditions, even if we delay @@ -173,7 +173,7 @@ void FBUITweenInstance::Apply( float EasedAlpha ) } bool bChangedRenderTransform = false; - FWidgetTransform CurrentTransform = Target->RenderTransform; + FWidgetTransform CurrentTransform = Target->GetRenderTransform(); if ( TranslationProp.IsSet() ) { diff --git a/Source/BUITween/Public/BUIEasing.h b/Source/BUITween/Public/BUIEasing.h index a077920..b264da0 100644 --- a/Source/BUITween/Public/BUIEasing.h +++ b/Source/BUITween/Public/BUIEasing.h @@ -1,10 +1,9 @@ #pragma once #include "CoreMinimal.h" -#include "CoreUObject.h" -UENUM() -enum class EBUIEasingType +UENUM(BlueprintType) +enum class EBUIEasingType : uint8 { Linear, Smoothstep, @@ -40,41 +39,40 @@ enum class EBUIEasingType struct FBUIEasing { public: - #define TWO_PI (6.28318530717f) - + static float Ease( EBUIEasingType Type, float time, float duration = 1.0f, float overshootOrAmplitude = 0.1f, float period = 1.0f ) { switch ( Type ) { - case EBUIEasingType::Linear: return Linear( time, duration ); break; - case EBUIEasingType::Smoothstep: return Smoothstep( time, 0, duration); break; - case EBUIEasingType::InSine: return InSine( time, duration ); break; - case EBUIEasingType::OutSine: return OutSine( time, duration ); break; - case EBUIEasingType::InOutSine: return InOutSine( time, duration ); break; - case EBUIEasingType::InQuad: return InQuad( time, duration ); break; - case EBUIEasingType::OutQuad: return OutQuad( time, duration ); break; - case EBUIEasingType::InOutQuad: return InOutQuad( time, duration ); break; - case EBUIEasingType::InCubic: return InCubic( time, duration ); break; - case EBUIEasingType::OutCubic: return OutCubic( time, duration ); break; - case EBUIEasingType::InOutCubic: return InOutCubic( time, duration ); break; - case EBUIEasingType::InQuart: return InQuart( time, duration ); break; - case EBUIEasingType::OutQuart: return OutQuart( time, duration ); break; - case EBUIEasingType::InOutQuart: return InOutQuart( time, duration ); break; - case EBUIEasingType::InQuint: return InQuint( time, duration ); break; - case EBUIEasingType::OutQuint: return OutQuint( time, duration ); break; - case EBUIEasingType::InOutQuint: return InOutQuint( time, duration ); break; - case EBUIEasingType::InExpo: return InExpo( time, duration ); break; - case EBUIEasingType::OutExpo: return OutExpo( time, duration ); break; - case EBUIEasingType::InOutExpo: return InOutExpo( time, duration ); break; - case EBUIEasingType::InCirc: return InCirc( time, duration ); break; - case EBUIEasingType::OutCirc: return OutCirc( time, duration ); break; - case EBUIEasingType::InOutCirc: return InOutCirc( time, duration ); break; - case EBUIEasingType::InElastic: return InElastic( time, duration, overshootOrAmplitude, period ); break; - case EBUIEasingType::OutElastic: return OutElastic( time, duration, overshootOrAmplitude, period ); break; - case EBUIEasingType::InOutElastic: return InOutElastic( time, duration, overshootOrAmplitude, period ); break; - case EBUIEasingType::InBack: return InBack( time, duration, overshootOrAmplitude, period ); break; - case EBUIEasingType::OutBack: return OutBack( time, duration, overshootOrAmplitude, period ); break; - case EBUIEasingType::InOutBack: return InOutBack( time, duration, overshootOrAmplitude, period ); break; + case EBUIEasingType::Linear: return Linear( time, duration ); + case EBUIEasingType::Smoothstep: return Smoothstep( time, 0, duration); + case EBUIEasingType::InSine: return InSine( time, duration ); + case EBUIEasingType::OutSine: return OutSine( time, duration ); + case EBUIEasingType::InOutSine: return InOutSine( time, duration ); + case EBUIEasingType::InQuad: return InQuad( time, duration ); + case EBUIEasingType::OutQuad: return OutQuad( time, duration ); + case EBUIEasingType::InOutQuad: return InOutQuad( time, duration ); + case EBUIEasingType::InCubic: return InCubic( time, duration ); + case EBUIEasingType::OutCubic: return OutCubic( time, duration ); + case EBUIEasingType::InOutCubic: return InOutCubic( time, duration ); + case EBUIEasingType::InQuart: return InQuart( time, duration ); + case EBUIEasingType::OutQuart: return OutQuart( time, duration ); + case EBUIEasingType::InOutQuart: return InOutQuart( time, duration ); + case EBUIEasingType::InQuint: return InQuint( time, duration ); + case EBUIEasingType::OutQuint: return OutQuint( time, duration ); + case EBUIEasingType::InOutQuint: return InOutQuint( time, duration ); + case EBUIEasingType::InExpo: return InExpo( time, duration ); + case EBUIEasingType::OutExpo: return OutExpo( time, duration ); + case EBUIEasingType::InOutExpo: return InOutExpo( time, duration ); + case EBUIEasingType::InCirc: return InCirc( time, duration ); + case EBUIEasingType::OutCirc: return OutCirc( time, duration ); + case EBUIEasingType::InOutCirc: return InOutCirc( time, duration ); + case EBUIEasingType::InElastic: return InElastic( time, duration, overshootOrAmplitude, period ); + case EBUIEasingType::OutElastic: return OutElastic( time, duration, overshootOrAmplitude, period ); + case EBUIEasingType::InOutElastic: return InOutElastic( time, duration, overshootOrAmplitude, period ); + case EBUIEasingType::InBack: return InBack( time, duration, overshootOrAmplitude, period ); + case EBUIEasingType::OutBack: return OutBack( time, duration, overshootOrAmplitude, period ); + case EBUIEasingType::InOutBack: return InOutBack( time, duration, overshootOrAmplitude, period ); } return 0; } @@ -126,17 +124,17 @@ struct FBUIEasing return -0.5f * ( time * ( time - 2 ) - 1 ); } - static float InCubic( float time, float duration = 1.0f ) - { - time /= duration; - return time * time * time; - } + static float InCubic( float time, float duration = 1.0f ) + { + time /= duration; + return time * time * time; + } - static float OutCubic( float time, float duration = 1.0f ) - { - time = time / duration - 1; - return ( time * time * time + 1 ); - } + static float OutCubic( float time, float duration = 1.0f ) + { + time = time / duration - 1; + return ( time * time * time + 1 ); + } static float InOutCubic( float time, float duration = 1.0f ) { @@ -269,18 +267,27 @@ struct FBUIEasing { float s; if ( time == 0 ) return 0; - time /= duration * 0.5f; - if ( time == 2 ) return 1; + time /= duration; + if ( time == 1 ) return 1; if ( period == 0 ) period = duration * ( 0.3f * 1.5f ); if ( overshootOrAmplitude < 1 ) { overshootOrAmplitude = 1; s = period / 4; } else s = period / TWO_PI * ( float ) FMath::Asin( 1 / overshootOrAmplitude ); - time -= 1; + + time *= 2; // Scale time to 0-2 + if ( time < 1 ) + { + time -= 1; return -0.5f * ( overshootOrAmplitude * ( float ) FMath::Pow( 2, 10 * time ) * ( float ) FMath::Sin( ( time * duration - s ) * TWO_PI / period ) ); - return overshootOrAmplitude * ( float ) FMath::Pow( 2, -10 * time ) * ( float ) FMath::Sin( ( time * duration - s ) * TWO_PI / period ) * 0.5f + 1; + } + else + { + time -= 1; // Adjust for the second half + return overshootOrAmplitude * ( float ) FMath::Pow( 2, -10 * time ) * ( float ) FMath::Sin( ( time * duration - s ) * TWO_PI / period ) * 0.5f + 1; + } } static float InBack( float time, float duration = 1.0f, float overshootOrAmplitude = 0.1f, float period = 1.0f ) @@ -307,4 +314,4 @@ struct FBUIEasing }; -#undef TWO_PI + diff --git a/Source/BUITween/Public/BUITweenInstance.h b/Source/BUITween/Public/BUITweenInstance.h index 0ab0455..a20e05b 100644 --- a/Source/BUITween/Public/BUITweenInstance.h +++ b/Source/BUITween/Public/BUITweenInstance.h @@ -1,54 +1,59 @@ #pragma once #include "BUIEasing.h" +#include "BUITweenTypes.h" #include "Components/Widget.h" #include "BUITweenInstance.generated.h" -DECLARE_DELEGATE_OneParam( FBUITweenSignature, UWidget* /*Owner*/ ); +DECLARE_DELEGATE_OneParam(FBUITweenSignature, UWidget* /*Owner*/); BUITWEEN_API DECLARE_LOG_CATEGORY_EXTERN(LogBUITween, Log, All); -template +template class TBUITweenProp { public: bool bHasStart = false; bool bHasTarget = false; - inline bool IsSet() const { return bHasStart || bHasTarget; } + bool IsSet() const { return bHasStart || bHasTarget; } T StartValue; T TargetValue; T CurrentValue; bool bIsFirstTime = true; - void SetStart( T InStart ) + + void SetStart(T InStart) { bHasStart = true; StartValue = InStart; CurrentValue = StartValue; } - void SetTarget( T InTarget ) + + void SetTarget(T InTarget) { bHasTarget = true; TargetValue = InTarget; } - void OnBegin( T InCurrentValue ) + + void OnBegin(T InCurrentValue) { - if ( !bHasStart ) + if (!bHasStart) { StartValue = InCurrentValue; CurrentValue = InCurrentValue; } } - bool Update( float Alpha ) + + bool Update(float Alpha) { const T OldValue = CurrentValue; - CurrentValue = FMath::Lerp( StartValue, TargetValue, Alpha ); + CurrentValue = FMath::Lerp(StartValue, TargetValue, Alpha); const bool bShouldUpdate = bIsFirstTime || CurrentValue != OldValue; bIsFirstTime = false; return bShouldUpdate; } }; -template +template class TBUITweenInstantProp { public: @@ -59,29 +64,33 @@ class TBUITweenInstantProp T TargetValue; T CurrentValue; bool bIsFirstTime = true; - void SetStart( T InStart ) + + void SetStart(T InStart) { bHasStart = true; StartValue = InStart; CurrentValue = StartValue; } - void SetTarget( T InTarget ) + + void SetTarget(T InTarget) { bHasTarget = true; TargetValue = InTarget; } - void OnBegin( T InCurrentValue ) + + void OnBegin(T InCurrentValue) { - if ( !bHasStart ) + if (!bHasStart) { StartValue = InCurrentValue; CurrentValue = InCurrentValue; } } - bool Update( float Alpha ) + + bool Update(float Alpha) { const T OldValue = CurrentValue; - if ( Alpha >= 1 && bHasTarget ) + if (Alpha >= 1 && bHasTarget) { CurrentValue = TargetValue; } @@ -102,149 +111,165 @@ struct BUITWEEN_API FBUITweenInstance GENERATED_BODY() public: - FBUITweenInstance() { } - FBUITweenInstance( UWidget* pInWidget, float InDuration, float InDelay = 0 ) - : pWidget( pInWidget ) - , Duration( InDuration ) - , Delay( InDelay ) + FBUITweenInstance() { - ensure( pInWidget != nullptr ); + } + FBUITweenInstance(UWidget* pInWidget, float InDuration, float InDelay = 0) + : pWidget(pInWidget) + , Duration(InDuration) + , Delay(InDelay) + { + ensure(pInWidget != nullptr); } + void Begin(); - void Update( float InDeltaTime ); - void Apply( float EasedAlpha ); + void Update(float InDeltaTime); + void Apply(float EasedAlpha); - inline bool operator==( const FBUITweenInstance& other) const + inline bool operator==(const FBUITweenInstance& other) const { return pWidget == other.pWidget; } + bool IsComplete() const { return bIsComplete; } // EasingParam is used for easing functions that have a second parameter, like Elastic - FBUITweenInstance& Easing( EBUIEasingType InType, TOptional InEasingParam = TOptional() ) + FBUITweenInstance& Easing(const EBUIEasingType InType, const TOptional& InEasingParam = TOptional()) { EasingType = InType; EasingParam = InEasingParam; return *this; } - FBUITweenInstance& ToTranslation( const FVector2D& InTarget ) + FBUITweenInstance& ToTranslation(const FVector2D& InTarget) { - TranslationProp.SetTarget( InTarget ); + TranslationProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& ToTranslation( float X, float Y ) + + FBUITweenInstance& ToTranslation(float X, float Y) { - TranslationProp.SetTarget( FVector2D( X, Y ) ); + TranslationProp.SetTarget(FVector2D(X, Y)); return *this; } - FBUITweenInstance& FromTranslation( const FVector2D& InStart ) + + FBUITweenInstance& FromTranslation(const FVector2D& InStart) { - TranslationProp.SetStart( InStart ); + TranslationProp.SetStart(InStart); return *this; } - FBUITweenInstance& FromTranslation( float X, float Y ) + + FBUITweenInstance& FromTranslation(float X, float Y) { - TranslationProp.SetStart( FVector2D( X, Y ) ); + TranslationProp.SetStart(FVector2D(X, Y)); return *this; } - FBUITweenInstance& ToScale( const FVector2D& InTarget ) + FBUITweenInstance& ToScale(const FVector2D& InTarget) { - ScaleProp.SetTarget( InTarget ); + ScaleProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromScale( const FVector2D& InStart ) + + FBUITweenInstance& FromScale(const FVector2D& InStart) { - ScaleProp.SetStart( InStart ); + ScaleProp.SetStart(InStart); return *this; } - FBUITweenInstance& ToOpacity( float InTarget ) + FBUITweenInstance& ToOpacity(float InTarget) { - OpacityProp.SetTarget( InTarget ); + OpacityProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromOpacity( float InStart ) + + FBUITweenInstance& FromOpacity(float InStart) { - OpacityProp.SetStart( InStart ); + OpacityProp.SetStart(InStart); return *this; } - FBUITweenInstance& ToColor( const FLinearColor& InTarget ) + FBUITweenInstance& ToColor(const FLinearColor& InTarget) { - ColorProp.SetTarget( InTarget ); + ColorProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromColor( const FLinearColor& InStart ) + + FBUITweenInstance& FromColor(const FLinearColor& InStart) { - ColorProp.SetStart( InStart ); + ColorProp.SetStart(InStart); return *this; } - FBUITweenInstance& ToRotation( float InTarget ) + FBUITweenInstance& ToRotation(float InTarget) { - RotationProp.SetTarget( InTarget ); + RotationProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromRotation( float InStart ) + + FBUITweenInstance& FromRotation(float InStart) { - RotationProp.SetStart( InStart ); + RotationProp.SetStart(InStart); return *this; } - FBUITweenInstance& ToMaxDesiredHeight( float InTarget ) + FBUITweenInstance& ToMaxDesiredHeight(float InTarget) { - MaxDesiredHeightProp.SetTarget( InTarget ); + MaxDesiredHeightProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromMaxDesiredHeight( float InStart ) + + FBUITweenInstance& FromMaxDesiredHeight(float InStart) { - MaxDesiredHeightProp.SetStart( InStart ); + MaxDesiredHeightProp.SetStart(InStart); return *this; } - FBUITweenInstance& ToCanvasPosition( FVector2D InTarget ) + FBUITweenInstance& ToCanvasPosition(FVector2D InTarget) { - CanvasPositionProp.SetTarget( InTarget ); + CanvasPositionProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromCanvasPosition( FVector2D InStart ) + + FBUITweenInstance& FromCanvasPosition(FVector2D InStart) { - CanvasPositionProp.SetStart( InStart ); + CanvasPositionProp.SetStart(InStart); return *this; } - FBUITweenInstance& ToPadding( const FMargin& InTarget ) + FBUITweenInstance& ToPadding(const FMargin& InTarget) { - PaddingProp.SetTarget( FVector4( InTarget.Left, InTarget.Top, InTarget.Right, InTarget.Bottom ) ); + PaddingProp.SetTarget(FVector4(InTarget.Left, InTarget.Top, InTarget.Right, InTarget.Bottom)); return *this; } - FBUITweenInstance& FromPadding( const FMargin& InStart ) + + FBUITweenInstance& FromPadding(const FMargin& InStart) { - PaddingProp.SetStart( FVector4( InStart.Left, InStart.Top, InStart.Right, InStart.Bottom ) ); + PaddingProp.SetStart(FVector4(InStart.Left, InStart.Top, InStart.Right, InStart.Bottom)); return *this; } - FBUITweenInstance& ToVisibility( ESlateVisibility InTarget ) + FBUITweenInstance& ToVisibility(ESlateVisibility InTarget) { - VisibilityProp.SetTarget( InTarget ); + VisibilityProp.SetTarget(InTarget); return *this; } - FBUITweenInstance& FromVisibility( ESlateVisibility InStart ) + + FBUITweenInstance& FromVisibility(ESlateVisibility InStart) { - VisibilityProp.SetStart( InStart ); + VisibilityProp.SetStart(InStart); return *this; } - FBUITweenInstance& OnStart( const FBUITweenSignature& InOnStart ) + FBUITweenInstance& OnStart(const FBUITweenSignature& InOnStart) { OnStartedDelegate = InOnStart; return *this; } - FBUITweenInstance& OnComplete( const FBUITweenSignature& InOnComplete ) + + FBUITweenInstance& OnComplete(const FBUITweenSignature& InOnComplete) { OnCompleteDelegate = InOnComplete; return *this; @@ -252,11 +277,93 @@ struct BUITWEEN_API FBUITweenInstance FBUITweenInstance& ToReset() { - ScaleProp.SetTarget( FVector2D::UnitVector ); - OpacityProp.SetTarget( 1 ); - TranslationProp.SetTarget( FVector2D::ZeroVector ); - ColorProp.SetTarget( FLinearColor::White ); - RotationProp.SetTarget( 0 ); + ScaleProp.SetTarget(FVector2D::UnitVector); + OpacityProp.SetTarget(1); + TranslationProp.SetTarget(FVector2D::ZeroVector); + ColorProp.SetTarget(FLinearColor::White); + RotationProp.SetTarget(0); + return *this; + } + + FBUITweenInstance& ApplyChangedValues(const FBUITweenValues& FromValues, const FBUITweenValues& ToValues, const FBUITweenValues& InitialValues) + { + // Apply From Values if they are different from initial values + if (FromValues.HasTranslationChange(InitialValues)) + { + FromTranslation(FromValues.Translation); + } + if (FromValues.HasScaleChange(InitialValues)) + { + FromScale(FromValues.Scale); + } + if (FromValues.HasOpacityChange(InitialValues)) + { + FromOpacity(FromValues.Opacity); + } + if (FromValues.HasColorChange(InitialValues)) + { + FromColor(FromValues.Color); + } + if (FromValues.HasRotationChange(InitialValues)) + { + FromRotation(FromValues.Rotation); + } + if (FromValues.HasCanvasPositionChange(InitialValues)) + { + FromCanvasPosition(FromValues.CanvasPosition); + } + if (FromValues.HasPaddingChange(InitialValues)) + { + FromPadding(FromValues.Padding); + } + if (FromValues.HasVisibilityChange(InitialValues)) + { + FromVisibility(FromValues.Visibility); + } + if (FromValues.HasMaxDesiredHeightChange(InitialValues)) + { + FromMaxDesiredHeight(FromValues.MaxDesiredHeight); + } + + // Apply To Values if they are different from initial values + if (ToValues.HasTranslationChange(InitialValues)) + { + ToTranslation(ToValues.Translation); + } + if (ToValues.HasScaleChange(InitialValues)) + { + ToScale(ToValues.Scale); + } + if (ToValues.HasOpacityChange(InitialValues)) + { + ToOpacity(ToValues.Opacity); + } + if (ToValues.HasColorChange(InitialValues)) + { + ToColor(ToValues.Color); + } + if (ToValues.HasRotationChange(InitialValues)) + { + ToRotation(ToValues.Rotation); + } + if (ToValues.HasCanvasPositionChange(InitialValues)) + { + ToCanvasPosition(ToValues.CanvasPosition); + } + if (ToValues.HasPaddingChange(InitialValues)) + { + ToPadding(ToValues.Padding); + } + if (ToValues.HasVisibilityChange(InitialValues)) + { + ToVisibility(ToValues.Visibility); + } + if (ToValues.HasMaxDesiredHeightChange(InitialValues)) + { + ToMaxDesiredHeight(ToValues.MaxDesiredHeight); + } + + return *this; } @@ -264,9 +371,9 @@ struct BUITWEEN_API FBUITweenInstance void DoCompleteCleanup() { - if ( !bHasPlayedCompleteEvent ) + if (!bHasPlayedCompleteEvent) { - OnCompleteDelegate.ExecuteIfBound( pWidget.Get() ); + OnCompleteDelegate.ExecuteIfBound(pWidget.Get()); bHasPlayedCompleteEvent = true; } } @@ -299,4 +406,3 @@ struct BUITWEEN_API FBUITweenInstance bool bHasPlayedStartEvent = false; bool bHasPlayedCompleteEvent = false; }; - diff --git a/Source/BUITween/Public/BUITweenModule.h b/Source/BUITween/Public/BUITweenModule.h index ef59232..041b550 100644 --- a/Source/BUITween/Public/BUITweenModule.h +++ b/Source/BUITween/Public/BUITweenModule.h @@ -23,13 +23,13 @@ class FBUITweenModule : public IModuleInterface, public FTickableGameObject { RETURN_QUICK_DECLARE_CYCLE_STAT( UBUITween, STATGROUP_Tickables ); } - virtual bool IsTickableWhenPaused() const + virtual bool IsTickableWhenPaused() const override { return true; } - virtual bool IsTickableInEditor() const + virtual bool IsTickableInEditor() const override { - return false; + return true; } diff --git a/Source/BUITween/Public/BUITweenTypes.h b/Source/BUITween/Public/BUITweenTypes.h new file mode 100644 index 0000000..ab20aca --- /dev/null +++ b/Source/BUITween/Public/BUITweenTypes.h @@ -0,0 +1,119 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/Widget.h" +#include "Components/Image.h" +#include "Components/Border.h" +#include "Components/SizeBox.h" +#include "Components/OverlaySlot.h" +#include "Components/HorizontalBoxSlot.h" +#include "Components/VerticalBoxSlot.h" +#include "Blueprint/UserWidget.h" +#include "Components/CanvasPanelSlot.h" +#include "BUITweenTypes.generated.h" + +USTRUCT(BlueprintType) +struct BUITWEEN_API FBUITweenValues +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + FVector2D Translation = FVector2D::ZeroVector; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + FVector2D Scale = FVector2D(1.f, 1.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + float Opacity = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + FLinearColor Color = FLinearColor::White; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + float Rotation = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + FVector2D CanvasPosition = FVector2D::ZeroVector; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + FMargin Padding = FMargin(0.0f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + ESlateVisibility Visibility = ESlateVisibility::Visible; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tween") + float MaxDesiredHeight = 0.0f; + + + // Helper function to restore widget to these values + void RestoreToWidget(UWidget* TargetWidget) const + { + if (!TargetWidget) + { + return; + } + + // Restore basic transform values + FWidgetTransform Transform = TargetWidget->GetRenderTransform(); + Transform.Translation = Translation; + Transform.Scale = Scale; + Transform.Angle = Rotation; + TargetWidget->SetRenderTransform(Transform); + + TargetWidget->SetRenderOpacity(Opacity); + TargetWidget->SetVisibility(Visibility); + + // Restore color for different widget types + if (UUserWidget* UW = Cast(TargetWidget)) + { + UW->SetColorAndOpacity(Color); + } + else if (UImage* UI = Cast(TargetWidget)) + { + UI->SetColorAndOpacity(Color); + } + else if (UBorder* Border = Cast(TargetWidget)) + { + Border->SetContentColorAndOpacity(Color); + } + + // Restore canvas position + if (UCanvasPanelSlot* CanvasSlot = Cast(TargetWidget->Slot)) + { + CanvasSlot->SetPosition(CanvasPosition); + } + + // Restore max desired height + if (USizeBox* SizeBox = Cast(TargetWidget)) + { + SizeBox->SetMaxDesiredHeight(MaxDesiredHeight); + } + + // Restore padding for different slot types + if (UOverlaySlot* OverlaySlot = Cast(TargetWidget->Slot)) + { + OverlaySlot->SetPadding(Padding); + } + else if (UHorizontalBoxSlot* HorizontalBoxSlot = Cast(TargetWidget->Slot)) + { + HorizontalBoxSlot->SetPadding(Padding); + } + else if (UVerticalBoxSlot* VerticalBoxSlot = Cast(TargetWidget->Slot)) + { + VerticalBoxSlot->SetPadding(Padding); + } + } + + + + // Helper functions to check if a value is different from the initial value + bool HasTranslationChange(const FBUITweenValues& InitialValues) const { return Translation != InitialValues.Translation; } + bool HasScaleChange(const FBUITweenValues& InitialValues) const { return Scale != InitialValues.Scale; } + bool HasOpacityChange(const FBUITweenValues& InitialValues) const { return !FMath::IsNearlyEqual(Opacity, InitialValues.Opacity); } + bool HasColorChange(const FBUITweenValues& InitialValues) const { return Color != InitialValues.Color; } + bool HasRotationChange(const FBUITweenValues& InitialValues) const { return !FMath::IsNearlyEqual(Rotation, InitialValues.Rotation); } + bool HasCanvasPositionChange(const FBUITweenValues& InitialValues) const { return CanvasPosition != InitialValues.CanvasPosition; } + bool HasPaddingChange(const FBUITweenValues& InitialValues) const { return Padding != InitialValues.Padding; } + bool HasVisibilityChange(const FBUITweenValues& InitialValues) const { return Visibility != InitialValues.Visibility; } + bool HasMaxDesiredHeightChange(const FBUITweenValues& InitialValues) const { return !FMath::IsNearlyEqual(MaxDesiredHeight, InitialValues.MaxDesiredHeight); } +}; \ No newline at end of file diff --git a/Source/BUITweenBP/BUITweenBP.Build.cs b/Source/BUITweenBP/BUITweenBP.Build.cs new file mode 100644 index 0000000..7ed30e6 --- /dev/null +++ b/Source/BUITweenBP/BUITweenBP.Build.cs @@ -0,0 +1,30 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class BUITweenBP : ModuleRules +{ + public BUITweenBP(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "UMG", + "BUITween" + } + ); + } +} \ No newline at end of file diff --git a/Source/BUITweenBP/Private/BUITweenBPLibrary.cpp b/Source/BUITweenBP/Private/BUITweenBPLibrary.cpp new file mode 100644 index 0000000..5584661 --- /dev/null +++ b/Source/BUITweenBP/Private/BUITweenBPLibrary.cpp @@ -0,0 +1,107 @@ +#include "BUITweenBPLibrary.h" + + +UWidget* UBUITweenBPLibrary::PlayTween(UWidget* TargetWidget, const FBUITweenValues& FromValues, const FBUITweenValues& ToValues, float Duration, float Delay, + bool bRestoreInitialValues, bool bPlayFromStart, EBUIEasingType EasingType) +{ + return PlayTweenAndWatch(TargetWidget, FromValues, ToValues, FOnAnimationComplete(),Duration, Delay, bRestoreInitialValues, bPlayFromStart, EasingType); +} + +UWidget* UBUITweenBPLibrary::PlayTweenAndWatch(UWidget* TargetWidget, const FBUITweenValues& FromValues, const FBUITweenValues& ToValues, FOnAnimationComplete OnComplete, + float Duration, float Delay, bool bRestoreInitialValues, bool bPlayFromStart, EBUIEasingType EasingType) +{ + if (!TargetWidget) + { + return nullptr; + } + + // Get initial values from the widget + const FBUITweenValues InitialValues;// = GetInitialValues(TargetWidget); + auto& TweenInstance = UBUITween::Create(TargetWidget, Duration, Delay); + + if (bPlayFromStart) + { + TweenInstance.ToReset().Apply(1.0f); + } + TweenInstance.ApplyChangedValues(FromValues, ToValues, InitialValues); + + TweenInstance.OnComplete(FBUITweenSignature::CreateLambda([OnComplete, bRestoreInitialValues, InitialValues](UWidget* Owner) + { + if (bRestoreInitialValues) + { + InitialValues.RestoreToWidget(Owner); + } + OnComplete.ExecuteIfBound(); + })); + + // Set easing and begin + TweenInstance.Easing(EasingType).Begin(); + return TargetWidget; +} + +FBUITweenValues UBUITweenBPLibrary::GetInitialValues(UWidget* TargetWidget) +{ + FBUITweenValues Values; + if (!TargetWidget) + { + return Values; + } + + // Get basic transform values + Values.Translation = TargetWidget->GetRenderTransform().Translation; + Values.Rotation = TargetWidget->GetRenderTransform().Angle; + Values.Scale = TargetWidget->GetRenderTransform().Scale; + Values.Opacity = TargetWidget->GetRenderOpacity(); + Values.Visibility = TargetWidget->GetVisibility(); + + // Get color from different widget types + if (UUserWidget* UW = Cast(TargetWidget)) + { + Values.Color = UW->GetColorAndOpacity(); + } + else if (UImage* UI = Cast(TargetWidget)) + { + Values.Color = UI->GetColorAndOpacity(); + } + else if (UBorder* Border = Cast(TargetWidget)) + { + Values.Color = Border->GetContentColorAndOpacity(); + } + + // Get canvas position + if (UCanvasPanelSlot* CanvasSlot = Cast(TargetWidget->Slot)) + { + Values.CanvasPosition = CanvasSlot->GetPosition(); + } + + // Get max desired height + if (USizeBox* SizeBox = Cast(TargetWidget)) + { + Values.MaxDesiredHeight = SizeBox->GetMaxDesiredHeight(); + } + + // Get padding from different slot types + if (UOverlaySlot* OverlaySlot = Cast(TargetWidget->Slot)) + { + Values.Padding = OverlaySlot->GetPadding(); + } + else if (UHorizontalBoxSlot* HorizontalBoxSlot = Cast(TargetWidget->Slot)) + { + Values.Padding = HorizontalBoxSlot->GetPadding(); + } + else if (UVerticalBoxSlot* VerticalBoxSlot = Cast(TargetWidget->Slot)) + { + Values.Padding = VerticalBoxSlot->GetPadding(); + } + + return Values; +} + +void UBUITweenBPLibrary::RestoreInitialValues(UWidget* TargetWidget, const FBUITweenValues& InitialValues) +{ + if (!TargetWidget) + { + return; + } + InitialValues.RestoreToWidget(TargetWidget); +} diff --git a/Source/BUITweenBP/Private/BUITweenBPModule.cpp b/Source/BUITweenBP/Private/BUITweenBPModule.cpp new file mode 100644 index 0000000..aa83d72 --- /dev/null +++ b/Source/BUITweenBP/Private/BUITweenBPModule.cpp @@ -0,0 +1,3 @@ + #include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FDefaultModuleImpl, BUITweenBP); diff --git a/Source/BUITweenBP/Private/UTestPanel.cpp b/Source/BUITweenBP/Private/UTestPanel.cpp new file mode 100644 index 0000000..b299a7a --- /dev/null +++ b/Source/BUITweenBP/Private/UTestPanel.cpp @@ -0,0 +1,249 @@ +// Copy Right ZeroSoul + +#include "UTestPanel.h" +#include "BUITween.h" +#include "Components/TileView.h" + +void UUTestPanel::NativePreConstruct() +{ + Super::NativePreConstruct(); + if (IsValid(ItemWidgetClass) && ItemWidgetClass->ImplementsInterface(UUserListEntry::StaticClass())) + { + // TileView->SetEntryWidgetClass(ItemWidgetClass); + } + GenerateTestItems(); +} + +void UUTestPanel::NativeConstruct() +{ + Super::NativeConstruct(); +} + +void UUTestPanel::NativeDestruct() +{ + Super::NativeDestruct(); + if (TileView) + { + TileView->ClearListItems(); + } +} + +void UUTestPanel::GenerateTestItems() +{ + if (!TileView) + { + return; + } + + TileView->ClearListItems(); + + // Get all easing types + UEnum* EasingEnum = StaticEnum(); + UEnum* TweenEnum = StaticEnum(); + // this last enum is MAX, no need + for (int32 j = 0; j < TweenEnum->NumEnums() - 1; ++j) + { + if (TweenEnum->HasMetaData(TEXT("Hidden"), j)) + { + continue; + } + // Create test items for each easing type + for (int32 i = 0; i < EasingEnum->NumEnums() - 1; ++i) + { + if (EasingEnum->HasMetaData(TEXT("Hidden"), i)) + { + continue; + } + + int64 Value = EasingEnum->GetValueByIndex(i); + FString EasingName = EasingEnum->GetDisplayNameTextByIndex(i).ToString(); + EBUIEasingType EasingType = static_cast(Value); + + // Create an item for each tween type + + + ETweenType TweenType = static_cast(TweenEnum->GetValueByIndex(j)); + FString TweenName = TweenEnum->GetDisplayNameTextByIndex(j).ToString(); + + UTweenTestItem* TestItem = NewObject(this); + TestItem->EasingType = EasingType; + TestItem->TweenType = TweenType; + TestItem->DisplayName = FString::Printf(TEXT("%s - %s"), *EasingName, *TweenName); + + TileView->AddItem(TestItem); + } + } +} + +void UUTestPanel::PlayAllTweens() +{ + if (!TileView) + { + return; + } + + for (UObject* Item : TileView->GetListItems()) + { + if (UTestItemWidget* TestWidget = Cast(TileView->GetEntryWidgetFromItem(Item))) + { + TestWidget->Play(); + } + } +} + +void UUTestPanel::StopAllTweens() +{ + if (!TileView) + { + return; + } + + for (UObject* Item : TileView->GetListItems()) + { + if (UTestItemWidget* TestWidget = Cast(TileView->GetEntryWidgetFromItem(Item))) + { + TestWidget->Stop(); + } + } +} + +void UUTestPanel::ResetAllTweens() +{ + if (!TileView) + { + return; + } + + for (UObject* Item : TileView->GetListItems()) + { + if (UTestItemWidget* TestWidget = Cast(TileView->GetEntryWidgetFromItem(Item))) + { + TestWidget->Reset(); + } + } +} + +//////----------------------------------------------TestItemWidget------------------------------------------------------ + +void UTestItemWidget::NativePreConstruct() +{ + Super::NativePreConstruct(); +} + +void UTestItemWidget::NativeConstruct() +{ + Super::NativeConstruct(); +} + +void UTestItemWidget::NativeDestruct() +{ + Super::NativeDestruct(); + if (TweenWidget) + { + UBUITween::Clear(TweenWidget); + } +} + + +void UTestItemWidget::ApplyTweenAnimation(FBUITweenInstance* TweenInstance) +{ + if (!TweenWidget || !TweenInstance) + { + return; + } + + switch (TweenType) + { + case ETweenType::Scale: + TweenInstance->FromColor(FLinearColor::Blue).ToColor(FLinearColor::Blue); + TweenInstance->FromScale(FVector2D(1.0f, 1.0f)).ToScale(FVector2D(2.0f, 2.0f)); + break; + + case ETweenType::Opacity: + TweenInstance->FromOpacity(1.0f).ToOpacity(0.0f); + break; + + case ETweenType::Rotation: + TweenInstance->FromColor(FLinearColor::Yellow).ToColor(FLinearColor::Yellow); + TweenInstance->FromRotation(0.0f).ToRotation(360.0f); + break; + + case ETweenType::Translation: + TweenInstance->FromColor(FLinearColor::Red).ToColor(FLinearColor::Red); + + TweenInstance->FromTranslation(FVector2D(0.0f, 0.0f)).ToTranslation(FVector2D(100.0f, 0.0f)); + break; + + case ETweenType::Color: + TweenInstance->FromColor(FLinearColor::Green).ToColor(FLinearColor::Yellow); + break; + + case ETweenType::All: + TweenInstance->FromScale(FVector2D(1.0f, 1.0f)).ToScale(FVector2D(2.0f, 2.0f)) + .FromOpacity(1.0f).ToOpacity(0.5f) + .FromRotation(0.0f).ToRotation(360.0f) + .FromTranslation(FVector2D(0.0f, 0.0f)).ToTranslation(FVector2D(120.0f, -10.0f)) + .FromColor(FLinearColor::White).ToColor(FLinearColor::Green); + break; + } + + TweenInstance->Easing(EasingType); +} + +void UTestItemWidget::Play() +{ + if (!TweenWidget) + { + return; + } + + if (UBUITween::GetIsTweening(TweenWidget)) + { + Reset(); + Play(); + } + else + { + auto TweenInstance = &UBUITween::Create(TweenWidget, 1.5f); + ApplyTweenAnimation(TweenInstance); + TweenInstance->OnComplete(FBUITweenSignature::CreateLambda([this](UWidget* Owner) + { + UBUITween::Create(TweenWidget, 0.5f) + .ToReset() + .OnComplete(FBUITweenSignature::CreateLambda([this](UWidget* Owner) + { + Play(); + } + )).Begin(); + })); + TweenInstance->Begin(); + } +} + +void UTestItemWidget::Stop() +{ + Reset(); +} + +void UTestItemWidget::Reset() +{ + if (TweenWidget) + { + UBUITween::Create(TweenWidget).ToReset().Apply(1.0f); + UBUITween::Clear(TweenWidget); + } +} + +void UTestItemWidget::NativeOnListItemObjectSet(UObject* ListItemObject) +{ + IUserObjectListEntry::NativeOnListItemObjectSet(ListItemObject); + + if (UTweenTestItem* TestItem = Cast(ListItemObject)) + { + EasingType = TestItem->EasingType; + TweenType = TestItem->TweenType; + DisplayName = TestItem->DisplayName; + Reset(); + Play(); + } +} diff --git a/Source/BUITweenBP/Public/BUITweenBP.h b/Source/BUITweenBP/Public/BUITweenBP.h new file mode 100644 index 0000000..e69de29 diff --git a/Source/BUITweenBP/Public/BUITweenBPLibrary.h b/Source/BUITweenBP/Public/BUITweenBPLibrary.h new file mode 100644 index 0000000..5a4ec86 --- /dev/null +++ b/Source/BUITweenBP/Public/BUITweenBPLibrary.h @@ -0,0 +1,54 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "BUITweenTypes.h" +#include "BUITween.h" +#include "BUITweenBPLibrary.generated.h" + +UCLASS() +class BUITWEENBP_API UBUITweenBPLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + DECLARE_DYNAMIC_DELEGATE_OneParam(FOnAnimationCompleteWithOwner, UWidget* ,Owner); + DECLARE_DYNAMIC_DELEGATE(FOnAnimationComplete); + + UFUNCTION(BlueprintCallable, Category = "UI|Tween", meta = (DisplayName = "Play UI Tween", DefaultToSelf="TargetWidget")) + static UWidget* PlayTween( + UWidget* TargetWidget, + const FBUITweenValues& FromValues, + const FBUITweenValues& ToValues, + float Duration = 0.3f, + float Delay = 0.0f, + bool bRestoreInitialValues = false, + bool bPlayFromStart = false, + EBUIEasingType EasingType = EBUIEasingType::InOutQuad + + ); + + UFUNCTION(BlueprintCallable, Category = "UI|Tween", meta = (DisplayName = "Play UI Tween And Watch", DefaultToSelf="TargetWidget")) + static UWidget* PlayTweenAndWatch( + UWidget* TargetWidget, + const FBUITweenValues& FromValues, + const FBUITweenValues& ToValues, + FOnAnimationComplete OnComplete, + float Duration = 0.3f, + float Delay = 0.0f, + bool bRestoreInitialValues = false, + bool bPlayFromStart = false, + EBUIEasingType EasingType = EBUIEasingType::InOutQuad + ); + + // Helper function to get initial values from widget + UFUNCTION(BlueprintCallable, Category = "UI|Tween", meta = (DisplayName = "Get UI Initial Values", DefaultToSelf="TargetWidget")) + static FBUITweenValues GetInitialValues(UWidget* TargetWidget); + + // Helper function to restore initial values + UFUNCTION(BlueprintCallable, Category = "UI|Tween", meta = (DisplayName = "Restore UI Initial Values", DefaultToSelf="TargetWidget")) + static void RestoreInitialValues(UWidget* TargetWidget, const FBUITweenValues& InitialValues); + +}; + + diff --git a/Source/BUITweenBP/Public/UTestPanel.h b/Source/BUITweenBP/Public/UTestPanel.h new file mode 100644 index 0000000..c597ed5 --- /dev/null +++ b/Source/BUITweenBP/Public/UTestPanel.h @@ -0,0 +1,111 @@ +#pragma once + +#include "CoreMinimal.h" +#include "BUITweenInstance.h" +#include "Blueprint/IUserObjectListEntry.h" +#include "Blueprint/UserWidget.h" +#include "UTestPanel.generated.h" + +class UTileView; +enum class EBUIEasingType : uint8; + +UENUM(BlueprintType) +enum class ETweenType : uint8 +{ + Scale UMETA(DisplayName = "Scale"), + Opacity UMETA(DisplayName = "Opacity"), + Rotation UMETA(DisplayName = "Rotation"), + Translation UMETA(DisplayName = "Translation"), + Color UMETA(DisplayName = "Color"), + All UMETA(DisplayName = "All Properties") +}; + +UCLASS() +class UTweenTestItem : public UObject +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tween") + EBUIEasingType EasingType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tween") + ETweenType TweenType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tween") + FString DisplayName; +}; + +/** + * + */ +UCLASS() +class BUITWEENBP_API UUTestPanel : public UUserWidget +{ + GENERATED_BODY() + + virtual void NativePreConstruct() override; + virtual void NativeConstruct() override; + virtual void NativeDestruct() override; + +protected: + // + void GenerateTestItems(); + +public: + UPROPERTY(EditAnywhere, Category="UI | Tween") + TSubclassOf ItemWidgetClass; + + UPROPERTY(BlueprintReadWrite, Category="UI | Tween", meta=(BindWidget)) + TObjectPtr TileView; + + UFUNCTION(BlueprintCallable, Category="UI | Tween") + void PlayAllTweens(); + + UFUNCTION(BlueprintCallable, Category="UI | Tween") + void StopAllTweens(); + + UFUNCTION(BlueprintCallable, Category="UI | Tween") + void ResetAllTweens(); +}; + +/** + * + */ +UCLASS() +class UTestItemWidget : public UUserWidget, public IUserObjectListEntry +{ + GENERATED_BODY() + + virtual void NativePreConstruct() override; + virtual void NativeConstruct() override; + virtual void NativeDestruct() override; + + virtual void NativeOnListItemObjectSet(UObject* ListItemObject) override; + +protected: + void ApplyTweenAnimation(FBUITweenInstance* InTweenInstance); + +public: + + UPROPERTY(BlueprintReadWrite, Category="Tween") + EBUIEasingType EasingType; + + UPROPERTY(BlueprintReadWrite, Category="Tween") + ETweenType TweenType; + + UPROPERTY(BlueprintReadWrite, Category="Tween") + FString DisplayName; + + UPROPERTY(BlueprintReadWrite, meta=(BindWidget)) + TObjectPtr TweenWidget; + + UFUNCTION(BlueprintCallable, Category="Tween") + void Play(); + + UFUNCTION(BlueprintCallable, Category="Tween") + void Stop(); + + UFUNCTION(BlueprintCallable, Category="Tween") + void Reset(); +};