Asset - Scripts AESnips - A powerful and easy to use sprite animation system [free]

angelwire

Member
angelEngine_snips_wide.png
AESnips is a system that allows objects to effortlessly handle simple or complex sprite animation playback for you.

Marketplace Link: https://marketplace.yoyogames.com/assets/9275/aesnips-animation-system

Summary:
Create "Snips" and simply tell an object to play the Snip animation and the system will handle everything for you.
No more worrying about sprite_index, image_index or writing complex code in the Animation End Event

angelEngine_snips_feature_list_small.png

For use in GMS 2.3

Documentation is available here: https://docs.google.com/document/d/1JSGG1mfid8lOiSictlqFUEiaxrTep1TUyvLne1Z2Yr0

Snips are powerful
Transitions between Snips make animations much more fluid. Any time you play a Snip you have the option to have the system try to find a Transition to bridge the gap between the Snip that is currently playing and the Snip you want to play. The system also automatically handles Snips that finish playing based on their end type. Snips can play once and stop, they can continually repeat, they can pingpong, and they can even call functions when done playing.
You can assign a function to be executed for each frame in a Snip - which is super useful for firing bullets or throwing objects.

Snips are dynamic
Each sprite frame can have its own speed. Split second delays can be added into animations without copy/pasting sprite frames or writing extra code.
Loops can be added to Snip animations to repeat certain frames. No more copy/pasting sprite frames to make your animations longer. Snip properties, frame speeds, and Loop repetition can be changed in-game, which means your animations can dynamically react to events in the game.

Snips are easy
Add one line to the animated objects’ Create Event, and Step Event it is ready to play Snip animations.
Create Snips with one line of code, play Snips with one line of code. Transitions and Loops can also be created with a single line of code and then you can completely forget about writing any more logic to handle them.
If you want more control over the Snips there are a lot of helpful “arbiter” functions that give you control without requiring you to memorize all the variable names.
Check the source code for the demo and to see just how easy it is to use.

Snips are growing
AESnips is still growing and I would love to be able to add more features to fit the needs of GameMaker developers who have interest in the system. If you’re making a game that relies heavily on sprite animations I would love to hear from you on what more you want from a system like this.

In this GIF the blue cowboy is playing one single Snip but using a Loop to twirl the gun. That Snip also has some slower frames to create a subtle delay before shooting the gun. There is a bullet_fire script attached to the gun firing frame. The cowboy can also skip the firing Loops to pull out the gun and then holster it without firing.

The red cowboy is made up of four Snips: "idle", "draw", "shoot", "holster". When the player presses space, the "shoot" Snip is played. After setting up the Snips and Transitions, only one line of code is needed in the Space Pressed event to play all 4 Snips in the perfect order, and fire the bullet! The "draw" Snip is the Transition between the "idle" and "shoot" Snip. The "idle" Snip is set as the successor to the "fire" Snip. And the "holster" Snip is set as the Transition between the "fire" and "idle" Snip.

Quick how-to-use:
Any Object that uses Snips MUST call a function in the Create Event and Step Event. The functions are named appropriately:
  • snip_create_event()​
  • snip_step_event()​
You can create Snips, Transitions, and Loops anywhere in your code. Loops and Transitions are created using the "new" keyword. Snips can be created with "new" or with snip_create_ext(...).
You can make changes to a Snip's frames' speed or assign a script to each frame with snip_set_frame_speed(...) and snip_set_frame_script(...)
When you want your object to play a Snip use snip_play(Snip, transition?). Use snip_play_next(Snip, transition?) to queue up a Snip to play after the current Snip is done playing.
You can pause individual objects or all Snips by having the object call snip_pause()/snip_resume() and snip_global_pause()/snip_global_resume()

