SOLVED [Beginner] Problem with Players facing direction when enemy executes hit animation

N

Natonius

Guest
Hello there,
i'm new in the coding world and hope to find help here. My problem is the following: Every time an enemy does the attack-animation, the player looks at the enemy for a second. It doesn't matter if he hits the Player or not. To illustrate it, the enemy is standing (on the x-axis) to the right of the player and executes the attack-animation. The player runs towards the enemy and past him. Enemy executes the attack animation and hits nothing. Shortly after the attack animation is finished, the player switches to the left idle position (yes, i have a right and left positions for the whole moveset. image_xscale is out of question for me). This applies to both sides, in this case the player is facing to the right.


GML:
//o_knight is the enemy, o_hero the player

//o_knight's Attack-State

case "attack":
        #region Attack State
    
        set_state_sprite(s_knight_attack, 0.6, 0);
    
        if animation_hit_frame(4)
        {
            audio_play_sound(a_swipe, 3, false)
            if o_hero.invisible == 0 and o_hero.hp > 0
            {
                if o_knight.x < o_hero.x
                {
                    create_hitbox(x, y, self, s_knight_attack_damage, 4, 4, 5, image_xscale);
                    o_hero.state = "knockback left";               
                }
            
                if o_knight.x > o_hero.x
                {
                    create_hitbox(x, y, self, s_knight_attack_damage, -4, 4, 5, image_xscale);
                    o_hero.state = "knockback right";               
                }
            
            }
        }
        if animation_end()
        {
            state = "chase";
        }
    
        #endregion
        break;
GML:
// create_hitbox script (thx to HeartBeast)
///@arg x
///@arg y
///@arg creator
///@arg sprite
///@arg knockback
///@arg lifespan
///@arg damage
///@arg xscale

var x_position = argument0;
var y_position = argument1;
var creator = argument2;
var sprite = argument3;
var knockback = argument4;
var lifespan = argument5;
var damage = argument6;
var xscale = argument7;

var hitbox = instance_create_layer(x_position, y_position, "instances", o_hitbox);
hitbox.sprite_index = sprite;
hitbox.creator = creator;
hitbox.knockback = knockback;
hitbox.alarm[0] = lifespan;
hitbox.damage = damage;
hitbox.image_xscale = xscale;
GML:
// Players Knockback State

case "knockback right":
        #region Knockback Right State
        facing = 1;
        invisible = 1;
        knockback_state(s_hero_hitstun_weight_right, "idle right");
    
    
        #endregion
        break;

    case "knockback left":
        #region Knockback Left State
        invisible = 1;
        facing = 0;
        knockback_state(s_hero_hitstun_weight_left, "idle left");
    
    
        #endregion
        break;
GML:
// knockback_state script

///@arg knockback_sprite
///@arg next_state

var knockback_sprite = argument0;
var next_state = argument1;

set_state_sprite(knockback_sprite, 0, 0);

move_and_collide(knockback_speed, 0);

var knockback_friction = 0.3;
knockback_speed = approach(knockback_speed, 0, knockback_friction);
if knockback_speed == 0
    {
        state = next_state;
    }
I don't want the player to stop animations (Run, role, even idle) for one frame whenever o_knight is executing the attack state. How can i handle this?
Sorry for my bad english. I hope i was able to descripe the problem. If not, let me know.
 
Last edited by a moderator:

Nidoking

Member
You're setting a knockback state.

The knockback state sets the "facing" variable that I assume sets a direction for the sprite.

Presumably, after the animation finishes, there's a period between changing back to whatever your idle state is and reasserting the proper "facing" value.
 

saffeine

Member
GML:
if o_knight.x < o_hero.x
{
    create_hitbox(x, y, self, s_knight_attack_damage, 4, 4, 5, image_xscale);
    o_hero.state = "knockback left";                                               // < Here.
}

if o_knight.x > o_hero.x
{
    create_hitbox(x, y, self, s_knight_attack_damage, -4, 4, 5, image_xscale);
    o_hero.state = "knockback right";                                           // < Here.
}
it seems like you have it set up so that the hero state is changed when the hitbox is created, not when the hitbox actually hits something.
 
N

Natonius

Guest
Thank u two for helping me! I uploaded a short video in the first post to show the problem clearly.

@Nidoking
Presumably, after the animation finishes, there's a period between changing back to whatever your idle state is and reasserting the proper "facing" value.
That's right, i've created a facing variable, but for another mechanism (camera) it has no other functions

