Home

Awesome

Fresh Cooked Tweens

A tweening library for Unreal Engine, by Jared Cook

UE4 and UE5 supported

C++:

FCTween::Play(
    GetActorLocation(),
    GetActorLocation() + FVector(0, 0, 50),
    [&](FVector t)
    {
        SetActorLocation(t);
    },
    2.0f,
    EFCEase::OutCubic);

Blueprints:

Table of Contents

  1. Plugin Setup
  2. Blueprints
  3. C++
  4. Easing Functions
  5. Performance
  6. Platforms
  7. References
  8. License

Why Tweens?

Tweening libraries provide convenient curve equations to ease a value between a start and end, like a position, scale, color, anything you want to smoothly change. They are useful in small projects and large projects. Many polished UIs have some kind of tween operating.

It can be a large efficiency gain to let a programmer or designer quickly set up a tween with easily tunable values, instead of taking up the art department's precious time tweaking curves manually.

In big projects, they are great for those little polish items. Some small projects can find broader use for them:

WaddleRotator = GetMesh()->GetRelativeRotation();
WaddleTween = FCTween::Play(
    -WADDLE_ANGLE, WADDLE_ANGLE,
    [&](float t)
    {
        WaddleRotator.Pitch = t;
        GetMesh()->SetRelativeRotation(WaddleRotator);
    },
    WADDLE_SECS, EFCEase::InOutSine)
                  ->SetYoyo(true)
                  ->SetLoops(-1);

Functionality already in Unreal Engine

It's good to know what is already available to you in-engine before installing other tools.

FMath::SpringDamper()
FMath::CriticallyDampedSmoothing()
UKismetMathLibrary::FloatSpringInterp()
UKismetMathLibrary::QuaternionSpringInterp()

Plugin Setup

PublicDependencyModuleNames.AddRange(new[] {"FCTween"});

<a name="blueprintsection"></a>

Blueprints

Basic Usage

Add a BP task from the "Tween" category.

Give it a start and end value, and hook up the Apply Easing pin to your update logic. The Value pin gives you the current value each frame. Here is an example of how to move an actor up 50 units:

Which looks like this:

Here is one that does an infinite bobbing animation:

If you need to do something with the created tween later on, such as stopping it after an interaction, you can use the Async Task pin to operate on the tween.

Custom Curve

Use one of the versions under "Tweens/Custom Curve" to provide a UE curve as the easing function. These work best with a curve that goes from t=0 to t=1, and then adjust the duration in the tween, for design flexibility.

Ease

Ease a 0-1 float with the given easing function. This is similar to UE's default Ease node, but it has all the FCTween functions available.

Ease with Params

Same as ease, but you can override the baked in parameters for Elastic, Bounce, Back, and Smoothstep. 0 means no override provided. Default values are listed in the comments, if you hover over the param.

<a name="cppsection"></a>

C++

Module Setup

If you would like to set up FCTween as a code module, instead of a plugin (for easily making code changes to FCTween), expand the section below:

<details> <summary>Expand</summary>

PublicDependencyModuleNames.AddRange(new[] {"FCTween"});
"Modules": [
    {
        "Name": "MyProject",
        "Type": "Runtime",
        "LoadingPhase": "Default"
    },
    {
        "Name": "FCTween",
        "Type": "Runtime",
        "LoadingPhase": "Default"
    }
],

</details>

Basic Usage

Basic example:

FCTween::Play(0, 1, [&](float t) { Foo = t; }, .5f);

With options:

FCTween::Play(0, 1, [&](float t) { Foo = t; }, .5f, EFCEase::OutElastic)
    ->SetLoops(2)
    ->SetYoyo(true)
    ->SetOnLoop([&]() { /* on loop functionality */ });

Using a custom curve

FCTween won't take in a UCurve (outside of Blueprints, which has helpers that do this same thing), but it's just two extra lines to do the lerp yourself:

UPROPERTY(EditAnywhere)
UCurveFloat* CustomCurve;

FVector Start = FVector::ZeroVector;
FVector End = FVector(0, 0, 150);

FCTween::Play(
    0, 1,
    [&](float t)
    {
        float EasedFloat = CustomCurve->GetFloatValue(t);
        FVector EasedVector = FMath::Lerp(Start, End, EasedFloat);
        SetActorLocation(OriginalPos + EasedVector);
    },
    10.0f, EFCEase::Linear);

Setting start/end values during execution

Use the specific derived class to set StartValue or EndValue during execution.

FCTweenInstanceVector* Tween = nullptr;

virtual void BeginPlay() override
{
    Tween = FCTween::Play(
        Target1->GetActorLocation(), Target2->GetActorLocation(), [&](FVector t) { SetActorLocation(t); }, 10.0f);
    // the set functions return the base tween class, be aware you'll have to static_cast it 
    // if you include it in the same line
    Tween->SetLoops(-1);
}
virtual void Tick(float DeltaSeconds) override
{
    Tween->StartValue = Target1->GetActorLocation();
    Tween->EndValue = Target2->GetActorLocation();
}

Safety / avoiding errors

If your tween's update is calling code on an actor, and that actor gets destroyed, but the tween system is still running the tween, your update lambda will throw an error.

