GML Simple animation system

DesArts

Member
Simple animation system

GM Version: GMS 1.4
Target Platform: ALL
Download: N/A
Links: N/A

Summary:
A simple solution to replace normal automatic image_speed animation and with features to control animation looping, and animation end events.

Tutorial:
The purpose of this system is simply greater control. You get benefits from creating systems yourself rather than using those buit in. This can be expanded on by anyone who uses it.

I have also had issues with inconsistent frame progression/flickering when using built in image speeds, and more consistently - the end animation event does not trigger on the correct frame. This system avoids these issues since we can control it.

This system is made out of 4 scripts:
  • animation_system_initialise() - which creates the variables for the object it's in (it is an object local system with no global variables) This should be used in the object's create event.
  • animation_system_update() -which will update the current animation, perform any end animation specific tasks etc. This should be used in any one of the object's step events.
  • animation_system_add_animation(sprite, nextanimation, endscript) - this will add an animation to an array, with the animation number increasing each time, starting at 0. You can specify an animation it should play next if image speed > 0, and the same thing with a script that will be executed at the end of the animation. This can be used wherever needed, usually in the create event.
  • animation_set(ani, image, speed) - this will set the animation. ani is the animation number returned when you created the animation, image is the equivalent of image_index, and speed is the equivalent of image_speed. "" can be used in any of these spaces to not change that specific attribute of the current animation state (for example to change the speed without changing the image or ani, set those to ""). This can be used anywhere you want to change anything going on in the system.
All this system does, is control 4 variables, created in the initialise script:

Call this in the create of the object you want to animate.
Code:
///animation_system_initialise()
 
    //controlling variables
    Animation = 0; //current animation sprite, from 0 up to (the_number_of_animations_created-1)
    AniImage = 0; //current animation image, works the same as image_index, not always integer
    AniSpeed = 0; //current animation speed, works the same as image_speed
    AniEnd = 0; //a flag which is 1 when an animation gets completed, will last for a whole step - for best use, place animation update script before all other code which may use this flag
 
    //actual animations
    SpriteArray = 0; //will be an array containing each sprite, which allows animations to be able to be switched and no longer hard-coded
This next one should go in begin-step so that for the rest of the step, you can accurately detect an animation ending.
Code:
///animation_system_update()
 
    //reset system for this step
    AniEnd = 0;
 
    //increase frame by speed
    AniImage += AniSpeed;
 
    //check there are animations
    if (is_array(SpriteArray))
    {
        //get end of animation
        var num;
        num = sprite_get_number(SpriteArray[Animation, 0]);
 
        //forwards
        if (AniSpeed >= 0)
        {
            if (AniImage >= num)
            {
                //signal animation end event for this step
                AniEnd = 1;
      
                //loop back around
                AniImage = 0;
            }
        }
 
        //backwards
        if (AniSpeed < 0)
        {
            if (AniImage <= 0)
            {
                //signal animation end event for this step
                AniEnd = 1;
      
                //loop back around
                var ani, num2;
                ani = SpriteArray[Animation, 1];
                num2 = num;
                if (ani != -1)
                {
                    num2 = sprite_get_number(SpriteArray[ani, 0]);
                }
                AniImage = num2;
            }
        }
 
        //general end stuff
        if (AniEnd)
        {
            //execute end script if necessary
            var script;
            script = SpriteArray[Animation, 2];
            if (script != -1) script_execute(script, -1);
  
            //move on to next animation if necessary
            var ani;
            ani = SpriteArray[Animation, 1];
            if (ani != -1) animation_set(ani, -1, -1);
        }
    }
Adds an animation. Usage:
animation_walk = animation_system_add_animation(spr_walk, -1, -1)
Code:
///animation_system_add_animation(sprite, nextanimation, endscript)

    //arguments
    var ani_sprite = argument0;
    var ani_nextanimation = argument1;
    var ani_endscript = argument2;
 
    //add animation
    var height;
    if (!is_array(SpriteArray))
    {
        //begin new array
        height = 0;
    }
    else
    {
        //add to existing array
        height = array_height_2d(SpriteArray);
    }

    SpriteArray[height, 0] = ani_sprite;
    SpriteArray[height, 1] = ani_nextanimation;
    SpriteArray[height, 2] = ani_endscript;

    //return ani number (for use to set to this animation)
    return height;
Simply changes the current animation. Use "" in the settings you do not wish to change.
For example:
animation_set(animation_walk, "", 1)
Code:
///animation_set(ani, image, speed)

    //arguments
    var ani_sprite = argument0;
    var ani_image = argument1;
    var ani_speed = argument2;
 
    //update
    if (is_array(SpriteArray))
    {
        if (array_height_2d(SpriteArray) >= (ani_sprite+1))
        {
            if (!is_string(ani_sprite)) Animation = ani_sprite;
            if (!is_string(ani_image)) AniImage = ani_image;
            if (!is_string(ani_speed)) AniSpeed = ani_speed;
        }
    }

Here are some optional scripts to get extra usage out of this system:

You can check what the current animation is by using the variable Animation (for example "if (Animation == 20)") however here is a script to get that for you as it might seem weird doing this outside of a script for some (plus, this way it can be expanded on or changed for every instance using it):
Code:
///animation_current()

    return Animation;

More useful however is this, which can swap an animation's properties, while keeping the same number id/animation variable (use -1 on any of the last 3 variables you do not wish to change):
Code:
///animation_swap(ani, new_sprite, newnextanimation, newendscript)

    //arguments
    var ani_id = argument0;
    var ani_newsprite = argument1;
    var ani_newnextanimation = argument2;
    var ani_newendscript = argument3;
 
    //update
    if (is_array(SpriteArray))
    {
        if (array_height_2d(SpriteArray) >= (ani_id+1))
        {
            if (ani_newsprite != -1) SpriteArray[ani_id, 0] = ani_newsprite;
            if (ani_newnextanimation != -1) SpriteArray[ani_id, 1] = ani_newnextanimation;
            if (ani_newendscript != -1) SpriteArray[ani_id, 2] = ani_newendscript;
        }
    }
Feel free to suggest fixes, improvements, or build upon it yourselves.


Edit:
Almost forgot something important!

Here's an example of how you'd draw the animation for the object:
Code:
draw_sprite_ext(SpriteArray[Animation, 0], floor(AniImage), x, y, image_xscale, image_yscale, image_angle, image_blend, image_alpha);
 
Last edited:
F

Felipe Rybakovas

Guest
Hey, thanks for sharing this! This look very useful!

But how this would handle an entire spritesheet ?

Or how can i set the index interval in this case ?
 

DesArts

Member
This works with separate sprites, if you have multiple animations in one sprite that's a different kind of system, really this is simply a replacement for image_index and the animation end events which can both be unreliable, and impossible to customise.
 
Last edited:

Jihl

Member
I wouldn't have known that this worked better than the built in system, looks kind of "too much code", so thanks for opening my eyes and giving us this tool!
 
Top