GMS 2 Help with enemy knockback

Elkrom

Member
hello I am trying to fix this knock-back effect on my enemy instances that I've implemented , basically using recycled code that i already had from a recoil effect from shaun spalding's platformer series (which I'm starting to think might not be the best way), and its kind of working but it looks like it's just jumping/teleporting back x amount of pixels rather than bouncing back smoothly. I'm wondering what i can do to achieve this effect and also what i can do to add a "knock-back frame" on the enemy. as in switch to a seperate sprite briefly as its being knocked backwards. watch "implementing game feel in archvale" if you want to see the effect i am talking about. but to be clear my game is a platformer not a top down rpg. thanks in advance this is my third post on here and ive had great feedback everytime.


[// Enemy Variables
vsp= 0;
hsp = 0;
move=0;
gunkickx=0;
gunkicky=0;

//Enemy step event
case e_state.idle:
{
hsp=(0) + gunkickx
gunkickx = 0;

vsp = (vsp + grav) + gunkicky;
gunkicky = 0; ]

and this is in the begin step event for my bullet object


[ if (place_meeting(x,y,pshootable))
{
with (instance_place(x,y,pshootable))
{
hp --;

hitfrom = other.direction;

gunkickx = lengthdir_x(20, other.image_angle)
gunkicky = lengthdir_y(20, other.image_angle)


}
instance_destroy();
}]
 

Elkrom

Member
just to clarify a little bit more im trying to have this effect on my enemy after its been shot or damaged. Not on the player when it is damaged. I didn't make that super clear.
 

NightFrost

Member
The problem is how you make the kick last only one frame by zeroing the kick variables after one use, and how you're moving them the length of 20 pixels on that frame. You have to make the kick last multiple frames by gradually shifting the kick values towards zero. You can use lerp to do that. For example
Code:
gunkickx = lerp(gunkickx, 0, 0.5);
would shift the value towards zero by half every step. You can adjust the kick's decay by making the interpolator value higher or lower. You also should lower the kick strength from 20 to make it look less like a teleport.
 

Elkrom

Member
Thank you @NightFrost works really nicely i had tried using lerp before i made the post but i did it wrong i think, i had it where i was initially establishing the gunkick variables in the weapons step event rather than in the enemies. I really appreciate the quick and easy response. Any idea of how i would go about adding my knockback frame while this effect is happening ? and also how are you putting the codes in those little windows so they're easier for people to read, i thought i knew how to do it but clearly I'm a noob.
 

NightFrost

Member
You seem to be using a state machine on your enemies, so one thing you could do to ease switching to approproate sprites is to create a separate state for knockback. So when they get hit by bullets, amongst other stuff you do you also would switch them to knockback state. Another advantage then is that you only need to worry about the knockback code in that state, and can remove it from any other state where it might be sitting. There's a small problem with exiting the knocback state. As the lerp command is lowering the knockback effect by taking a percentage off the value, it can run into really small fractions and it can appear the enemy has just frozen in place for a moment. So you need to say that when the knockback is small enough, that's good for now and exit the state. For example, when the knockback is 0.5 pixels or less.

So the state would look something like:
Code:
case e_state.knockback:
    hsp = gunkickx;
    vsp = vsp + grav + gunkicky;
    gunkickx = lerp(gunkickx, 0, 0.5);
    gunkicky = lerp(gunkicky, 0, 0.5);
    // We will measure current length of knockback by taking its vector length.
    var _kicklength = point_distance(0, 0, gunkickx, gunkicky);
    // If knockback is short enough, we switch to idle state.
    if(_kicklength <= 0.5) state = e_state.idle;
    // Set appropriate sprite here.
As for code formatting, you can use bbcode tags for that. Write (without spaces) [ code ] and [ /code ] and insert your code in between. Also, if you have the editor buttons active, the Insert button has a code option.
 

Elkrom

Member
hey i tried that and it didnt seem to work for me not sure what im doing wrong. and i'm sure the code is cringe to look at it i'm really new to this a lot of the stuff ive done is just stealing code from different tutorials and forcing them into place until it works. i also have a lot of code i just turned into comments so i can come back to it if i need it without having to rewrite it aka look up the tutorial i found it in. if you see anything i'm doing wrong then let me know. Thank you!


Code:
///  --------------------------   STATE MACHINE     ---------------

switch (state)
{
                        //IDLE STATE
    case e_state.idle:
    {
        hsp=(0) + gunkickx
        gunkickx = lerp(gunkickx, 0, 0.5);
       
        vsp = (vsp + grav) + gunkicky;
        gunkicky = lerp(gunkicky, 0, 0.5);
       
        ///Afraid of heights
            AfraidOfHeights();
           

// Animate 

if (place_meeting(x,y+1,obj_wall))
{
    grounded = true;
    if (movespeed!=0) sprite_index = spr_slimeIdle;    
}   
        else 
{
    grounded = false;
   
}
            if (grounded) && (afraidofheights) && (!place_meeting(x+hsp,y+1,obj_wall))
{
    hsp = - hsp;
}   
       
       
        if (distance_to_object(obj_player) < 96) state = e_state.chase;
       
       
    }
    break;
   
                        ///---------------- CHASE STATE ---------------
    case e_state.chase:
    {
        if instance_exists(obj_player)
        {
       
        if (!collision_line(x,y,obj_player.x,obj_player.y,obj_wall,false,false))
       
        {
        dir = sign(obj_player.x - x);
        hsp = (dir * 1.5) + gunkickx;
        gunkickx = lerp(gunkickx, 0, 0.5);
        vsp = (vsp + grav) + gunkicky;
        gunkicky = lerp(gunkicky, 0, 0.5);
        sprite_index = spr_slimeAttack
        if (abs(obj_player.x-x)<1) { hsp = 0; }
        if (distance_to_object(obj_player) > 128) state = e_state.idle;
        if instance_exists(obj_playerDead) state = e_state.idle;
        if (dir != 0) image_xscale = dir; 
       
        }
       


   
    }
}   
   
 break;                           
    case e_state.knockback:
    hsp = gunkickx;
    vsp = vsp + grav + gunkicky;
    //gunkickx = lerp(gunkickx, 0, 0.5);
    //gunkicky = lerp(gunkicky, 0, 0.5);
    // We will measure current length of knockback by taking its vector length.
    var _kicklength = point_distance(0, 0, gunkickx, gunkicky);
    // If knockback is short enough, we switch to idle state.
    if(_kicklength <= 0.5) state = e_state.idle;
    sprite_index = spr_slimeHit;
   
}

//vsp = vsp + grav;

// Dont walk off edges


//if (grounded) && (afraidofheights) && (!place_meeting(x+hsp,y+1,obj_wall))
//{
//    hsp = - hsp;
//}

///-----------------------COLLISION SCRIPT------------------------------

Collisions();

// Gravity
//if (vsp < 10) vsp += grav;

/// enemy rotates when hits wall
//if (hsp !=0) image_xscale = sign(hsp) * size; 
//image_yscale = size;


if (flash > 0)
{
    //audio_play_sound(snd_hit,6,0)
    sprite_index = spr_slimeFlash
    flash = flash - x;
}

//x=x;
//y=y;


[/code}
 

Elkrom

Member
this is the step event for my bullet object, my bad i forgot to include that.


Code:
if (place_meeting(x,y,pshootable))
{
    with (instance_place(x,y,pshootable))
    {
        hp --;
        flash = 3;
        hitfrom = other.direction;
       
        gunkickx = lengthdir_x(17, other.image_angle)
        gunkicky = lengthdir_y(17, other.image_angle)
       
       
       
       
    }
    instance_destroy();
}



if (place_meeting(x,y,obj_wall)) instance_destroy();
 
Put this into an object into an empty room so you can see how this works, with a camera and whatnot, this makes the object shoot towards the mouse when you click and will do that slow effect you wanted afterward. You can edit this and fix it to how you want it for your enemy. I'm using motion_set and friction to achieve this effect.

I was also using this for another project, so there is the effect where if you tap a lot really fast the effect will wear down over more clicks, you can simply remove that. It is in the step event.

This will help you understand motion a little better.

Create Event
Code:
/// @description

tapped = false;

speed_ = 6;
speed_max = speed_;

long_atk = false;

atk_tap_timer = room_speed*0.7;
atk_tap_timer_max = atk_tap_timer;
Step Event
Code:
/// @description Control

if device_mouse_check_button_released(0,mb_left){

    if long_atk = false{   
        dir = point_direction(x,y,mouse_x,mouse_y);       
        motion_set(dir,speed_);   
        long_atk = true;
    }

    if long_atk == true{
        if speed_ > 0{speed_ = speed_-1;}
        dir = point_direction(x,y,mouse_x,mouse_y);       
        motion_set(dir,speed_);
        atk_tap_timer = atk_tap_timer_max;
    }
}

if long_atk == true{
    if atk_tap_timer > 0{atk_tap_timer -= 1;}
    if atk_tap_timer <= 0{atk_tap_timer = atk_tap_timer_max;long_atk = false; speed_ = speed_max}
}

friction = 0.2;
Draw - You don't need this event, this is just to show the values being drawn about the object. Its a debug.

Code:
/// @description debug
draw_self();

draw_set_font(fnt_test);

draw_text(x,y-15,""+string(atk_tap_timer));

if long_atk == true{draw_set_color(c_red);}
if long_atk == false{draw_set_color(c_lime);}
draw_text(x,y-25,""+string(long_atk));
 

Elkrom

Member
Thanks @spaceman_games but I already figured out the knockback issue and your solution Wouldn’t help me anyways as I’m using Gamepad input (which I should’ve made a point to explain I just forgot). My current issue is trying to get a knock back sprite to show while the knockback is happening, can’t seem to figure it out.
 
Top