GMS 2 State machiens small problem

Zynpex

Member
I'm sure this is a quick fix, but I have been up all night trying to find a solution for this aggravating problem.

Okay so basically, I want to have the player stop moving completely while in the attack state"(ATTACK_HAMMER)", if he is not grounded, i have a script i can call to check if he is on the ground. Its called on_ground. I have found one solution to stop the player completely, but another issue arose, I know what the issue is but not how to resolve it and i have tried everything i can think of. The issue has to do with the characters facing direction. When attacking, the player stops, but rapidly flick left and right at the same time while the animation is playing.


Player Create Event
///description Variables
hsp = 0;
vsp = 0;
max_hsp = 64
walk_spd = 11.0;
vsp_decimal = 0;
hsp_decimal = 0;
jump_spd = -38;


//friction

drag = .35;

//facing
facing = 1;

//movement

left = 0;
right = 0;
up = 0;
down = 0;
attack = 0;
jump = 0;
block = 0;





//sates

enum states {
IDLE,
WALK,
JUMP,
ATTACK_HAMMER,
ATTACK_SHOOT,
ATTACK_ROCK,
ATTACK_ARROW,
CROUCH,
BLOCK,
CROUCH_BLOCK,
FALL_ROLL


}

state = states.IDLE;



//create states array

states_array[states.IDLE] = player_idle_state;
states_array[states.WALK] = player_walk_state;
states_array[states.JUMP] = player_jump_state;
states_array[states.ATTACK_HAMMER] = player_attack_hammer_state;
states_array[states.ATTACK_ROCK] = player_attack_rock_state;
states_array[states.ATTACK_ARROW] = player_attack_arrow_state;
states_array[states.ATTACK_SHOOT] = player_attack_shoot_state;
states_array[states.CROUCH] = player_crouch_state;
states_array[states.FALL_ROLL] = player_fall_roll_state;
states_array[states.BLOCK] = player_block_state;

// create sprites array


sprites_array[states.IDLE] = s_player_idle;
sprites_array[states.WALK] = s_player_walk;
sprites_array[states.JUMP] = s_player_jump;
sprites_array[states.ATTACK_HAMMER] = s_player_attack_hammer;
sprites_array[states.ATTACK_ROCK] = s_player_attack_rock;
sprites_array[states.ATTACK_ARROW] = s_player_attack_arrow;
sprites_array[states.ATTACK_SHOOT] = s_player_attack_shoot;
sprites_array[states.CROUCH] = s_player_crouch;
sprites_array[states.FALL_ROLL] = s_player_fall;
sprites_array[states.BLOCK] = s_player_block;



animation script;
Im using a switch statement here.


///anim();

sprite_index = sprites_array[state];
image_xscale = facing;

switch(state) {
case states.JUMP:
if vsp < 0 state = states.JUMP else state = states.FALL_ROLL
break;

case states.ATTACK_HAMMER:
if hsp != 0 {
if on_ground()
hsp = lerp(hsp, 0, drag*10) ;
if hsp != 0 facing = sign(hsp);
}


break;
}
 

Simon Gust

Member
When I test the following code
Code:
hsp = lerp(hsp, 0, drag*10) ;
I also experience shaking / flicking.

This has to do with how lerp() works.
Basically the first value will change to the second value based on the third value as a percentage.

hsp changes to 0 by (drag * 10) percent. Or if you put in the numbers:
hsp changes to 0 by 350%.

This causes the hsp value to be reversed to a greater negative value, then next step, reversed to a positive even greater value and so on.
You should only lerp by less than 100% percentages.

Or if you want full control, don't use lerp at all and code your own acceleration and deceleration system.
 

Zynpex

Member
When I test the following code
Code:
hsp = lerp(hsp, 0, drag*10) ;
I also experience shaking / flicking.

This has to do with how lerp() works.
Basically the first value will change to the second value based on the third value as a percentage.

hsp changes to 0 by (drag * 10) percent. Or if you put in the numbers:
hsp changes to 0 by 350%.

This causes the hsp value to be reversed to a greater negative value, then next step, reversed to a positive even greater value and so on.
You should only lerp by less than 100% percentages.

Or if you want full control, don't use lerp at all and code your own acceleration and deceleration system.

