• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

[SOLVED] Help with attack state machine

C

Coleman K

Guest
Hello i am still very much a beginner and am having trouble with my attack state. I have it to where a hitbox spawns with my sPlayerAttack animation but once I press my attack key my player stops and the attack animation loops continuously and I lose control. I'm assuming i'm just not understanding how to get back to my normal state or missing something simple.

Here is my normal state code:

Code:
scr_get_input();

walksp = 3;
grv = 0.3;
accel = 0.2;
var move =(key_right - key_left) * walksp;
hsp = approach(hsp,move,accel);

vsp = vsp + grv;

if (place_meeting(x,y+1,oWall)) and (key_jump)
{
    vsp = -4
}

if key_attack
{
     state = state.attack;
}

// Animation
if (hsp != 0)
{
    image_xscale = sign(hsp);
}
if (hsp == 0)
{
    sprite_index = sPlayerIdle;
}
else
{
        sprite_index = sPlayerRun;
    }
    
scr_collision();
and here is my attack code:
Code:
hsp = 0;
vsp = 0;

sprite_index = sPlayerAttack;

image_speed = 1;

if (image_index >= 0)and (image_index <= 3)
{
    with(instance_create_depth(x,y,0,oHitBox))
    {
        image_xscale = other.image_xscale;
    
    }
    
}
 
Z

Zenru45

Guest
Well, it looks like you don't have any way of resetting 'state'.
if key_attack
{
state = state.attack;
}
Sets the 'state' to 'attack' but I don't see anything to reset it back to normal. What you need is to return 'state' to its default at some point after the attack is complete. Perhaps the end of your attack code?
I'm getting the impression you might be thinking releasing the key resets 'state' and it does not. Hope that helpls.
 
C

Coleman K

Guest
Well, it looks like you don't have any way of resetting 'state'.
if key_attack
{
state = state.attack;
}
Sets the 'state' to 'attack' but I don't see anything to reset it back to normal. What you need is to return 'state' to its default at some point after the attack is complete. Perhaps the end of your attack code?
I'm getting the impression you might be thinking releasing the key resets 'state' and it does not. Hope that helpls.
I've tried setting state to equal state.normal after my attack code however when I do this and press the attack button the attack animation only flashes briefly and inconsistently. Is there a way to have all of the animation play and then set the state back to normal? I also have a switch state in my oPlayer object with the code:

Code:
switch state
{
    case state.normal: scr_player_normal(); break;
    case state.attack: scr_player_attack(); break;
    
}
 
Last edited by a moderator:
Z

Zenru45

Guest
Well, you could try an alarm event. Basically you start the alarm when the button is to attack is used and code the state reset for when the alarm event triggers. You just need to know how long your attack animation is. Search for 'alarm' in the GMS2 manual for more on those.
 
Z

Zenru45

Guest
It would look something like:
if key_attack
{
state = state.attack;
alarm[0] = (whatever time you want in miliseconds);
}
The create an alarm[0] event and put the reset in there.
 
C

Coleman K

Guest
It would look something like:
if key_attack
{
state = state.attack;
alarm[0] = (whatever time you want in miliseconds);
}
The create an alarm[0] event and put the reset in there.
I think I've found the problem. My character has a bobbing idle animation and a bob when she moves. If I take the bob away she goes through her entire attack animation and then goes to state normal. Why would the bobbing get in the way? My attack sprite animation is a different size (just wider for the slash). Could that have something to do with it?
 
C

Coleman K

Guest
What controls the bobbing animation? Is it a state?
In my normal state I set my sprite to an idle sprite that bobs up and down. The bobbing is done in the sprite itself with like 5 frames.
 
Z

Zenru45

Guest
'Normal state' as in the default state? So the state is being reset after the attack? I'm a bit confused here. Try Maxlos suggestion. I haven't used end animation events myself, but the idea seems sound.
 
C

Coleman K

Guest
'Normal state' as in the default state? So the state is being reset after the attack? I'm a bit confused here. Try Maxlos suggestion. I haven't used end animation events myself, but the idea seems sound.
Ok I’ll try that. Yeah I have it working where after the attack happens it goes back to state.normal where I have all of my movement and jumps and what not. The only problem I’m having now is that unless I press attack when the bob (idle) animation is at its first frame the attack animation is cut short. Sorry I’m horrible at articulating all of this XD. Thanks for your help though!
 
T

Tidbit

Guest
I've messed a bit around with states before in the past and they can be tricky when messing around with animations and game maker itself is a bit funny with animations. I suggest a bit of a better way of changing your states:
Code:
if (!override) {
var a = state.normal;
a = right    ? state.move : a;
a = left     ? state.move :  a;
a = attack   ? directions.down : a;
}
As a quick lesson in case you aren't aware this sort of operation ( x = y ? 1 : 2 ) is called a ternary. And basically x is set to 1 or 2 depending on whether y is true or false (1 or 2).

We use override as a boolean to allow changing states in the first place, this way if we have a longer animation it wont be interrupted by a state change.

Then we would want something like this after that.:
Code:
if (a != state.normal) {
scr_checkState(a);
}
scr_checkState would look like this:
Code:
var _state;
_state = argument0;
override = 1;

switch a {
case state.move:
scr_movePlayer();
break;
case state.attack:
[INDENT]scr_attackPlayer();
break;
}
At the end of your movePlayer and attackPlayer then I suggest you have a keycheck and if the correct key isn't being pressed then you can set override to 0. Thus signalling the end of the state which would allow things to be set back to the normal state if nothing else is pressed, or allow things to go to a new state entirely.

Also, if you want you can have just one sprite with all of your animations and then just loop through certain ranges of frames depending on the current state.

This can be done with a simple for loop where you do something like...
Code:
for (i = current frame; i < last frame before looping/ending; i++) {
current frame = i
if current frame >= last frame {
current_frame = frame start (or frame end if you dont want to loop)
}
return current_frame;
}
This is sorta pseudo code but it should get the basic idea across. Will probably need a little bit more to get it working 100% right.

This should solve the issue you were having with your animations as well I think unless I misunderstood.

edit: seems like indents dont like code blocks sooo I apologize for the lack of indents.
 
Last edited by a moderator:
Z

Zenru45

Guest
You're welcome. It sounds like whatever starts your idle needs to be held off until after your attack animation. I could probably help more if I saw more of your code, but you might want to try using what you've learned today to experiment with. Good luck with your project!
 

MaxLos

Member
Ok I’ll try that. Yeah I have it working where after the attack happens it goes back to state.normal where I have all of my movement and jumps and what not. The only problem I’m having now is that unless I press attack when the bob (idle) animation is at its first frame the attack animation is cut short. Sorry I’m horrible at articulating all of this XD. Thanks for your help though!
Are you setting your image index to 0 when you change to your attack sprite?
 

MaxLos

Member
This fixed it! Such a simple thing I missed lmao. Thanks so much!
Glad I was able to help! I used to forget to do it myself. Here's a neat little trick you can use so you don't have to set your image index to 0 whenever changing sprites:
Code:
//Create Event
last_sprite = sprite_index;
Code:
//End Step
if (last_sprite != sprite_index) //Check if we changed our sprite index
{
     last_sprite = sprite_index;
     image_index = 0; //Set image index to 0
}
There are times where you might not want to set your image_index to 0 though, like when changing from a walking animation to a running one, you can just exit out of the event in those cases like so:
Code:
//End Step
if (last_sprite != sprite_index) //Check if we changed our sprite index
{
     if (last_sprite = spr_player_walk) and (sprite_index = spr_player_run) exit;
     last_sprite = sprite_index;
     image_index = 0; //Set image index to 0
}
Bless the man who posted this method on a random tutorial completely unrelated to it. Saved it to a notepad and have using it in my projects ever since. Whoever they are, I hope their doing well
 
Top