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

GameMaker Having knockback issues with my 2D platformer

J

Jon Kennel

Guest
v2.1.3.189

My game (loosely based on "Beowulf") has a character who can press 'x' to swing his sword. This creates my (obj_sword_hitbox) in front of the "obj_player" for a single frame. When the (obj_sword_hitbox) collides with (obj_enemy_parent), I want to create a knock-back effect on the enemy.

(I'M CREATING THIS GAME FOR MY ENGLISH CLASS AND IT IS DUE ON TUESDAY SO QUICK RESPONSES WOULD BE GREATLY APPRECIATED)

Anyway, the knock-back code only works properly if the enemy is not being knocked back against or near a wall. If I hit an enemy near a wall, the enemy seems to get stuck inside of the wall and continues to jump up and fall back down.

I have a state machine set up where the enemy switches between an "idle" state and a "damaged" state.

I think it's because the enemy is pushed inside of the wall by the knock-back and is therefore unable to touch the ground and reset back to it's "idle" state; and is therefore perpetually stuck in the "damaged" state.

But that's just my theory.

I've tried using (place_meeting) and (place_free) functions to check for a wall; but I haven't been able to get those to work. I've also tried looking through various forum posts for solutions; but I haven't had any luck with that either.


Here is the collision event with the (obj_sword_hitbox) - (inside of "obj_enemy_parent")
Code:
/// Collision event with obj_sword_hitbox
if (state != scr_enemy_damaged) {
    image_blend = make_color_rgb(220, 150, 150)
    
    hsp = (sign(x-other.x)*4);
    vsp = -3;
    
    state = scr_enemy_damaged;
    
    scr_collision();
}

if (instance_exists(obj_enemy_stats)) {
    obj_enemy_stats.hp -= 1;
}


Step event - inside obj_enemy_parent
Code:
/// Step event
// Execute state
script_execute(state);

Create event - inside of obj_enemy_parent

Code:
/// Create event 
//Initializes variables
hsp = 0;
vsp = 0;
grav = .2;

// Sets default state
state = scr_enemy_idle;


Script "scr_enemy_damaged"

Code:
/// scr_enemy_damaged
// Face the source of the damage
if (hsp != 0) {
    image_xscale = -sign(hsp);
}

// Apply gravity
if (!place_meeting(x,y+1,obj_solid)) {
    vsp += grav;
} else {
    vsp = 0;
    // Apply friction
    hsp = lerp(hsp,0,1);
}

// Collision code
scr_collision();

// Change back to idle state
if (hsp == 0 && vsp == 0) {
    image_blend = c_white;
    state = scr_enemy_idle;
}

script "scr_enemy_idle"
Code:
/// scr_enemy_idle
// Set color back to normal
image_blend = c_white;

// Flips sprite towards player
if (instance_exists(obj_player)) {
    image_xscale = -(sign(x-obj_player.x));
}

// Applies gravity
if (!place_meeting(x,y+1,obj_solid)) {
    vsp += grav;
} else {
    vsp = 0;
}

// Collsion code
scr_collision();


Here is the script "scr_collision"
Code:
 /// scr_collision
// Horizontal collision
if (place_meeting(x+hsp,y,obj_solid)) {
    while (!place_meeting(x+sign(hsp),y,obj_solid)) {
        x += sign(hsp);
    }
    hsp = 0;
}

x += hsp;

// Vertical Collision
if (place_meeting(x,y+vsp,obj_solid)) {
    while (!place_meeting(x,y+sign(vsp),obj_solid)) {
        y += sign(vsp);
    }
    vsp = 0;
}

y += vsp;

I think I've posted all relevant code here but if you have any questions or need any more info then just ask. Like I said before, I only have 3 days to finish this game and still have quite lot of work ahead of me. This knock-back issue is really holding me back, so I appreciate anyone who takes the time to respond.

Thanks.

screenshot game.PNG

gaaame.PNG
 

Slyddar

Member
Somehow it looks like the gravity is not being apllied, and since you are zeroing the vsp maybe try just applying gravity all the time, it doesn't really matter if you are on the ground.

Just remove the "Apply gravity" section from all states, and add your normal gravity code instead to see if that helps. You can also apply friction each step too. It's not going to slow down the game or anything applying it each step. Also your lerp function is decreasing hsp by 0% each step. The last number is the percent of hsp, that will be assigned to be the new hsp. So with 0.95 hsp will be 95% of the current hsp.
Code:
//Apply Gravity
vsp += grav;
//Friction
hsp = lerp(hsp,0,0.98);
Another suggestion is sometimes the cause of getting stuck in walls is due to image_xscale flipping, because the origin might be off by a pixel, or more. Instead of flipping the actual sprite you just monitor it via a variable and draw a flipped version of it. Create a "facing" variable and apply the flip code to that instead. Then in draw use draw_sprite_ext and use your facing variable in place of image_xscale.
 
Last edited:
J

Jon Kennel

Guest
What is the code for
script_execute(state) in the step event?
In the create event, I set up the state variable to equal scr_enemy_idle (state = scr_enemy_idle).
In the step event, script_execute(state) basically just runs the code in the enemy idle script.

So then, the code for the script_execute(state) would be found inside of the script, "scr_enemy_idle."
 
J

Jon Kennel

Guest
Somehow it looks like the gravity is not being apllied, and since you are zeroing the vsp maybe try just applying gravity all the time, it doesn't really matter if you are on the ground.

Just remove the "Apply gravity" section from all states, and add your normal gravity code instead to see if that helps. You can also apply friction each step too. It's not going to slow down the game or anything applying it each step. Also your lerp function is decreasing hsp by 0% each step. The last number is the percent of hsp, that will be assigned to be the new hsp. So with 0.95 hsp will be 95% of the current hsp.
Code:
//Apply Gravity
vsp += grav;
//Friction
hsp = lerp(hsp,0,0.98);
Another suggestion is sometimes the cause of getting stuck in walls is due to image_xscale flipping, because the origin might be off by a pixel, or more. Instead of flipping the actual sprite you just monitor it via a variable and draw a flipped version of it. Create a "facing" variable and apply the flip code to that instead. Then in draw use draw_sprite_ext and use your facing variable in place of image_xscale.
Thanks for the reply.

I have removed the "Apply gravity" section from all of my states, and I have added the normal gravity code instead (vsp += grav).

I have also replaced my friction code with yours, (hsp = lerp(hsp, 0, 0.98) and I have applied this to all states.

However, these changes seem to make things worse:
  • The enemy is now knocked fully into the air but only a few pixels horizontally.
  • While in the air, the enemy gets knocked back multiple times; even when I only hit him once.
  • When I hit the enemy against a wall, it has the same problem as with my previous code
I considered your other suggestion; however, I am using a 32x32 test sprite for collision whose origin is exactly centered so I do not think that could be the cause of the problem.
 

Slyddar

Member
  • The enemy is now knocked fully into the air but only a few pixels horizontally.
  • While in the air, the enemy gets knocked back multiple times; even when I only hit him once.
  • When I hit the enemy against a wall, it has the same problem as with my previous code
Just entered your code in a project to test it out and in relation to these three points you made.
  • In the hitbox collision, change the hsp knockback value of 4 to something like 15 to give it more effect. I actually had the lerp value way too high. I moved the apply gravity and lerp into the top of the scr_collision script as below so it gets applied to all scripts.
//Apply Gravity
vsp += grav;
//Friction
hsp = lerp(hsp,0,0.12);

// Horizontal collision
if (place_meeting(x+hsp,y,obj_solid)) {
while (!place_meeting(x+sign(hsp),y,obj_solid)) {
x += sign(hsp);
}
hsp = 0;
}

x += hsp;

// Vertical Collision
if (place_meeting(x,y+vsp,obj_solid)) {
while (!place_meeting(x,y+sign(vsp),obj_solid)) {
y += sign(vsp);
}
vsp = 0;
}

y += vsp;
  • The enemy is getting hit multiple times because he is leaving the damaged state due to the hsp and vsp equalling zero when he is at the peak of being in the air. You should do another check. I've just made it so when they are back on the ground and stopped. Also cause lerp makes them slide they take a while to get to 0, so check for close to 0 as below.
Code:
// Change back to idle state
if (abs(hsp) <= 0.1 && abs(vsp) <= 0.1) and place_meeting(x,y+1,obj_solid) {
    image_blend = c_white;
    state = scr_enemy_idle;
}
  • Mine works perfectly when against a wall. We probably need to see how the enemy is moving, as something else is effecting them getting stuck, because using what code you've given it's working as you can see from the pic. Only difference in my example was my gravity was 0.25 instead of 0.2.
Also if you are using hsp to move, it will effect how the knockback is done, as the hsp will get reset every step. Another way to do knockback is to have another variable called hsp_carry which contains addditional movement influences on the object. You can use that for push/pull effects, but remember to include it in the collision detecting as well. The difference to hsp is hsp_carry doesn't get zero'd every step, unless it collides with a wall.
 

Attachments

Last edited:
Top