This resolved the flicking back and forth, how ever the player still has a small HSP value, even if its set to 0 the player continues to move in the amount of a few pixels
 

Simon Gust

Member
This resolved the flicking back and forth, how ever the player still has a small HSP value, even if its set to 0 the player continues to move in the amount of a few pixels
I assume you're still using lerp(), hsp can also not ever reach 0, it can reach infinte decimal places after 0 though. Like 0.000001, but never 0.
That's why I suggested making your own acceleration and deceleration system using adding and subtracting values from hsp.
Code:
if (hsp > 0) hsp = max(hsp - drag, 0);
else
if (hsp < 0) hsp = min(hsp + drag, 0);
This is a basic deceleration code that I use in my games.
 

Slyddar

Member
After you apply the drag, put this code in to enable small lerp values to be just as good as zero.
Code:
//stop
if abs(hsp) <= 0.1 hsp = 0;
Also the change in the lerp function should be between 0 and 1, in order to move towards the target value.
 
Last edited:

Zynpex

Member
///player_attack_state()
//get input
get_input();


//calculate movements
calc_movement();

//check state
if state = states.ATTACK {
hsp = 0;
} else {

}

if image_index >= image_number - sprite_get_speed(sprite_index)/room_speed {
if hsp != 0 state = states.WALK else state = states.IDLE;
}

//apply movements
collision();


//apply animations
anim();
 

Zynpex

Member
Thank you how ever for your feed back, i slept on it for a day or two and came back was like i wonder if its because of anything in my attack state.
 

Zynpex

Member
:( i feel like an idiot......... none of this would have even worked hahah even though it should have....
The problem occured below, as i was checking to see if the HSP was != 0 then we use state WALK. because state WALK applies hsp in left or right, then made the player slide while attacking on the ground. So using the code in the ANIM script does not work, because the attack script is set up this way, all i did was add the
if state = states.ATTACK {
hsp = 0;
} else {

}

this says if we are not in the attack state then the character is switching states if we are holding A for left or D for right, and we apply hsp and the state for moving as normal.
 

Slyddar

Member
///player_attack_state()
//get input
get_input();


//calculate movements
calc_movement();

//check state
if state = states.ATTACK {
hsp = 0;
} else {

}
}
Well glad you worked it out, but just be aware there is no state change above this check state line, and you are in the ATTACK state, so hsp will always be set to 0. It's the same as having no if check and just setting hsp = 0.
 

Zynpex

Member
If im not in the attack state, that's what the else is for , this refers to any other state except the attack under certain conditions this will return to idle or walk, with the given hsp needed either 0 or as i have defined hsp to be when walking
 

Slyddar