it seems like you have it set up so that the hero state is changed when the hitbox is created, not when the hitbox actually hits something.
exactly that is the problem. Can u help me with the further steps? I'm an absolute noob at this and need step by step instructions...

thanks so far.
 

saffeine

Member
Thank u two for helping me! I uploaded a short video in the first post to show the problem clearly.

@Nidoking


That's right, i've created a facing variable, but for another mechanism (camera) it has no other functions



exactly that is the problem. Can u help me with the further steps? I'm an absolute noob at this and need step by step instructions...

thanks so far.
it should be as simple as putting it into the script that handles the collision with o_hitbox.
the issue is only that it's in the attack animation, and not in the "player getting hit" event, which are separate.
those two lines will have to be removed from that code, and put in the collision event between the player / hitbox.

in the collision event between the player and the hitbox, you should put something along the lines of:
GML:
//    if it's in the player object, put this.
if( other.image_xscale > 0 ){
    state = "knockback left";
}else{
    state = "knockback right";
}

//    if it's in the hitbox object, put this.
if( image_xscale > 0 ){
    o_hero.state = "knockback left";
}else{
    o_hero.state = "knockback right";
}
you might have to swap the two lines that set the knockback. i'm not sure which direction is which in your project ( whether the variable is set depending on knock direction or facing direction ).
 
N

Natonius

Guest
o_hitbox has a collision event with o_lifeform. o_hero is a child of o_lifeform, so he inherits hp, max_hp, and knockback_speed. The o_hero's knockback state functions very well. my problem is a different one.

whenever the enemy executes the attack state, o_hero idles for one single frame (left or right depends on the attack direction). exspecially when enemy hits the void o_hero still idles...

because i spent the whole day looking for the mistake and i obviously need your help, i want to share even more code with you. In the hope of moving forward with my progress.

in the collision event o_hitbox with o_lifeform:

GML:
if creator = noone or creator == other or ds_list_find_index(hit_objects, other) != -1
{
    exit;  
}

other.hp -= damage;
audio_play_sound(a_medium_hit, 4, false)
repeat(10)
{
    instance_create_layer(other.x, other.y - 12, "Effects", o_hit_effect);
}

if instance_exists(o_hero)
{
    #region Player Death State
    if creator.object_index == o_hero and other.hp <= 0 and other.state != "death"
    {
    o_hero.kills += 1;
    }
   
   
    if other.object_index == o_hero
    {
        //Screenshake when Player got Hit
        add_screenshake(8, 8);
       
        if other.hp <= 0
        {
            var number = sprite_get_number(s_skeleton_bones);
            for (var i = 0; i < number; i++)
            {
                var bx = other.x + random_range (-8, 8);
                var by = other.y + random_range (-24, 8);
                var bone = instance_create_layer(bx, by, "Instances", o_skeleton_bone);
                bone.direction = 90 - (image_xscale * random_range(30, 60));
                bone.speed = random_range(3, 10);
                bone.image_index = i;
                if i == 5 bone.image_angle = 130;
            }
           
            // Highscores will be set:
            ini_open("save.ini")
            ini_write_real("Scores", "Kills", other.kills);
            var highscore = ini_read_real("Scores", "Highscore",0);
            if other.kills > highscore
            {
                ini_write_real("Scores", "Highscore", other.kills)
            }
            ini_close();
        }
    }
    else
    {
        //Screenshake when Hit enemy
       
        other.alarm[0] = 120;      
        add_screenshake(5, 5);
    }
   
    #endregion
}

ds_list_add(hit_objects, other);
if other.state != "death" /* add knockback exception for boss -> */ and other.object_index != o_boss
{
    if other.object_index == o_hero
    {
        if o_hero.x > other.x
        {
            o_hero.state = "knockback left";
        }
       
        if o_hero.x < other.x
        {
            o_hero.state = "knockback right";
        }      
    }
   
   
    if other.object_index != o_hero
    {
        other.state = "knockback";
    }
       
}
other.knockback_speed = knockback * image_speed;

GML:
case "attack":
        #region Attack State
       
        set_state_sprite(s_knight_attack, 0.6, 0);
       
        if animation_hit_frame(4)
        {
            audio_play_sound(a_swipe, 3, false)
            if o_hero.invisible == 0 and o_hero.hp > 0
            {
                if o_knight.x < o_hero.x
                {
                    create_hitbox(x, y, self, s_knight_attack_damage, 4, 4, 5, image_xscale);
                   
                    o_hero.state = "knockback left";
                   
                }
               
                if o_knight.x > o_hero.x
                {
                    create_hitbox(x, y, self, s_knight_attack_damage, -4, 4, 5, image_xscale);
                   
                    o_hero.state = "knockback right";
                   
                }
               
               
            }
        }
        if animation_end()
        {
            state = "chase";
        }

