Skip to content

Tween System

github-actions[bot] edited this page Aug 29, 2026 · 2 revisions

Tween System

SparkEngine provides a handle-based tween system for smoothly interpolating values over time. The system supports sequencing, parallel composition, configurable easing, looping, and lifecycle control — suitable for UI animations, camera movements, gameplay feedback, and any time-driven interpolation.

Release boundary: This is implementation guidance, not a support claim. The stable-v1 profile remains blocked and does not separately certify the tween system's feature breadth. The required Windows editor product is inside the profile, but its own release evidence is also blocked.

Source: SparkEngine/Source/Engine/Tween/TweenSystem.h Namespace: Spark Tests: Tests/TestTween.cpp (14 test cases)


Table of Contents


Overview

The tween system solves the problem of time-based value interpolation without scattering timer logic across update functions. Instead of manually tracking elapsed time and computing lerp values, you describe the interpolation once and let the system drive it.

┌─────────────────────────────────────────────────────────────┐
│                       Game Code                             │
│   TweenFloat(), CreateSequence(), CreateParallel()          │
├─────────────────────────────────────────────────────────────┤
│                      TweenSystem                            │
│  ┌──────────────┬──────────────────┬──────────────────────┐ │
│  │ Handle       │ Update           │ Composition          │ │
│  │ Registry     │ Loop             │                      │ │
│  │              │                  │                      │ │
│  │ Create()     │ Update(dt)       │ CreateSequence()     │ │
│  │ Get()        │ (drives all      │ CreateParallel()     │ │
│  │ Cancel()     │  active tweens)  │                      │ │
│  └──────────────┴──────────────────┴──────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│  TweenInstance          TweenHandle         EaseType        │
│  (value, callbacks,     (uint32_t ID)       (10 easing      │
│   easing, loop)                              functions)      │
└─────────────────────────────────────────────────────────────┘

Key Types

Type Responsibility
TweenSystem Singleton manager: creates, updates, and destroys tweens by handle
TweenInstance A single interpolation with duration, easing, callbacks, and loop configuration
TweenHandle Lightweight uint32_t identifier for referencing a tween
EaseType Enum of 10 easing functions controlling the interpolation curve
TweenState Enum tracking tween lifecycle: Pending, Running, Paused, Completed
LoopMode Enum for loop behavior: None, Loop, PingPong

Easing Functions

The EaseType enum provides 10 built-in easing curves. The ApplyEase() free function maps a linear t in [0, 1] to the eased value.

float ApplyEase(EaseType ease, float t);
EaseType Behavior Best For
Linear Constant speed Mechanical motion, progress bars
EaseInQuad Slow start, accelerate Objects starting from rest
EaseOutQuad Fast start, decelerate Objects coming to rest
EaseInOutQuad Slow start and end Smooth camera transitions
EaseInCubic Slower start than quad Dramatic acceleration
EaseOutCubic Slower end than quad Soft landing effects
EaseInOutCubic Smooth S-curve Polished UI animations
EaseInElastic Elastic wind-up Anticipation effects
EaseOutElastic Elastic overshoot Bouncy UI elements
EaseOutBounce Bouncing at end Physical bounce effects

TweenInstance

A TweenInstance represents a single interpolation operation. It is created by the TweenSystem and configured via a chaining API.

class TweenInstance
{
public:
    using UpdateCallback = std::function<void(float t)>;
    using CompleteCallback = std::function<void()>;

    TweenInstance(float duration, UpdateCallback onUpdate,
                  EaseType ease = EaseType::Linear);

    TweenInstance& OnComplete(CompleteCallback callback);
    TweenInstance& SetLoop(LoopMode mode, int32_t count = -1);
    TweenInstance& SetDelay(float delay);
    TweenInstance& SetPlayRate(float rate);

    void Pause();
    void Resume();
    void Cancel();
    void Restart();

    TweenState GetState() const;
    float GetProgress() const;   // elapsed / duration
    float GetElapsed() const;
};

Chaining API

After creating a tween, configure it with method chaining:

auto handle = tweenSystem.Create(1.0f, [&](float t)
{
    opacity = t;  // t is the eased value in [0, 1]
}, EaseType::EaseOutQuad);

if (auto* tween = tweenSystem.Get(handle))
{
    tween->OnComplete([]() { LOG_INFO("Fade complete"); })
          .SetDelay(0.5f)
          .SetPlayRate(2.0f);
}

Loop Modes

LoopMode Behavior
None Play once, then complete
Loop Restart from the beginning after each cycle
PingPong Alternate forward and backward each cycle

The count parameter controls how many times to loop. Pass -1 for infinite looping.

tween->SetLoop(LoopMode::PingPong, 3);  // 3 cycles: forward, back, forward
tween->SetLoop(LoopMode::Loop, -1);     // infinite loop

Lifecycle Control

Method Effect
Pause() Freezes the tween at its current progress
Resume() Continues from where it was paused
Cancel() Stops and marks as Completed (no callback)
Restart() Resets elapsed time and resumes from the beginning

