Repository navigation
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-v1profile 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)
- Overview
- Easing Functions
- TweenInstance
- TweenHandle
- TweenSystem
- Practical Examples
- Relationship to Utils/Tween.h
- Integration
- See Also
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) │
└─────────────────────────────────────────────────────────────┘
| 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 |
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 |
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;
};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);
}| 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| 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 |
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.
auto& ts = TweenSystem::GetInstance();
ts.Initialize();// 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);// 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);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);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 |
auto& ts = TweenSystem::GetInstance();
float panelOpacity = 0.0f;
auto handle = ts.TweenFloat(panelOpacity, 0.0f, 1.0f, 0.3f,
EaseType::EaseOutQuad);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");
});
}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});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
}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.
-
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
- 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
Published from 2b03dc797148. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers