SOLVED Trying to display one sprite followed by another in same state

Palocles

Member
Hopefully this is simple, I don't want to spam the forum with questions...

So i've got a state engine and need a couple of states to run multiple sprites. This works ok for jump where a different sprite is running for upwards movement to the downwards movement sprite (if/else) statement.

I'm running into trouble with my attack state. Ultimately i want to be able to string two (or more) different attack animations together for rapid attacks (combo, if you will) without returning to a neutral (idle) state in between. So the attack animations do not include the final few frames which would return the player sprite to the neutral position, allowing attack B to immediately follow attack A.

In the mean time, for this more simple state engine, I want the 'return' animation to follow attack A and then return to idle, but its not leaving the attack state anymore.

GML:
function scrPlayerAttack(){
   
    show_debug_message("entered Attack State");
   
    sprite_index = sprBoganAttack;
    image_xscale = currentFacing;
   
    if(image_index >= (image_number -1))
    {
        sprite_index = sprBoganAttackEnd;      
       
        if(image_index >= (image_number -1))
        {
            show_debug_message("leaving Attack State");
            currentState = playerState.idle;
        }
    }
}
original code which returns to idle correctly:
Code:
function scrPlayerAttack(){
   
    show_debug_message("entered Attack State");
   
    sprite_index = sprBoganAttack;
    image_xscale = currentFacing;
   
    if(image_index >= (image_number -1))
    {  
        show_debug_message("leaving Attack State");
        currentState = playerState.idle;
    }
}
It seems that this nested statement should perform like the original code did but it doesn't for some reason.
 
Last edited:

jb skaggs

Member
When you change sprites is image_index still reading last frame? If so try changing it back to zero when changing sprites. I could be wrong but I dont think changing sprites changes the image_index current number.
 

Mk.2

Member
GML:
image_xscale = currentFacing;

if (!attackEnd) //Set this new variable to false in the create event first
{
    sprite_index = sprBoganAttack; //Only sets sprite_index to this animation if the attack hasn't ended
                                   //Previously, the end attack animation would not play
  
    if ((image_index + image_speed) >= image_number) //Lets the animation play fully, instead of skipping the final frame like your previous method
    {
        attackEnd = true;
        image_index = 0; //Resets the image index, as this doesn't happen automatically when changing sprite_index
    }
}
else
{
    sprite_index = sprBoganAttackEnd;

    if ((image_index + image_speed) > image_number)
    {
        show_debug_message("leaving Attack State");
        currentState = playerState.idle;
        attackEnd = false;
        image_index = 0;
    }
}
Remember to change attackEnd back to false whenever the animation is interupted, for example, when the player takes damage.
 

Palocles

Member
That works great, thanks Mk.2!

Why does it need a flag (attackEnd) to work though?

And how would I do code to allow the combo attack before the attackEnd? Is that going to require another flag or can a nested if do it? The idea is for the player to be able to keep swinging basic attacks if they press the button near the end of the first swing.
 
Run your original code in your head:
(simplified)
Code:
sprite_index = sprBoganAttack; // This runs every frame, no matter what image_index is
image_xscale = currentFacing; // As above
    
if(image_index >= (image_number -1))
{
    // Right here, sprite index is still equal to sprBoganAttack every frame
    sprite_index = sprBoganAttackEnd; // If image_index is greater than or equal to the last frame, we change the sprite from sprBoganAttack to sprBoganAttackEnd every frame
}
All that stuff is happening every single frame. It visually looks like you've set the sprite index to sprBoganAttackEnd properly, but really what you're doing is setting it to sprBoganAttack every frame and then setting it to sprBoganAttackEnd every frame a bit later in the code. Also, the image_index is not being reset to 0 when the sprite is set to sprBoganAttackEnd, so it will only ever show the last frame of sprBoganAttackEnd. However, if you do set image_index back to 0, then image_index >= (image_number-1) is no longer true and so you've stopped setting the sprite to sprBoganAttackEnd after you've set it to sprBoganAttack. Which means it'll start showing sprBoganAttack again.

You need the flag, because without the flag, the only option you have is to set the sprite to either sprBoganAttack or sprBoganAttack and then sprBoganAttackEnd every frame. And as we've seen, once you mess around with image_index, you lose the ability to set the sprite to sprBoganAttackEnd. The flag lets the computer know which of the two states you want shown. Do you want sprBoganAttack shown? Or do you want sprBoganAttackEnd shown? The flag lets you simply set it to whichever one is appropriate at the time and then play around with image_index without having to worry about the dual-setting of the sprites you were doing originally.
 

Mk.2

Member
And how would I do code to allow the combo attack before the attackEnd? Is that going to require another flag or can a nested if do it? The idea is for the player to be able to keep swinging basic attacks if they press the button near the end of the first swing.
A simple method would be to check which attack animation is currently playing, then check if the image index is greater than a certain value. Set a flag to true if the attack key is pressed. Once the animation ends, if that flag is set to true, start the next attack.
 

Palocles

Member
OK, thanks again guys.

Yeah, it was showing the original attack animation continuously. Would putting image_index =0; after the sprite_index call not have worked?

Mk.2 i'll try what you've said in the morning and see how it goes. At the moment i'm trying to get a simplified engine working but i want to know to make the more full engine work later.
 

Mk.2

Member
Yeah, it was showing the original attack animation continuously. Would putting image_index =0; after the sprite_index call not have worked?
RefresherTowel has already explained this, but just to reiterate, the problem was that you were continuously setting the sprite index to sprBoganAttack. Even though you set it to sprBoganAttackEnd once the animation was finished, on the next frame it just got set to sprBoganAttack again, regardless of the image index. That is the purpose of the flag; it only sets the sprite index to sprBoganAttack if the flag is set to false.
 

Palocles

Member
This code seems to be working now. At least it does both attack animations, I'm just not sure if it skips the return animation after attack B but i think that might be to do with the sprites.

The intention was for a combo attack like this to be quicker than individual attacks, so I might need to increase the length of the return animation so it's more noticeable.

Thanks for the help everyone.

Edit: I just added a visual marker to the attack end animation and it shows the code working as intended. I will have to tweak the duration of the animations to get the intended effect.

GML:
function scrPlayerAttackCombo(){
   
    show_debug_message("entered Attack State");
    image_xscale = currentFacing;
   
    if(!attackEnd)
    {
        if(attackA)
        {
            sprite_index = sprBoganAttackA;
            if(image_index >= (image_number/2) && key_attack_A)
            {
                combo = true;
            }
            if((combo == true) && (image_index >= image_number -1))
            {
                sprite_index = sprBoganAttackB
                attackA = false;
                image_index = 0;
                combo = false;
            }
            else if(image_index >= (image_number -1))
            {
                attackEnd = true;
                image_index = 0;
                combo = false;
            }
        }
        if(!attackA)
        {
            sprite_index = sprBoganAttackB;
            if(image_index >= (image_number/2) && key_attack_A)
            {
                combo = true;
            }
            if((combo == true) && (image_index >= image_number -1))
            {
                sprite_index = sprBoganAttackA
                attackA = true;
                image_index = 0;
                combo = false;
            }
            else if(image_index >= (image_number -1))
            {
                attackEnd = true;
                image_index = 0;
                combo = false;
            }
        }
    }
    else
    {
        sprite_index = sprBoganAttackEnd;      
       
        if((image_index) >= (image_number -1))
        {
            show_debug_message("leaving Attack State");
            currentState = playerState.idle;
            attackEnd = false;
            image_index = 0;
        }
    }
}
 
Top