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

GML Knockback in plarform game

TOMXT

Member
Hi, noob here
I have a problem with programming knocback in my platform game. I tryied everything and it still not working well. My goal is easiest smooth knocback when collide with enemy depend on direction of enemy object. Any idea how to do that?
Heres my "movement core", maybe it would help
GML:
key_left = keyboard_check(ord("A"));
key_right = keyboard_check(ord("D"));
key_jump = keyboard_check_pressed(ord("W"));
key_climb = keyboard_check(ord("W"));

var move = key_right - key_left;
hsp = (move * walksp)

vsp =vsp + grv


// jump
if(place_meeting(x,y+1,o_Wall)) and (key_jump)
{
    vsp = -7;
   
   
}
//climb
if(place_meeting(x,y,o_Lina)) and (key_climb)
{
    grv = 0;
    vsp = -4;
   
   
}
grv = 0.3;

//horizontal
if(place_meeting(x+hsp,y, o_Wall))
{
   
    while(!place_meeting(x+sign(hsp), y, o_Wall))
    {
       
        x = x + sign(hsp);
    }
    hsp = 0;
}

x = x +hsp;

// vertical
if(place_meeting(x,y+vsp, o_Wall))
{
   
    while(!place_meeting(x, y+sign(vsp), o_Wall))
    {
       
        y = y + sign(vsp);
    }
    vsp = 0;
}

y = y +vsp;
 
Last edited:

TheouAegis

Member
First off, you don't want the player able to move at will when knocked back, do you? So logically, you will want your movement control code wrapped up inside some sort of condition that prevents it from running when knocked back.

Second, you will probably want an invincibility time that lasts during the knockback duration and a little bit after (games that don't have this are typically crap). So you will want to set up an alarm or make your own timer, then make sure the player takes no damage while that alarm or timer is counting down.

Setting the direction of knockback is the easy part, kind of. At its core, it is hspeed = sign(enemy.xprevious - player.xprevious), or sign(enemy.x-enemy.hspeed - player.x+player.hspeed). Many games use (enemy.x - player.x), but this can have some ugly effects.
 

Slyddar

Member
Knockback is achieved the easiest, imho, using state machines. You have a knockback state, which does not allow player movement, so the player is set to that state, and knocked back in the direction you set, and after a short time, the state is changed to give them control again. The reason it's easy that way, is you stop them giving input to hsp, which is what is countering any knockback you are trying to apply.

In saying that, there are alternatives, but they are not the best option. You could set a variable to the time you want to be knocked back when they get hit, say knockback = 60, and change your capturing of input to not allow input when knocked back. For example :
Code:
if knockback > 0 {
  knockback--;
  //zero any input
  key_jump = 0;
  key_climb = 0;
} else {
  var move = key_right - key_left;
  hsp = (move * walksp)
}
You would need a friction or drag variable on hsp somewhere too, in order to slow it down over time.
 

Simon Gust

Member
It's not going to be so straight forward, but here are the basics.

to get the knockback values you have to find the angle from the enemy to the player.
I like to use center values for positions of the two objects.

say you have a collision event with obj enemy
Code:
var enemy_center_x = other.x + (other.bbox_right - other.bbox_left) / 2;
var enemy_center_y = other.y + (other.bbox_bottom - other.bbox_top) / 2;

var player_center_x = x +  (bbox_right - bbox_left) / 2;
var player_center_y = y +  (bbox_bottom - bbox_top) / 2;
with two points you can calculate the angle from one to the other using point_direction().
Code:
var dir = point_direction(enemy_center_x, enemy_center_y, player_center_x, player_center_y);
Then to make it work with seperate directional values (hsp, vsp) you have to split the direction up into x and y vectors using lengthdir_().
Code:
var kb_force = 5.00;
var h_impact = lengthdir_x(kb_force, dir);
var v_impact = lengthdir_y(kb_force, dir);
you can apply these forces to hsp and vsp, but you will be met with a problem.
hsp and vsp will be overwritten the very next frame by your movement code. There are two ways to get around that

1. Making a state machine where knockback is it's own state

2. Writing your movement code so it only ever applies to hsp and vsp instead of overwriting them.

You will have to implement some kind of invincibility timer to prevent enemies from hurting you multiple times a frame (and apply knockback).
 

TOMXT

Member

@Simon Gust
i changed my project from platform game to top down shooter, now i use state machine but i still have a problem with knockback, timer is working, collision is working but as u can see that event is moving player in weird directions.
This is my knockback script
GML:
function PlayerStateKnockback(){
PlayerCollision();

counterknockback = 0;
var enemy_center_x = o_enemyparent.x + (o_enemyparent.bbox_right - o_enemyparent.bbox_left) /  2;
var enemy_center_y = o_enemyparent.y + (o_enemyparent.bbox_bottom - o_enemyparent.bbox_top) / 2;

var player_center_x = x +  (bbox_right - bbox_left) / 2;
var player_center_y = y +  (bbox_bottom - bbox_top) / 2;

var dir = point_direction(enemy_center_x, enemy_center_y, player_center_x, player_center_y);
var kb_force = 5.00;
other.direction = other.image_angle;
hsp = lengthdir_x(kb_force, dir);
vsp = lengthdir_y(kb_force, dir);

KnockbackDistanceRemaining = max(0, KnockbackDistanceRemaining - 5);


 if(KnockbackDistanceRemaining <= 0)
{
    state = PlayerStateFree;
}

}
Have u maybe any idea what is wrong?
 

Simon Gust

Member
I would structure the code differently.

This knockback code is something that should only happen one frame, the same frame the player is hit.
If you have code for the player getting hit, put it there. You will need to exactly know which enemy hits the player instead of using o_enemyparent. It doesn't point to the correct instance 99% of the time.
The knockback state is only a timer where the player doesn't react to inputs.
 

TheouAegis

Member
var enemy_center_x = o_enemyparent.x + (o_enemyparent.bbox_right - o_enemyparent.bbox_left) / 2; var enemy_center_y = o_enemyparent.y + (o_enemyparent.bbox_bottom - o_enemyparent.bbox_top) / 2;

var player_center_x = x + (bbox_right - bbox_left) / 2;
var player_center_y = y + (bbox_bottom - bbox_top) / 2;
First off, you can simplify these to
(bbox_right+bbox_left)/2;
(bbox_top+bbox_bottom)/2;


o_enemyparent.x
Do you know what is wrong with this? You prefixed an object index to a variable being read. This ain't always going to be the object you collide with. The odds of it being the enemy you collide with get worse and worse the more enemies you add to the room. You must use the id of the enemy you collide with.
 
Top