Guides Tweening What Are Tweens?

Tweening 3 min read Updated Apr 2026

What Are Tweens?

Tweens are short scripted motions that move a value smoothly from one state to another.

Use them when you want authored-feeling motion without hand-writing the same timer math in update() again and again.

A tween is a good fit for work like:

  • sliding a door open
  • pulsing a pickup when it spawns
  • nudging a button or panel into place
  • moving a camera target to a new position
  • rotating a collectible so it settles into place
  • scaling an object up for a hit or reward effect

If the behaviour sounds like "go from here to there over this much time", it probably wants a tween.

The Mental Model

Without tweening, you usually write code like this:

public function update(): void
{
    $this->elapsed += Time::deltaTime();
    $t = min(1.0, $this->elapsed / $this->duration);
    $this->transform->localPosition = Vector3::lerp($this->start, $this->end, $t);
}

That works, but it spreads motion rules across fields, timers, and update loops.

A tween lets you say the same thing directly:

Tween::moveLocalTo($this->transform, new Vector3(3.0, 0.0, 0.0), 0.35);

Lenga then advances the motion for you. Your script creates the motion and keeps the handle only if it needs to control or wait for it.

Where Tweening Fits

Tweening does not replace every kind of animation.

Use sprite or animation clips when:

  • you are changing frames in a sprite sheet
  • a character needs authored pose animation
  • a state machine should choose between named animation states

Use tweens when:

  • the object or UI needs a procedural move, rotate, or scale
  • the target changes while the game is running
  • the motion is short and gameplay-driven
  • code should describe an effect, not manually manage a timer

Use normal update() movement when:

  • the object is continuously controlled by input
  • physics should own the motion
  • every frame needs custom steering or collision decisions

The Tweening Tools

  • Tween::moveTo(...)
  • Tween::moveLocalTo(...)
  • Tween::scaleTo(...)
  • Tween::rotateTo(...)
  • Tween::rotateLocalTo(...)
  • TweenOptions for delay, easing, unscaled time, and relative motion
  • TweenHandle for pause, resume, cancel, and state checks
  • TweenHandle::wait() for coroutine sequencing

These tools are enough for the most common scripted motion: moving, rotating, scaling, delaying, easing, interrupting, and sequencing transform changes.

A First Example

This script makes an object rise slightly when the scene starts.

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Vector3;
use Lenga\Engine\Tweening\EasingFunction;
use Lenga\Engine\Tweening\Tween;
use Lenga\Engine\Tweening\TweenOptions;

final class PickupIntro extends Behaviour
{
    public function start(): void
    {
        Tween::moveLocalTo(
            $this->transform,
            new Vector3(0.0, 0.35, 0.0),
            0.25,
            TweenOptions::make()
                ->relative()
                ->ease(EasingFunction::EaseOutCubic),
        );
    }
}

The important details are:

  • $this->transform is the thing being moved
  • new Vector3(0.0, 0.35, 0.0) is the destination
  • 0.25 is the duration in seconds
  • relative() means "move by this amount", not "move to exactly this local position"
  • EasingFunction::EaseOutCubic makes the motion start quickly and settle gently

World Space or Local Space?

The transform tween methods come in world-space and local-space pairs.

Use world-space tweens when the destination is a scene position:

Tween::moveTo($this->transform, new Vector3(5.0, 2.0, 0.0), 0.4);

Use local-space tweens when the object is part of a parented setup:

Tween::moveLocalTo($this->transform, new Vector3(0.0, 1.0, 0.0), 0.4);

Local-space tweens are usually the safer default for pickups, child objects, weapons, UI-like world objects, and anything parented under another object.

Common Mistakes

Starting the same tween every frame

Do not start a tween unconditionally in update().

public function update(): void
{
    Tween::moveLocalTo($this->transform, new Vector3(0.0, 1.0, 0.0), 0.2);
}

That creates a new tween every frame. Start tweens in response to an event, state change, input press, collision, or coroutine step.

Using a tween for physics movement

If an object is controlled by physics, prefer moving it through the physics API. A tween writes transform values directly, so it is best for authored motion, effects, UI-like motion, and non-physics objects.

Replacing a gameplay state machine with motion

Tweening can make a state change feel better, but it should not hide the state change. Keep important game rules explicit, then use tweens to present them clearly.

What Comes Next

Read these in order if you are learning the feature:

  1. Move, Rotate, and Scale with Tweens
  2. Sequence Tweens with Coroutines
  3. Control Tweens During Gameplay