Member
The script you posted has this at the top "///player_attack_state()", so while in this script, you are already in the attack state, unless changed. This line
Code:
//check state
if state = states.ATTACK {
is running in the attack state before you have done any other state changes, so it will 100% always be true, unless you are changing states in the calc_movement script, which in the course I never did. I'm just stating, that for the example you posted, the check does nothing.
 

Zynpex

Member
if state = states.ATTACK {
hsp = 0;
} else {

}

if image_index >= image_number - sprite_get_speed(sprite_index)/room_speed {
if hsp != 0 state = states.WALK else state = states.IDLE;
}


checks for attack, and if we are not in attack and not moving then we are in idle, another state, or we are moving but not attacking state = to states. walk other wise hsp = 0; and we attack and state = attack
 

Zynpex

Member
But im curious how you would code it, and the check state, is there for all scripts that are for states
and i have 12
 

Zynpex

Member
For example this is my Idle state
///player_idle_state()
//get input
get_input();


//calculate movements
calc_movement();

//check state
if hsp != 0 state = states.WALK;

if attack {
state = states.ATTACK;
image_index = 0;

}

//apply movements
collision();


//apply animations
anim();
 

Zynpex

Member
if state = states.ATTACK
hsp = 0;
i have coded it this way, because the idle state hsp = 0 as well as we are not moving. And since i also check for the hsp = 0; in this script, i felt like i must declare if we are not in the IDLE state but we are in the attack state, the hsp = 0;. But i will see what it does if i remove it i see what you mean now
 

Zynpex

Member
hsp = 0; works just was well, and yes its because the attack scripts uses states array to call for that sprite animation, i do not need to check if we are in the attack state. Thanks for the tip!
 

Slyddar

Member
If you don't want to allow hsp to be anything but zero while in the attack state, then as you've discovered, just putting hsp = 0 in the attack state will solve that.
 

Zynpex

Member
Yes, but if i were to only want hsp to be 0 while on the ground , but not while in the air this would not work, intresting, would i need to check on_ground?. As i would assume this would only make hsp=0 if were were on the ground?
 

Zynpex

Member
I just realized who I am talking to, haha small world Peter! Hello from the out side, really enjoyed your course!

///player_attack_state

//get_input
get_input();

//calc movement
calc_movement();

//check state
if on_ground(){
hsp = 0;

} else {
if !on_ground{
hsp = hsp

}
}
if image_index >= image_number - sprite_get_speed(sprite_index)/room_speed {
if !on_ground() state = states.JUMP else
if hsp != 0 state = states.WALK else state = states.IDLE
}

if jump {
jumped();
state = states.ATTACK;

}



I have left so code out here however there is more, this seems to have answered my question, this indeed does prevent hsp = 0 while in the attack state, but only if we are on the ground!
 

Simon Gust

Member
I think it's best if you switched off this kind of state machinery.
Is it really necessary to have a state for idling, walking, jumping? Those 3 you could just combine into 1 state.
If I look at your states you even have a crouch_block state, that alone has me cringing.
You would also benefit for using a more physicy system where physics apply independently to what state your player is in.
 

Zynpex

Member
You, really are an amazing teacher. The way you go about, having your students question decisions even if they are not really doing anything wrong, and what they are doing is right. But there is a much better or much more desired result, but with out giving them the answer and allowing them to solve the problem you are hinting towards with out giving away the solution before its actually needed! This makes good teachers outside of this virtual world!
 

Slyddar

Member
Haha, yeh I recognised the code when I read the thread. Glad you are experimenting and finding out what works. Just make sure you have completed the course before you try to implement your own code though, as it will make it much easier on you in the long run. And thank you for the kind words, I appreciate your insight. I guess being a school teacher helped me pick up a few tricks ;)

Is it really necessary to have a state for idling, walking, jumping? Those 3 you could just combine into 1 state.
If I look at your states you even have a crouch_block state, that alone has me cringing.
Hi Simon,
To be fair, you're commenting on a system you don't completely understand, so I can see how you might think it's confusing. In my course we create a state for each sprite, which essentially removes any work involving sprite allocations. A simple array is used to allocate the sprite based on the state. Once established, it's actually really intuitive, and useful. The course is currently the highest rated Gamemaker 2 course on Udemy, so I've found many people have enjoyed the system. As always though, each to their own.
 

Simon Gust

Member
Haha, yeh I recognised the code when I read the thread. Glad you are experimenting and finding out what works. Just make sure you have completed the course before you try to implement your own code though, as it will make it much easier on you in the long run. And thank you for the kind words, I appreciate your insight. I guess being a school teacher helped me pick up a few tricks ;)


Hi Simon,
To be fair, you're commenting on a system you don't completely understand, so I can see how you might think it's confusing. In my course we create a state for each sprite, which essentially removes any work involving sprite allocations. A simple array is used to allocate the sprite based on the state. Once established, it's actually really intuitive, and useful. The course is currently the highest rated Gamemaker 2 course on Udemy, so I've found many people have enjoyed the system. As always though, each to their own.
I understand it, short animation code, that's the system.
I don't understand what code you have in place for the physics. Does each state have it's own block of physics? Do you end up having copies of physics code in each state?
 

Slyddar

Member
I don't understand what code you have in place for the physics. Does each state have it's own block of physics? Do you end up having copies of physics code in each state?
It's a beginner course, with some intermediate content, so having separate physics for air/ground is not high on the priority list imho. We just use the same one script that each state accesses for it's physics.

I assume you're still using lerp(), hsp can also not ever reach 0, it can reach infinte decimal places after 0 though. Like 0.000001, but never 0.
We should clarify that floating point limitations will allow hsp to reach zero within a fairly short timeframe, and will not continue on calculating infinitely. Even at a lerp change value of 0.1, extremely large values will become zero within a few seconds.
 
Last edited:
Top