reckfellENGINE

AnimationPlayer: clips and playback

A complete example, keys, libraries, pause, restart and completion.

On this page

Run the example

  1. Create a level with an Entity3D root. Add AnimationClips.cs and PlayerAnimationDemo.cs to the C# project; attach the latter to the root. Use a separate level for each example in this section.
  2. Add Input Map actions animation_restart on R, animation_pause on Space and animation_fall on F. Build and run the current level with F6.
  3. A blue cube rises 2 units in one second and stops. R restarts the rise; Space pauses or resumes playback. F plays the downward clip from its upper pose.

Code creates the camera, light, visual entity and AnimationPlayer. This demonstrates properties, not a physics door; the cube has no collision body. The shared helper is also required by the tracks and AnimationTree examples.

AnimationClips.cs
C#
using Reckfell;

public static class AnimationClips
{
    public static Animation Value(string path, Variant from, Variant to, double seconds, bool loop = false)
    {
        var clip = new Animation
        {
            Length = seconds,
            LoopMode = loop ? Animation.LoopModeEnum.Linear : Animation.LoopModeEnum.None
        };
        int track = clip.AddTrack(Animation.TrackType.Value);
        clip.TrackSetPath(track, new EntityPath(path));
        clip.ValueTrackSetUpdateMode(track, Animation.UpdateMode.Continuous);
        clip.TrackInsertKey(track, 0, from);
        clip.TrackInsertKey(track, seconds, to);
        return clip;
    }

    public static AnimationLibrary Library()
    {
        var library = new AnimationLibrary();
        library.AddAnimation("RESET", Value("Visual:position", Vector3.Zero, Vector3.Zero, .001));
        var rise = Value("Visual:position", Vector3.Zero, new Vector3(0,2,0), 1);
        rise.AddMarker("start", .2);
        rise.AddMarker("end", .8);
        library.AddAnimation("rise", rise);
        library.AddAnimation("fall", Value("Visual:position", new Vector3(0,2,0), Vector3.Zero, 1));
        library.AddAnimation("idle", Value("Visual:position", Vector3.Zero, Vector3.Zero, 1, true));
        library.AddAnimation("walk", Value("Visual:position", new Vector3(0,1,0), new Vector3(0,1,0), 1, true));
        library.AddAnimation("run", Value("Visual:position", new Vector3(0,3,0), new Vector3(0,3,0), 1, true));
        var jump = Value("Visual:position", Vector3.Zero, Vector3.Zero, 1);
        jump.TrackInsertKey(0, .5, new Vector3(0,2,0));
        library.AddAnimation("jump", jump);
        return library;
    }

    public static AnimationPlayer Stage(Entity3D owner)
    {
        var visual = new Entity3D { Name = "Visual" };
        var mesh = new MeshInstance3D
        {
            Mesh = new BoxMesh(),
            MaterialOverride = new StandardMaterial3D { AlbedoColor = new Color(.15f,.35f,1) }
        };
        visual.AddChild(mesh);
        owner.AddChild(visual);
        var camera = new Camera3D { Name = "Camera", Position = new Vector3(5,4,7), Current = true };
        owner.AddChild(camera);
        camera.LookAt(owner.GlobalPosition + new Vector3(0,1,0));
        owner.AddChild(new DirectionalLight3D { RotationDegrees = new Vector3(-45,-30,0) });
        var player = new AnimationPlayer
        {
            Name = "AnimationPlayer",
            RootEntity = new EntityPath(".."),
            PlaybackAutoCapture = false
        };
        owner.AddChild(player);
        player.AddAnimationLibrary("", Library());
        return player;
    }
}
PlayerAnimationDemo.cs
C#
using Reckfell;

public partial class PlayerAnimationDemo : Entity3D
{
    public AnimationPlayer Player { get; private set; } = null!;

    public override void _Ready()
    {
        Player = AnimationClips.Stage(this);
        Player.Play("rise");
    }

    public void RestartRise()
    {
        Player.Stop();
        Player.Play("rise");
    }

    public void PauseOrResume()
    {
        if (Player.IsPlaying())
            Player.Pause();
        else
            Player.Play();
    }