GML:
switch (state)
{
    case "idle right":
        #region Idle Right State
        set_state_sprite(s_hero_idle_right, 1, 0);
        facing = 1;
        invisible = 0;
           
        if input.right
        {
            state = "move right";
        }
       
        if input.left
        {
            state = "move left";
        }
       
        if input.roll  
        {
            state = "roll right";
        }
   
        if input.attack
        {
            state = "attack one right";      
        }
       
        #endregion
        break;
   
    case "idle left":
        #region Idle Left State
        set_state_sprite(s_hero_idle_left, 1, 0);
        facing = -1;
        invisible = 0;
           
        if input.right
        {
            state = "move right";
        }
       
        if input.left
        {
            state = "move left";
        }
       
        if input.roll  
        {
            state = "roll left";
        }
   
        if input.attack
        {
            state = "attack one left";      
        }
       
        #endregion
        break;
   
    case "move right":
        #region Move Right State
        facing = 1;
        invisible = 0;
       
        if input.right
        {
            move_and_collide(run_speed, 0);
            image_xscale = 1;
            sprite_index = s_hero_run_right;
            image_speed = 1;          
        }

               
        if keyboard_check_released(vk_right)
        {
            state = "idle right";
        }
       
               
        else
        {
            if animation_hit_frame(2) or animation_hit_frame(5)
            {
                audio_play_sound(a_footstep, 5, false)
            }
        }
           
        if input.roll
   
        {
            state = "roll right";
        }
   
        if input.attack
        {
            state = "attack one right";      
        }      
       
        if input.left
        {
            state = "move left";
        }
        #endregion
        break;
           
    case "move left":
        #region Move Left State      
        facing = -1;
        invisible = 0;
       
        if input.left
        {
            move_and_collide(-run_speed, 0);
            image_xscale = 1;
            sprite_index = s_hero_run_left;
            image_speed = 1;          
        }    
       
        if keyboard_check_released(vk_left)
        {
            state = "idle left";
        }
       
        else
        {
            if animation_hit_frame(2) or animation_hit_frame(5)
            {
                audio_play_sound(a_footstep, 5, false)
            }
        }
           
        if input.roll
   
        {
            state = "roll left";
        }
   
        if input.attack
        {
            state = "attack one left";      
        }      
       
        if input.right
        {
            state = "move right";
        }
       
        #endregion
        break;
       
    case "roll right":
        #region Roll Right State
        set_state_sprite(s_hero_roll_right, 1.5, 0);
        facing = 1;
        invisible = 1;
       
        if image_xscale == 1
            {
                move_and_collide(roll_speed, 0);
            }
           
       
            if animation_end()
            {
                state = "idle right";
            }
                   
        #endregion
        break;
           
    case "roll left":
        #region Roll Left State
        set_state_sprite(s_hero_roll_left, 1.5, 0);
        facing = -1;
        invisible = 1;
                       
            if image_xscale == 1
            {
                move_and_collide(-roll_speed, 0);
            }
       
            if animation_end()
            {          
                state = "idle left";          
            }
           
        #endregion
        break;
       
    case "attack one right":  
        #region Attack One Right State      
        set_state_sprite(s_hero_attack_one_right, 1.25, 0);
        facing = 1;
        invisible = 0;
       
        if animation_hit_frame(1)
        {
            audio_play_sound(a_swipe, 3, false)
            create_hitbox(x, y, self, s_hero_attack_one_damage_right, 3, 4, 5 , image_xscale);
        }
               
        if input.attack and animation_hit_frame_range(4, 6)
        {
            state = "attack two right";      
        }
       
        if animation_end()
        {
            state = "idle right";
        }  
       
        #endregion
        break;
       
    case "attack one left":  
        #region Attack One Left State      
        set_state_sprite(s_hero_attack_one_left, 1.25, 0);
        facing = -1;
        invisible = 0;
       
        if animation_hit_frame(1)
        {
            audio_play_sound(a_swipe, 3, false)
            create_hitbox(x, y, self, s_hero_attack_one_damage_left, -3, 4, 5 , image_xscale);
        }
       
        if input.attack and animation_hit_frame_range(4, 6)
        {
            state = "attack two left";      
        }
       
        if animation_end()
        {
            state = "idle left";
        }
           
       
       
        #endregion
        break;
       
    case "attack two right":
        #region Attack Two Right State
        set_state_sprite(s_hero_attack_two_right, 1, 0);
        facing = 1;
        invisible = 0;
       
        if animation_hit_frame(1)
        {
            audio_play_sound(a_swipe, 3, false)
            create_hitbox(x, y, self, s_hero_attack_two_right_damage, 3, 4, 5, image_xscale);
        }
       
        if input.attack and animation_hit_frame_range(2, 4)
       
        {
            state = "attack three";      
        }
       
        if animation_end()
        {
            state = "idle right";
        }
       
        #endregion
        break;
       
    case "attack two left":
        #region Attack Two Left State
        set_state_sprite(s_hero_attack_two_left, 1, 0);
        facing = -1;
        invisible = 0;
       
        if animation_hit_frame(1)
        {
            audio_play_sound(a_swipe, 3, false)
            create_hitbox(x, y, self, s_hero_attack_two_left_damage, -3, 4, 5, image_xscale);
        }
       
        if input.attack and animation_hit_frame_range(2, 4)
       
        {
            state = "attack three";      
        }
       
        if animation_end()
        {
            state = "idle left";
        }
       
        #endregion
        break;
       
    case "attack three":
         #region Attack Three State
        set_state_sprite(s_skeleton_attack_three, 0.7, 0);
        invisible = 0;
       
        if animation_hit_frame(2)
        {
            audio_play_sound(a_swipe, 3, false)
            create_hitbox(x, y, self, s_skeleton_attack_three_damage, 4, 4, 8, image_xscale);
        }
       
        if animation_end()
        {
            state = "idle right"
        }
       
        #endregion
        break;
       
    case "knockback right":
        #region Knockback Right State
        facing = 1;
        invisible = 1;      
        knockback_state(s_hero_hitstun_weight_right, "idle right");          
        #endregion
        break;
   
    case "knockback left":
        #region Knockback Left State
        facing = -1;
        invisible = 1;
        knockback_state(s_hero_hitstun_weight_left, "idle left");      
        #endregion
        break;
       
    default:
       
        state = "idle right";
        break;
}

