• 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!

Animation issues

K

Kptkrunch

Guest
I have been working on state machines and have gone through Gloomy Toads tutorial, Shaun Spaldings tutorial, and looked through several pre-built options. The issue I have regardless of the code I use is that the player character gets suck on animations/sprites.

When I land from a jump it stays in the falling sprite, when I stop running it remains in the running sprite, and so on. I can tell the character is switching states but I cannot make rhyme or reason as to what could be causing this.

I typically try to figure stuff out on my own, but I have been trying to fix it on my own for weeks. Is there anything specific that trips up new coders that I should look for? Hopefully it's something simple.

If not I can post my code when I get home.

Thank you in advance!
 
K

Kptkrunch

Guest
I tried to take a screenshot of the error but when I do it shows that the sprite is correct. I think it's rapidly switching facing or doing both image xscales. It's weird because it sometimes pulls to the right as well like it has some horizontal speed. I cannot figure it out, and it feels like a glitch. I'm out of ideas.

obj_controller, create event

/// @desc game controller

global.left = (ord("A"));
global.right = (ord("D"));
global.jump = (ord("W"));
global.crouch = (ord("S"));
global.attack = (vk_up);

enum player_states
{
normal,
run,
fall,
jump,
crouch,
attack,

}

obj_player, create event

/// @desc define vars

depth = -10;

act_left = global.left;
act_right = global.right;
act_jump = global.jump;
act_crouch = global.crouch;
act_attack = global.attack;

/// MAXIMUMS

hor_speed_max = 5.0;
vert_speed_max = 20;
///boost_speed_max = 4;

/// X and Y speeds

hor_speed = 0;
vert_speed = 0;

/// FORCES

frict = .2;
accel = .25;
weight = 0.20;
///grav = 3;
jump_power = 15;
///boost_power = .10;


/// PLAYER STATE VARS

current_state = player_states.normal;
grounded = true;

_________________________________

obj_player, step event

/// @desc

action_left = keyboard_check(act_left);
action_right = keyboard_check(act_right);
action_jump = keyboard_check(act_jump);
action_crouch = keyboard_check(act_crouch);
action_attack = keyboard_check(act_attack);
cancel_left = keyboard_check_released(act_left);
cancel_right = keyboard_check_released(act_right);

/// Player facing

if (hor_speed > 0)
{
image_xscale = 1;

}

else if (hor_speed < 0)
{
image_xscale = -1;

}

/// Horizontal movement

if(action_left && !place_meeting(x - 1, y, obj_par_wall))
{
if(hor_speed > -hor_speed_max)
{
hor_speed -= accel;

}

}

else if(action_right && !place_meeting(x + 1, y, obj_par_wall))
{
if(hor_speed < +hor_speed_max)
{
hor_speed += accel;

}

}

/// Friction

if (!action_left && !action_right)
{
if(hor_speed != 0)
{
hor_speed -= sign(hor_speed) * frict;

}

}

if(!place_meeting(x + hor_speed, y, obj_par_wall))
{
x += hor_speed;

}

else
{
while(!place_meeting(x + sign(hor_speed), y, obj_par_wall))
{
x += sign(hor_speed);

}

hor_speed = 0;

}

/// Gravity

if(current_state != player_states.normal)
{
if(vert_speed < vert_speed_max)
{
vert_speed += weight;

}

}

if(!place_meeting(x, y + vert_speed, obj_par_wall))
{
y += vert_speed;

}

else
{
move_contact_solid(point_direction(x, y, x, y + vert_speed), vert_speed_max);
vert_speed = 0;

}

_________________________________

/// Player state switch

switch (current_state)
{
/// Case 1
case player_states.normal:
scr_normal();
break;

/// Case 2
case player_states.run:
scr_run();
break;

/// Case 3
case player_states.jump:
scr_jump();
break;

/// Case 4
case player_states.crouch:
scr_crouch();
break;

/// Case 5
case player_states.fall:
scr_fall();
break;

/// Case 6
case player_states.attack:
scr_attack();
break;

}

scr_player_animations();

_________________________________

scr_player_animations, script for controlling animations

/// @desc
animation = sprite_index;

switch (animation)
{
/// case 1
case player_states.normal:
sprite_index = spr_player_idle;
image_speed = 0;
break;

/// case 2
case player_states.run:
sprite_index = spr_player_run;
image_speed = 0;
break;

/// case 3

case player_states.jump:
sprite_index = spr_player_jump;
image_speed = 0;
break;

/// case 4
case player_states.fall:
sprite_index = spr_player_fall;
image_speed = 0;
break;

/// case 5
case player_states.attack:
sprite_index = spr_attack_air;
image_speed = 0;
break;

}

scr_normal, normal state script

/// @desc

grounded = true;

// switch to jump state

if(action_jump)
{
current_state = player_states.jump;

}

// swtich to fall state

if(!place_meeting(x, y + 1, obj_par_wall))
{
current_state = player_states.fall;

}

// switch to attack state

if(action_attack)
{
current_state = player_states.attack;

}

// switch to crouch state

if(action_crouch)
{
current_state = player_states.crouch;

}

scr_run, player run state script

/// @desc