TweenHandle

using TweenHandle = uint32_t;
constexpr TweenHandle INVALID_TWEEN = 0;

Handles are monotonically increasing IDs starting at 1. Use INVALID_TWEEN to check for invalid or uninitialized handles.


TweenSystem

Singleton Access

auto& ts = TweenSystem::GetInstance();
ts.Initialize();

Creating Tweens

// Low-level: create with custom update callback
TweenHandle Create(float duration, TweenInstance::UpdateCallback onUpdate,
                   EaseType ease = EaseType::Linear);

// Get the instance for further configuration
TweenInstance* Get(TweenHandle handle);

Convenience Helpers

// Interpolate a float reference from start to end
TweenHandle TweenFloat(float& target, float from, float to,
                       float duration, EaseType ease = EaseType::Linear);

// Fire a callback after a delay (no interpolation)
TweenHandle TweenDelay(float delay, TweenInstance::CompleteCallback onComplete);

Composition

Tweens can be composed into sequences (one after another) or parallel groups (all at once):

// Play tweens one after another
TweenHandle CreateSequence(std::vector<TweenHandle> tweens);

// Play all tweens simultaneously, complete when the longest finishes
TweenHandle CreateParallel(std::vector<TweenHandle> tweens);

Update Loop

Call Update() once per frame from the main game loop:

ts.Update(deltaTime);

Completed tweens are automatically removed after their completion callback fires.

Method Description
Cancel(handle) Cancel a specific tween
CancelAll() Cancel all active tweens
Pause(handle) Pause a specific tween
Resume(handle) Resume a paused tween
GetActiveTweenCount() Number of tweens currently active
Console_GetStatus() Formatted status string for debug console

Practical Examples

Fade In UI Element

auto& ts = TweenSystem::GetInstance();

float panelOpacity = 0.0f;
auto handle = ts.TweenFloat(panelOpacity, 0.0f, 1.0f, 0.3f,
                            EaseType::EaseOutQuad);

Camera Dolly

auto& ts = TweenSystem::GetInstance();

float cameraZ = 0.0f;
auto dolly = ts.TweenFloat(cameraZ, 0.0f, 10.0f, 2.0f,
                           EaseType::EaseInOutCubic);

if (auto* tween = ts.Get(dolly))
{
    tween->OnComplete([]()
    {
        LOG_INFO("Camera dolly complete");
    });
}

Sequenced Door Animation

auto& ts = TweenSystem::GetInstance();

float doorAngle = 0.0f;
float lightIntensity = 0.0f;

// Step 1: open the door
auto openDoor = ts.TweenFloat(doorAngle, 0.0f, 90.0f, 1.0f,
                              EaseType::EaseOutCubic);

// Step 2: wait 0.5s
auto pause = ts.TweenDelay(0.5f, nullptr);

// Step 3: fade in the light
auto fadeLight = ts.TweenFloat(lightIntensity, 0.0f, 1.0f, 0.5f,
                               EaseType::EaseInQuad);

// Play in order
auto sequence = ts.CreateSequence({openDoor, pause, fadeLight});

Looping Pulse Effect

auto& ts = TweenSystem::GetInstance();

float scale = 1.0f;
auto pulse = ts.Create(0.8f, [&](float t)
{
    scale = 1.0f + 0.1f * t;
}, EaseType::EaseInOutQuad);

if (auto* tween = ts.Get(pulse))
{
    tween->SetLoop(LoopMode::PingPong, -1);  // infinite pulse
}

Relationship to Utils/Tween.h

SparkEngine has two tween systems:

Feature Utils/Tween.h Engine/Tween/TweenSystem.h
Complexity Simple, standalone Full-featured singleton
Handle-based No Yes
Sequencing No Yes (CreateSequence)
Parallel groups No Yes (CreateParallel)
Loop modes No Yes (Loop, PingPong)
Delay / play rate No Yes
Best for Quick one-off lerps Handle-owning animation systems

The TweenSystem in Engine/Tween/ is the handle-owning engine API. Utils/Tween.h is a lightweight helper for simple cases where handle management is unnecessary.


Integration

  • Main loop: TweenSystem::GetInstance().Update(deltaTime) must be called each frame
  • Initialization: Call TweenSystem::GetInstance().Initialize() at engine startup
  • Shutdown: Call TweenSystem::GetInstance().Shutdown() at engine teardown
  • Console: Console_GetStatus() provides debug output for the SparkConsole

See Also

  • Coroutine System — For complex async gameplay sequences with branching logic
  • Animation — For skeletal animation and state machines
  • UI System — Common use case for tween-driven transitions
  • Event System — Trigger tweens in response to game events

SparkEngine Wiki

Website Entry Points

Getting Started

Engine Subsystems

Gameplay & Tools

Platform Support

Graphics

Advanced

Development & Process

Research & Analysis

Engineering Notes & Audits

Specifications

Reference

Clone this wiki locally