These must be the affected codes. Sorry to overwhelm you with thousand lines of code. But i'm going crazy over this...
 

saffeine

Member
o_hitbox has a collision event with o_lifeform. o_hero is a child of o_lifeform, so he inherits hp, max_hp, and knockback_speed. The o_hero's knockback state functions very well. my problem is a different one.

whenever the enemy executes the attack state, o_hero idles for one single frame (left or right depends on the attack direction). exspecially when enemy hits the void o_hero still idles...

because i spent the whole day looking for the mistake and i obviously need your help, i want to share even more code with you. In the hope of moving forward with my progress.

These must be the affected codes. Sorry to overwhelm you with thousand lines of code. But i'm going crazy over this...

yeah, i do understand what the issue is and i'm almost sure it's because you have this in the enemy attack code:
GML:
o_hero.state = "knockback left";
o_hero.state = "knockback right";
this is being called whether the player has been hit or not, because it's called when the enemy attacks.
you also have it in the collision code, it should only be in one place.

remove those two lines from the enemy's attack code and the issue should be solved, hopefully.

another thing to note is that you have o_knight in the knight's attack script. with more instances of o_knight this could cause a lot of issues for you later.
since the object that's calling it is the object that's checking, you can just compare it to x / y instead of o_knight.x / o_knight.y.
if you only plan to have one knight in the room at a time then you shouldn't have too much of an issue, just something to keep in mind.
 
N

Natonius

Guest
yeah, i do understand what the issue is and i'm almost sure it's because you have this in the enemy attack code:
GML:
o_hero.state = "knockback left";
o_hero.state = "knockback right";
this is being called whether the player has been hit or not, because it's called when the enemy attacks.
you also have it in the collision code, it should only be in one place.

remove those two lines from the enemy's attack code and the issue should be solved, hopefully.

another thing to note is that you have o_knight in the knight's attack script. with more instances of o_knight this could cause a lot of issues for you later.
since the object that's calling it is the object that's checking, you can just compare it to x / y instead of o_knight.x / o_knight.y.
if you only plan to have one knight in the room at a time then you shouldn't have too much of an issue, just something to keep in mind.

thank u very much. It worked so far and i understand your point. The player doesn't idle anymore.
 
Last edited by a moderator:
Top