New feature for Snips v0.1.4 - SnipPlayer:
Before this feature, Snips were totally tied to an Object's built-in variables (image_index, sprite_index, x,y, image_xscale...). This meant that Objects were limited to only 1 Snip. A SnipPlayer is a struct that will sort of take the place of the object's built-in variables and allow objects to play multiple Snips at the same time - or draw copies the same Snip anywhere in the room. Instead of needing 1 Object per Snip, an Object can control multiple SnipPlayers and draw them any number of times. To use a SnipPlayer, create a Snip and set it up just like you normally would. Then you need to create a SnipPlayer with the "new" keyword. And then include the SnipPlayer instance before the control functions: such as my_snip_player.snip_play(my_snip). Then include the SnipPlayer.step() in the Object's step event, and SnipPlayer.draw(...) to draw the SnipPlayer anywhere you want.

And that's a quick tutorial, I encourage you to check out the documentation and demo source code.
Documentation: https://docs.google.com/document/d/1JSGG1mfid8lOiSictlqFUEiaxrTep1TUyvLne1Z2Yr0


Marketplace Link: https://marketplace.yoyogames.com/assets/9275/aesnips-animation-system
GitHub repository including demo project and source code: https://github.com/angelwire/AESnips

I'm very open to feedback. This is a project that is still growing and I'd love to hear suggestions!

 
Last edited:

Voidfyre

Member
Damn I'm pretty new and learning and I feel like I'm gonna be so lost again once 2.3 comes out. So many cool new features. Gonna save this for later!
 

angelwire

Member
Damn I'm pretty new and learning and I feel like I'm gonna be so lost again once 2.3 comes out. So many cool new features. Gonna save this for later!
The GML updates in 2.3 are great. I started developing this project with the current stable release but I took a chance and started using 2.3 and working with it was so much better. I had to spend a couple hours studying the manual and watching tutorials, but it was definitely worth it.

New update v.02:
  • Added snip_draw_debug(x,y). This script will draw what you see in the GIFs of the main post
  • Fixed an error with Loops
 

❤️×1

Member
Honestly, it sounded kinda overkill but, once I actually tried it, AESnips is revealing another big gap in GMS2 feature set.
Also, ping pong and reverse animation are already in beta on github, only a few days after I asked @angelwire to add them. Awesome lib and awesome support \o/
 

angelwire

Member
0.1.3 update:
-Removed the need to use the Animation End Event
-Added pingpong playback via the new end_type enum
-Added the ability to play Snips in reverse
-Added the ability to clone Loops and Transitions from one Snip to another
-Added more playback control functions like snip_cancel_play_next(), snip_play_reqest()
-Fixed some bugs with the debug visual and Loops
-Check the AESnipsChangelog in the sample project for more info

Give it a try on the marketplace
Or check it out on GitHub

Thanks to @❤️×1 for the pingpong playback and reverse Snip suggestions!
 

angelwire

Member
New feature for Snips v0.1.4 - SnipPlayer:
Before this feature, Snips were totally tied to an Object's built-in variables (image_index, sprite_index, x,y, image_xscale...). This meant that Objects were limited to only 1 Snip. A SnipPlayer is a struct that will sort of take the place of the object's built-in variables and allow objects to play multiple Snips at the same time - or draw copies the same Snip anywhere in the room. Instead of needing 1 Object per Snip, an Object can control multiple SnipPlayers and draw them any number of times. To use a SnipPlayer, create a Snip and set it up just like you normally would. Then you need to create a SnipPlayer with the "new" keyword. And then include the SnipPlayer instance before the control functions: such as my_snip_player.snip_play(my_snip). Then include the SnipPlayer.step() in the Object's step event, and SnipPlayer.draw(...) to draw the SnipPlayer anywhere you want.

Thanks to @Chourando for the suggestion via GitHub

0.1.4
{
!!!!!!!!
!!! Steps to update from 0.1.3:
!!! Trying to set a Snip's speed to a value less than 0 will throw an error, replace any negative speed values with a positive value and add snip_set_backward()
!!!!!!!!

-Added SnipPlayer struct to play Snips
-Objects can still play Snips without the need for a SnipPlayer
-Add SnipPlayers to an object to allow the object to play multiple Snips at the same time
-Each SnipPlayer needs to be added to the Step event in order to animate and the Draw event to be drawn
-SnipPlayers use similar playback functions, but must be preceeded by the SnipPlayer instance for example: my_snip_player.snip_play(my_snip)
-Added an ae_snip_direction variable to objects that play Snips.
-A PingPong style Snip can be played by multiple objects without one instance of the Snip impacting the other
-Setting a Snip's speed to a value less than 0 will throw an error, use snip_set_backward() instead
}
 