    public override void _UnhandledInput(InputEvent @event)
    {
        if (@event.IsActionPressed("animation_restart"))
            RestartRise();
        else if (@event.IsActionPressed("animation_pause"))
            PauseOrResume();
        else if (@event.IsActionPressed("animation_fall"))
            Player.Play("fall");
        else
            return;
        GetViewport().SetInputAsHandled();
    }
}

Resolve the track target

The player's RootEntity = ".." points to the level root. Visual:position therefore addresses the child entity Visual and its position property. Before the colon is an entity path; after it is a property or resource-property chain.

String paths use the engine property position, not the C# property name Position. Play() schedules processing; call Advance(0) after playback starts when the value must be available immediately.

AnimationPlayer inherits Entity and does not relay 3D transforms like Entity3D. Visual and AnimationPlayer are siblings here. Use Entity3D parents for spatial hierarchies.

The equivalent editor workflow

Create AnimationPlayer below the root containing Visual. Open the bottom Animation panel and create a one-second clip in the global library. Insert a Visual.position key at time 0 with (0,0,0), then at 1 second with (0,2,0). Scrub the timeline to check it. Choose discrete updates for booleans and continuous updates for movement.

Do not combine manually authored clips with PlayerAnimationDemo in one hierarchy; the script creates its own AnimationPlayer and targets. For imported animations, use the imported library or an editable copy; do not expect manual changes to survive reimporting over them.

Library names

The empty library name "" is global; play its clip as "rise". A library named "doors" uses the full key "doors/rise". A resource filename and a clip name are different things. Check HasAnimation() before conditional playback.

Time controls

CallResult
Play("rise")Starts the clip; calling it again for the same clip is not a restart command
Pause() → Play()Retains the position and resumes the assigned clip
Stop()Stops and resets playback to the current section's start, applying its initial pose
Stop(keepState: true)Stops and resets time while retaining the applied pose
Stop() → Play("rise")An explicit restart, as with R in the example
Seek(t, update: true, updateOnly: true)Immediately shows a pose without method-track calls; it does not play everything up to t
PlayBackwards("rise")Plays backward from the end

SpeedScale multiplies customSpeed. SpeedScale = 0 stops time progression while IsPlaying() can remain true. Do not infer a paused operation from that method alone. Stop() clears the current clip; do not immediately read CurrentAnimationPosition. First assign the clip again through AssignedAnimation = "rise" or start Play(). The retained name from the AssignedAnimation getter alone does not prove that a current clip exists.

The default callback updates by frames. Choose physics processing for physics-related properties; for your own timing, choose CallbackModeProcess = Manual and call Advance(delta) once in the appropriate place. Do not keep automatic processing alongside an additional manual step. In manual mode, decide whether to step while paused; an explicit Advance() can advance a paused clip.

Completion and queues

AnimationFinished represents a nonlooping clip's normal end. Stopping and seeking do not replace that event; looping clips do not have a normal final event. Awaiting the signal without accounting for cancellation or object deletion can leave an operation unfinished.

Queue() places a clip after the current one; a perpetual loop will not reach it on its own. Use AnimationTree for conditional states, and track cancelable gameplay operations separately from waiting for a signal.

Sections, markers and preview

The helper's rise clip contains start at 0.2 seconds and end at 0.8. PlaySectionWithMarkers("rise", "start", "end") plays that section: Y=0.4 to Y=1.6 over 0.6 seconds. Markers define boundaries, not method calls; use a separate track for events. PlaySection() accepts time boundaries directly.

Editor autoplay starts the chosen clip when the player enters the level. Our code already starts it explicitly with Play("rise"). Timeline Step helps place keys; it does not set runtime update frequency. Onion Skinning displays neighboring editor poses to check a trajectory without adding gameplay keys.

RESET and diagnostics

RESET defines initial animated-property values and supplies a reference pose for editor operations and blending. It is not a game save. Reset On Save concerns saving the edited level, not restoring progress on load. To reset gameplay explicitly, play RESET and process a zero step; ordinary Stop() does not mean play RESET.

If nothing changes, check the full animation key, RootEntity, Visual, the property name and keys. Then check Active, SpeedScale, callback mode and competing scripts. If only the editor preview works, confirm that runtime code calls Play() or autoplay is configured.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.