if(cancel_left || cancel_right)
{
current_state = player_states.normal;

}

if(action_jump)
{
current_state = player_states.jump;

}

if(action_attack)
{
current_state = player_states.attack;

}

scr_jump, player jumping state script

/// @desc

y += -jump_power;
grounded = false;

if(vert_speed > 1)
{
current_state = player_states.fall;

}

if(action_attack)
{
current_state = player_states.attack;

}

if (place_meeting(x, y + 1, obj_par_wall))
{
current_state = player_states.normal;

}

scr_crouch, player crouching state script

/// @desc

grounded = true;


if(!action_crouch)
{
current_state = player_states.normal;

}

if(action_attack)
{
current_state = player_states.attack

}

scr_attack, player attack state script

/// @desc

if(!grounded)
{
sprite_index = spr_attack_air;
image_index = 0;
image_speed = 1;

}

else
{
sprite_index = spr_player_attack;
image_index = 0;
image_speed = 1;

}


if(ev_animation_end)
{
current_state = player_states.normal;

}

scr_fall, player falling state script

/// @desc

grounded = false;

if(place_meeting(x, y + 1, obj_par_wall))
{
current_state = player_states.normal;

}

if(action_attack)
{
current_state = player_states.attack;

}
 

Simon Gust

Member
That code is very long and complicated, more than it needs to be.
First, I suggest rethinking the state system. Do you really need a state for every little action?
I like to have a state "normal" for things like standing, running, jumping, crouching.

Code:
case player_states.normal:
    
    // image_xscale code here
    // horizontal movement code here
    // friction code here (if you only want friction in normal state)
    // jump code here
    
    grounded = place_meeting(x, y+1, obj_par_wall);
    
    if (grounded)
    {
        // on the ground
        if (action_crouch)
        {
            // crouching
            // sprite_index = spr_player_crouch;
            // image_speed = 0;
        }
        else
        {
            // standing / walking
            if (abs(hor_speed) > 1.00)
            {
                // walking
                sprite_index = spr_player_run;
                image_speed = 0.5;
            }
            else
            {
                // standing
                sprite_index = spr_player_idle;
                image_speed = 0;
            }
        }
    }
    else
    {
        // in the air
        if (vert_speed > 1) 
        {
            // falling
            sprite_index = spr_player_fall;
            image_speed = 0;
        }
        else
        {
            // jumping
            sprite_index = spr_player_jump;
            image_speed = 0;
        }
    }
    
    // initiate attack code here
    
break;
something like this.

When that is fixed, you just need to fix every other part of your code, it's not optimal.
 
K

Kptkrunch

Guest
Thank you for your advice. I am pretty new at coding in general, and this is the first self built machine from what I've learned.

Honestly I still don't know what is too much or too little so that's likely part of the problem I'm having. I thought having the states broken down more specifically would help, but apparently not. I plan to expand this into a very large machine with lots of ways to traverse terrain, but it starts with getting the core working.

I will try your method this evening and let you know. Thanks for the advice.
 
R

Rattlejaw

Guest
In each state, you have to have the transition code to go back to a previous state or new state. Also, and this is important, make sure you add break; at the end of each state, otherwise states start overriding each other.

Inside your jumping state, make sure you assign the jumping sprite. Then you have to make sure you transition to your falling state when you start to fall down. So inside your jumping state, you transition to falling by doing something like if vspeed > 0 and !place_meeting (x,y+1, obj_wall) { state = "falling";}

Inside your falling state, to go back idle, you have to transition to idle, like if place_meeting (x, y+1, obj_wall) { state = "idle"}.

if your animation is stuck in running instead of going back to idle, look inside your running state. Make sure you have the transition code to go back to idle, something like- if not keyboard_check (vk_right) or not keyboard_check (vk_left) {state = "idle";}

So for a simple state machine, create a variable called state. Set state = "idle";
Then in the step event,
switch(state)
{
case "idle":
hspeed = 0;
vspeed = 0;
sprite_index = spr_idle;

// now for transition to other states
if keyboard_check (vk_right) { state = "running";}
if keyboard_check (vk_left) { state = "running";}
if keyboard_check_pressed (vk_space)
{
jump_code;
state = "jumping";}

// very important!!
break;


case "running":
vspeed = whatever you set your speed to;
sprite_index = spr_running;

//flips your sprite
if keyboard_check (vk_right)
{
image_xscale = 1;
}

if keyboard_check (vk_left)
{
image_xscale = -1;
}

//now transition to other states
if !keyboard_check (vk_right) && !keyboard_check (vk_left) { state = "idle"};
if keyboard_check_pressed (vk_space)
{
jump code;
state = "jumping"}

//very important
break;
}
I didn't put every single line of code or transition code in that example, but that should be enough to get you started. I tried to make it as readable as possible. Remember, every state needs transition code to go to another state. Make sure in each state, you have the right sprite assigned to sprite_index = , and then make sure you add break; to the end of each state.

You are on the right track in regards to learning about state machines, because once you get it down, things start falling into place. For me personally, I like having a new state whenever my sprite animation is going to require a different sprite. However, you don't have to change states if you're simply flipping the xscale of a sprite during a state.
 
Last edited by a moderator:
Top