Last edited:

angelwire

Member
Does AESnips account for delta time with the image_speed?

EDIT: Nevermind! Apparently delta time isn't needed for image_speed, I learned something new! xD
I'm sorry for the late reply, I'm glad you were able to answer your own question!

Just to clarify, AESnips does use image_speed. It also respects the sprite's "Frames per second" / "Frames per game frame" setting. I'm not 100% sure, but using "frames per second" on your sprites may be the reason that you don't need to use delta time.

Things may be different if you're playing a Snip through a SnipPlayer, however, since it doesn't use image_speed
 

ZeDuval

Member
@angelwire Is there a non-hacky way to scale all snip speeds with a global gamespeed variable I did not find?

I have such a variable that is used to scale everything time/speed related to make things like fast-forward(2.0), slow-motion(0.5) or simply a pause functionality(0.0) possible.

It would be great if we could pass a reference(there would be multiple possible implementations) to our speed scale modifier variable to the library, so that it can work alongside the library internal speed scaling.

What do you think?
 

angelwire

Member
@angelwire Is there a non-hacky way to scale all snip speeds with a global gamespeed variable I did not find?

I have such a variable that is used to scale everything time/speed related to make things like fast-forward(2.0), slow-motion(0.5) or simply a pause functionality(0.0) possible.

It would be great if we could pass a reference(there would be multiple possible implementations) to our speed scale modifier variable to the library, so that it can work alongside the library internal speed scaling.

What do you think?
This is a great idea. I'm sorry for taking so long to reply, but if you or anyone else is still interested here's what I recommend:

At the top of the AESnipsBase script, create a global variable AE_SNIP_GLOBAL_SPEED_SCALE and start it out at 1.
Code:
//Global variables
globalvar AE_SNIP_GLOBAL_IS_PAUSED,
          AE_SNIP_GLOBAL_SPEED_SCALE //<-- Here
;

AE_SNIP_GLOBAL_IS_PAUSED = false;
AE_SNIP_GLOBAL_SPEED_SCALE = 1.0; //<-- Here

//How the Snip should behave when it finishes playing
enum ae_snip_end {
    end_stop, //Freeze on the completed frame
...
Then scroll down inside the snip_step_event function in the AESnipsBase script (near line 200). And multiply the image_speed value by AE_SNIP_GLOBAL_SPEED_SCALE along with all of the other values. (make sure the semi-colon ; is on the right line)
Code:
...
var _frame_speed = _snip.frame_speed[(_index - _start)] //The speed for the current snip's current frame
    
    //Adjust the speed of the sprite depending on the following values
    //Frame Speed - Snip Speed - Snip Direction - Individual snip paused? - Individual Snip stopped? - All snips paused?
    image_speed = _frame_speed //The speed for the current snip's current frame
                * _snip.snip_speed //The speed for the whole current snip
                * _snip.snip_direction //Whether the Snip wants to be played forwards or backwards
                * ae_snip_direction  //Whether the Snip is being played forwards or backwards by the object
                * !ae_snip_is_paused //If the snip is paused then this is 0, if not then it's 1
                * !ae_snip_is_frozen //If the snip is frozen at the end of an snip
                * !AE_SNIP_GLOBAL_IS_PAUSED //If the snip system as a whole is paused
/* Here -> */    * AE_SNIP_GLOBAL_SPEED_SCALE; //Scale the speed of every snip
                
    //Find out what frame the Snip will be on next frame
    var _next_frame = _index + (image_speed * _snip.snip_speed_scale);
...
You'll want to do the same thing in the AESnipsPlayer step() method if you're using the SnipPlayer. Changing this value will cause the entire AESnips system to play Snips faster or slower (.5 is half speed, 2 is double speed, 0 is paused). Make sure the AE_SNIP_GLOBAL_SPEED_SCALE value is never negative because it might make things behave strangely.
 
Top