Overview
This template handles easy animation swapping, primarily useful for alternate skins or transformations with different animations or sprites.
It stores every sprite used by the character in a variable called spr_something
. This variable can be set on-the-fly to any sprite_get()
, and the code in this template will make sure that the sprite gets used whenever appropriate - based on sprite, not state. For example, it will use the correct hurt sprite despite them all using the PS_HURT
state, based on whichever sprite is assigned by the game engine.
Not only this, but it's also convenient to use these variables elsewhere in the code, in place of sprite_get()
. For example, you can have articles set their sprite using sprite_index = player_id.spr_something;
, and it will change using the system.
init.gml
In init.gml
, we will define the sprite variables. Paste this into your code:
useskins = false;
//Ground
spr_idle = sprite_get("idle");
spr_crouch = sprite_get("crouch");
spr_walk = sprite_get("walk");
spr_walkturn = sprite_get("walkturn");
spr_dash = sprite_get("dash");
spr_dashstart = sprite_get("dashstart");
spr_dashstop = sprite_get("dashstop");
spr_dashturn = sprite_get("dashturn");
//Air
spr_jumpstart = sprite_get("jumpstart");
spr_jump = sprite_get("jump");
spr_doublejump = sprite_get("doublejump");
spr_walljump = sprite_get("walljump");
spr_pratfall = sprite_get("pratfall");
spr_land = sprite_get("land");
spr_landinglag = sprite_get("landinglag");
//Dodge
spr_parry = sprite_get("parry");
spr_roll_forward = sprite_get("roll_forward");
spr_roll_backward = sprite_get("roll_backward");
spr_airdodge = sprite_get("airdodge");
spr_airdodge_waveland = sprite_get("waveland");
spr_tech = sprite_get("tech");
//Hurt
spr_hurt = sprite_get("hurt");
spr_bighurt = sprite_get("bighurt");
spr_hurtground = sprite_get("hurtground");
spr_uphurt = sprite_get("uphurt");
spr_downhurt = sprite_get("downhurt");
spr_bouncehurt = sprite_get("bouncehurt");
spr_spinhurt = sprite_get("spinhurt");
//Attack
spr_jab = sprite_get("jab");
spr_dattack = sprite_get("dattack");
spr_ftilt = sprite_get("ftilt");
spr_dtilt = sprite_get("dtilt");
spr_utilt = sprite_get("utilt");
spr_nair = sprite_get("nair");
spr_fair = sprite_get("fair");
spr_bair = sprite_get("bair");
spr_uair = sprite_get("uair");
spr_dair = sprite_get("dair");
spr_fstrong = sprite_get("fstrong");
spr_ustrong = sprite_get("ustrong");
spr_dstrong = sprite_get("dstrong");
spr_nspecial = sprite_get("nspecial");
spr_fspecial = sprite_get("fspecial");
spr_uspecial = sprite_get("uspecial");
spr_dspecial = sprite_get("dspecial");
spr_taunt = sprite_get("taunt");
//Respawn
spr_plat = sprite_get("plat");
These are some of the "bare minimum" sprites that a character will typically have. You can remove any that your character/costumes will not use, and you can add any that are missing. For example, add spr_nspecial_air = sprite_get("nspecial_air");
if your character uses the nspecial_air
sprite and you want to be able to swap it on the fly. You should also add any non-character sprites, such as article sprites. For Trummel & Alto, I have a long list of extra sprites here for things such as the clouds.
useskins
is a true/false variable which controls part of the next section's code.
animation.gml
In animation.gml
, we will handle the automatic swapping out of the character sprites. Paste this at the bottom of the script (but above all #define
s if you have any already):
if useskins{
//Ground
changeAnim(spr_idle, sprite_get("idle"));
changeAnim(spr_crouch, sprite_get("crouch"));
changeAnim(spr_walk, sprite_get("walk"));
changeAnim(spr_walkturn, sprite_get("walkturn"));
changeAnim(spr_dash, sprite_get("dash"));
changeAnim(spr_dashstart, sprite_get("dashstart"));
changeAnim(spr_dashstop, sprite_get("dashstop"));
changeAnim(spr_dashturn, sprite_get("dashturn"));
//Air
changeAnim(spr_jumpstart, sprite_get("jumpstart"));
changeAnim(spr_jump, sprite_get("jump"));
changeAnim(spr_doublejump, sprite_get("doublejump"));
changeAnim(spr_walljump, sprite_get("walljump"));
changeAnim(spr_pratfall, sprite_get("pratfall"));
changeAnim(spr_land, sprite_get("land"));
changeAnim(spr_landinglag, sprite_get("landinglag"));
//Dodge
changeAnim(spr_parry, sprite_get("parry"));
changeAnim(spr_roll_forward, sprite_get("roll_forward"));
changeAnim(spr_roll_backward, sprite_get("roll_backward"));
changeAnim(spr_airdodge, sprite_get("airdodge"));
changeAnim(spr_airdodge_waveland, sprite_get("waveland"));
changeAnim(spr_tech, sprite_get("tech"));
//Hurt
changeAnim(spr_hurt, sprite_get("hurt"));
changeAnim(spr_bighurt, sprite_get("bighurt"));
changeAnim(spr_hurtground, sprite_get("hurtground"));
changeAnim(spr_uphurt, sprite_get("uphurt"));
changeAnim(spr_downhurt, sprite_get("downhurt"));
changeAnim(spr_bouncehurt, sprite_get("bouncehurt"));
changeAnim(spr_spinhurt, sprite_get("spinhurt"));
//Attack
changeAnim(spr_jab, sprite_get("jab"));
changeAnim(spr_dattack, sprite_get("dattack"));
changeAnim(spr_ftilt, sprite_get("ftilt"));
changeAnim(spr_dtilt, sprite_get("dtilt"));
changeAnim(spr_utilt, sprite_get("utilt"));
changeAnim(spr_nair, sprite_get("nair"));
changeAnim(spr_fair, sprite_get("fair"));
changeAnim(spr_bair, sprite_get("bair"));
changeAnim(spr_uair, sprite_get("uair"));
changeAnim(spr_dair, sprite_get("dair"));
changeAnim(spr_fstrong, sprite_get("fstrong"));
changeAnim(spr_ustrong, sprite_get("ustrong"));
changeAnim(spr_dstrong, sprite_get("dstrong"));
changeAnim(spr_nspecial, sprite_get("nspecial"));
changeAnim(spr_fspecial, sprite_get("fspecial"));
changeAnim(spr_uspecial, sprite_get("uspecial"));
changeAnim(spr_dspecial, sprite_get("dspecial"));
changeAnim(spr_taunt, sprite_get("taunt"));
}
//Handle certain looping animations
if (sprite_index == spr_idle){
var frames = 6;
var frame_dur = 8;
image_index = floor((state_timer mod (frames * frame_dur)) / frame_dur);
}
if (sprite_index == spr_walk){
var frames = 6;
var frame_dur = 8;
image_index = floor((state_timer mod (frames * frame_dur)) / frame_dur);
}
if (sprite_index == spr_dash){
var frames = 6;
var frame_dur = 4;
image_index = floor((state_timer mod (frames * frame_dur)) / frame_dur);
}
#define changeAnim
var old_spr = argument[1];
var new_spr = argument[0];
if (sprite_index == old_spr && old_spr != new_spr){
sprite_index = new_spr;
}
changeAnim()
is a function which takes its second argument, checks if the character is using that sprite, and if so, switches the sprite to the first argument. For example, if the character is currently using the regular idle_stripX
sprite, the animation handler will switch it over to whichever sprite is stored in spr_idle
. This is how the system manages to cope with things such as the various hurt sprites - it checks directly for which sprite has been assigned by the game engine, not just which state the character is in.
By running changeAnim()
for each character animation, we handle all of these sprite changes when needed. As before, you should add any missing sprites, and you MUST remove anything that you removed from the init.gml
section. Keep in mind that, since this is only used by the character itself, you do not need to add sprites that are to be used by articles and the like. If useskins
is false, this chunk of code will not run, just to maximize performance.
Lastly, the middle section is to handle certain animations which would normally be handled by the game via idle_anim_speed
and so on... but break if it's not using the file named idle_stripX.png
etc. If you exclude this bit of code and replace the idle/walk/dash sprites using this system, the animation won't work. For each of these blocks of code, frames
is the number of frames in the sprite, while frame_dur
is the amount of time each frame should last. Incidentally, you can use this same bit of code to give a looping animation to any other sprites that need it, such as pratfall (which I did for Trummel & Alto).
Usage
Now that this has been added to your character, let's make it actually useful. (It doesn't accomplish much on its own, especially because useskins
is false by default; luckily, the below examples set it to true when necessary)
Here's a script to change things based on the player's alternate costume, placed immediately after the init.gml
code found above:
//Alts
switch(get_player_color(player)){
case 6: //Skin Slot 7
spr_idle = sprite_get("my_cool_costume_idle");
spr_taunt = sprite_get("my_cool_costume_taunt");
useskins = true;
break;
case 7: //Skin Slot 8
spr_idle = sprite_get("my_cooler_costume_idle");
spr_taunt = sprite_get("my_cooler_costume_taunt");
useskins = true;
break;
}
These extra sprites should be stored in the sprites
folder with all the others.
To change sprites on the fly rather than via costume, simply put spr_idle = sprite_get("my_second_idle");
anywhere in your character's code. To change back to default, put spr_idle = sprite_get("idle");
(and so on for other sprites). For example:
if character_is_transformed{
spr_idle = sprite_get("transformed_idle");
spr_taunt = sprite_get("transformed_taunt");
useskins = true;
}
else{
spr_idle = sprite_get("idle");
spr_taunt = sprite_get("taunt");
useskins = false;
}
To reference the sprites for use outside of character animations, simply use the spr_something
instead of sprite_get()
. For example, use draw_sprite(spr_my_cool_hud_element, 0, x, y);
. For articles and projectiles, remember to use player_id.spr_something
.
One last, important step: add all new alt. sprites to load.gml and set their offsets accordingly!
Known Engine Quirks
I will add to the list below as I find additional caveats to this system.
The animation for the jump and doublejump sprites is based on the frame count of jump_stripX.png
and doublejump_stripX.png
. This means that, for the animation to play correctly, the frame count of these animations should stay the same for each alternate version of the sprites. (e.g. jump_strip9.png
, doublejump_strip3.png
, costume_jump_strip9.png
, costume_doublejump_strip3.png
)