To avoid this, you could:

FCTween::Play(
    GetActorLocation(), GetActorLocation() + FVector(0, 0, 50),
    [&](FVector t)
    {
        if (IsValid(this))
        {
            SetActorLocation(t);
        }
    },
    2.0f);
UPROPERTY()
UFCTweenUObject* TweenObj;
	
TweenObj = FCTween::Play()
    ->CreateUObject();

Tween Pointers

#pragma once

#include "FCTween.h"
#include "FCTweenInstance.h"

#include "TweenExample.generated.h"

UCLASS()
class ATweenExample : public AActor
{
	GENERATED_BODY()
	
public:
	UPROPERTY()
	int Foo = 0;

	// raw pointer
	FCTweenInstance* Tween = nullptr;
    
	// UObject wrapper version
	UPROPERTY()
	UFCTweenUObject* TweenUObj;

	virtual void BeginPlay() override
	{
		Super::BeginPlay();

		// tween a float from 0 to 1, over .5 seconds, infinitely looping
		Tween = FCTween::Play(0, 1, [&](float t) { Foo = t; }, .5f)
			->SetLoops(-1);
		
		// UObject version
		TweenUObj = FCTween::Play()
		->CreateUObject();
	}

	virtual void BeginDestroy() override
	{
		if (Tween != nullptr)
		{
			// tells FCTween to recycle this tween
			Tween->Destroy();
			Tween = nullptr;
		}
		
		// UObject version
		if (IsValid(TweenUObj))
		{
			TweenUObj->Destroy();
		}

		Super::BeginDestroy();
	}
};

Manual Memory Management

If you have a case that the recycling system doesn't work for, or need to do something custom, you can manage the memory and the update manually.

#pragma once

#include "FCTween.h"
#include "FCTweenInstance.h"

#include "TweenExample.generated.h"

UCLASS()
class ATweenExample : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY()
	int Foo = 0;

	FCTweenInstanceVector* ManualTween = nullptr;

	virtual void BeginPlay() override
	{
		Super::BeginPlay();

		ManualTween = new FCTweenInstanceVector();
		ManualTween->Initialize( 
			FVector::ZeroVector, FVector::OneVector, [&](FVector t) { SetActorLocation(t); }, .5f, EFCEase::OutQuad);
		ManualTween->SetYoyo(true)
		    ->SetLoops(-1);
	}

	virtual void BeginDestroy() override
	{
		if (ManualTween != nullptr)
		{
			delete ManualTween;
			ManualTween = nullptr;
		}

		Super::BeginDestroy();
	}

	virtual void Tick(float DeltaSeconds) override
	{
		Super::Tick(DeltaSeconds);
		
		ManualTween->Update(DeltaSeconds, false);
	}
};

Easing Functions

Useful quick reference: https://easings.net/

AvailableFunctions
LinearInCubicInExpoInBounce
SmoothstepOutCubicOutExpoOutBounce
SteppedInOutCubicInOutExpoInOutBounce
InSineInQuartInCircInBack
OutSineOutQuartOutCircOutBack
InOutSineInOutQuartInOutCircInOutBack
InQuadInQuintInElastic
OutQuadOutQuintOutElastic
InOutQuadInOutQuintInOutElastic

In/Out explanation

Most functions have an In, Out, and InOut version. This indicates which end of the function the easing takes place.

"In" means the easing happens at the start:

"Out" means the easing happens at the end:

"InOut" means the easing happens at start and end:

Examples

Linear

Sine

Quadratic

Cubic

Quartic

Quintic

Exponential

Circular

Smoothstep

Stepped

Elastic

Bounce

InBack

OutBack

Performance

Stress Tests

Here is the stress testing project, if you want to run it yourself: https://github.com/jdcook/ue_tween_library_stress_test

Memory is the difference shown in the Low Level Memory Tracker once the tweens initialize. I don't believe any of the libraries take up that many Megabytes, that is just what is displayed in the LLM. So it's just to get an idea of how the libraries compare.

20,000 tweens on startup, +1 per frame

Initialize MillisecondsFrames Per SecondFreeze after startup tweens completeMemory
FCTween1.39 ms60 fps0~9MB
BUITween10.06 ms55 to 60 fps0~10 MB
iTween282 ms11 to 46 fps2 seconds~41MB

40,000 tweens on startup, +40 per frame

Initialize MillisecondsFrames Per SecondFreeze after startup tweens completeMemory
FCTween2.4 ms60 fps0~21MB
BUITween25.58 ms40 to 60 fps7 seconds~34 MB
iTween578 ms6 to 24 fps8 seconds~88MB

80,000 tweens on startup, +80 per frame

Initialize MillisecondsFrames Per SecondFreeze after startup tweens completeMemory
FCTween4.23 ms60 fps0~44MB
BUITween50.05 ms27 to 60 fps22 seconds~50MB
iTween1207 ms3 to 12 fps23 seconds~175MB

Notes on performance

Platforms

I've only tested packaging for Windows, so if you are shipping on Linux, Mac, Android, iOS, or a console, be sure to test packaging early.

References

License

MIT

The easing equations themselves are derivative of other people's work, and their licenses have been included in FCEasing.h