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

Step Events: Handle input in the Step and Movement + Collisions in the End Step?

Bentley

Member
I've been looking at a platformer engine. The engine gets the player's inputs in the Step and then applies changes to the player's x and y in the End Step:
Code:
STEP

h = (key_right - key_left) * move_spd;

END STEP

repeat (abs(move_spd))
{
    if (!place_meeting(x + sign(h), y, obj_wall)) x += sign(h);
    else break;
}
Can anyone explain the logic behind using the Step Events this way? I had thought that by the time the End Step runs, all instances already moved to their new x/y positions. So why make changes to x/y at that point? Wouldn't it make sense to do them before the End Step? Explanation about the Steps, how GM handles them, and why the above is coded in that order would be helpful. Thank you.
 
Last edited:
So, it's a bit "extreme" to put it in the end step. You just want to do one then the other: read input, then move.

If you are using the built in motion variables: speed, direction, hspeed, vspeed, gravity, etc... then yes, between step and end step these values update, but since you aren't using them GM will not automatically move your instance.
 

Simon Gust

Member
I've been looking at a platformer engine. The engine gets the player's inputs in the Step and then applies changes to the player's x and y in the End Step:
Code:
STEP

h = (key_right - key_left) * move_spd;

END STEP

repeat (abs(move_spd))
{
    if (!place_meeting(x + sign(h), y, obj_wall)) x += sign(h);
    else break;
}
Can anyone explain the logic behind using the Step Events this way? I had thought that by the time the End Step runs, all instances already moved to there new x/y positions. So why make changes to x/y at that point? Wouldn't it make sense to do them before the End Step? Thanks for reading. Explanation about the Steps, how GM handles them, and why the above is coded in that order would be helpful. Thanks.
I would go with
1. input
2. motion (walking, jumping, gravity, attacking)
3. collision (including updating x and y)
4. animation
 

Bentley

Member
So, it's a bit "extreme" to put it in the end step. You just want to do one then the other: read input, then move.

If you are using the built in motion variables: speed, direction, hspeed, vspeed, gravity, etc... then yes, between step and end step these values update, but since you aren't using them GM will not automatically move your instance.
So you're saying that if you change X/Y manually (without the built-in variables), the changes occur immediately? For example:

Begin Step: if (keyboard_check(vk_right)) x += 5 //My x increases by 5 immediately?
Step: if (keyboard_check(vk_right)) x += 5; //My x increases by another 5 immediately?
 
Last edited:

Bentley

Member
I would go with
1. input
2. motion (walking, jumping, gravity, attacking)
3. collision (including updating x and y)
4. animation
Do you mean something like this?
Code:
//Input

h_input = keyboard_check(vk_right) - keyboard_check(vk_left);

//Motion

if (!place_meeting(x + (h_input * 5), y, obj_wall)) x += h_input * 5;

//Collision

else
{
    repeat (5)
    {
        if (!place_meeting(x + sign(h_input), y, obj_wall)) x += sign(h_input);
        else break;
    }
}

//Animation

if (h_input > 0) sprite_index = spr_walk_right;
Would this all be in the Step? Also, if you don't mind, can you give me an example of how you would code it?
 
So you're saying that if you change X/Y manually (without the built-in variables), the changes occur immediately?
Yup. If you ever directly change/set your x and y value, your position changes IMMEDIATELY. So any future checks against that value, like collision checks using the x and y position will use the current value of x and y; it doesn't take any time for those values to "update" when you set them.

Now you wont "see" those changes until you get to the draw event and the draw event is ran, obviously, but the change is being made.
 

Bentley

Member
Yup. If you ever directly change/set your x and y value, your position changes IMMEDIATELY. So any future checks against that value, like collision checks using the x and y position will use the current value of x and y; it doesn't take any time for those values to "update" when you set them.

Now you wont "see" those changes until you get to the draw event and the draw event is ran, obviously, but the change is being made.
This is just the explanation I was looking for. I was always confused because I would read: "By the time the End Step runs, all instances have moved to their new positions." I had no idea that it was only referring to changes in position caused by the built-in variables. Thanks a bunch Pixelated_Pope.
 

Simon Gust

Member
Do you mean something like this?
Code:
//Input

h_input = keyboard_check(vk_right) - keyboard_check(vk_left);

//Motion

if (!place_meeting(x + (h_input * 5), y, obj_wall)) x += h_input * 5;

//Collision

else
{
    repeat (5)
    {
        if (!place_meeting(x + sign(h_input), y, obj_wall)) x += sign(h_input);
        else break;
    }
}

//Animation

if (h_input > 0) sprite_index = spr_walk_right;
Would this all be in the Step? Also, if you don't mind, can you give me an example of how you would code it?
That's pretty good. Usually though platformer engines aren't so direct.
my engine (can't be implemented like that)
Code:
/// INPUT
key_lft = -keyboard_check(ord("A"));
key_rgt = +keyboard_check(ord("D"));
move = key_lft + key_rgt;

key_upw = -keyboard_check(ord("W"));
key_dwn = +keyboard_check(ord("S"));
tilt = key_upw + key_dwn;

key_jump = keyboard_check_pressed(vk_space);
key_jump_held = keyboard_check(vk_space);
use_item = mouse_check_button(mb_left);
key_walk = keyboard_check(vk_shift);

/// MOTION
//GRAVITATION/////////////////////////////////////////////////////////////////////////////
if (liquidborn)
{
    //GRAVITY
    if (vspd < 4) 
        vspd += 0.10;
    else vspd -= 0.10;
   
    //FALLDAMAGE
    fall_damage = 0;
}
else
{
    //GRAVITY IN LIQUIDS
    if (vspd < 20)            
        vspd += 0.40;
}

//DEATH///////////////////////////////////////////////////////////////////////////////////
if (state == STATES.DEAD)
{
    if (abs(hspd) < 0.1)
        hspd = 0;
    else if (earthborn) 
        hspd *= 0.80;
       
    //TERMINATE
    exit;
}

//JUMPING////////////////////////////////////////////////////////////////////////////////
if (key_jump && earthborn)
    vspd -= jump_height;
if (vspd < 0 && !key_jump_held) 
    vspd = max(vspd, -jump_height / 2.50)

//ACCELERATE/////////////////////////////////////////////////////////////////////////////
if (move != 0 && state == STATES.CHILL)
{      
    //ROUND SPEED
    if (abs(hspd) < 0.10) 
        hspd = 0;
       
    //FACING
    image_xscale = move; 
}

//MOTION PLANING/////////////////////////////////////////////////////////////////////////
if (earthborn)
{
    if (tilt)
    {
        //HARD CAP MAX SPEED
        move_spd = 0;
        if (abs(hspd) > move_spd || move == 0)
            hspd *= 0.90;
    }
    else
    {
        //HARD CAP MAX SPEED
        move_spd = ground_spd;
        if (abs(hspd) > move_spd || move == 0)
            hspd *= 0.80;
       
        //GAIN MOTION
        if (abs(hspd) < move_spd || sign(hspd) != move)
            hspd += move * move_spd / 12.00 ;
    }    
}
else
{    
    //HARD CAP MAX SPEED
    move_spd = air_spd;
    if (abs(hspd) > move_spd || move == 0)
        hspd *= 0.95;
   
    //GAIN MOTION
    if (abs(hspd) < move_spd || sign(hspd) != move)
        hspd += move * move_spd / 12.00;
}


if (liquidborn)
    move_spd = liquid_spd;

//OTHER/////////////////////////////////////////////////////////////////////////////////
if (key_walk)
{
    ground_spd = ground_walk_spd;
    air_spd = air_walk_spd;
    liquid_spd = liquid_walk_spd;
}
else
{
    ground_spd = ground_run_spd;
    air_spd = air_run_spd;
    liquid_spd = liquid_run_spd;
}  

//FALLDAMAGE////////////////////////////////////////////////////////////////////////////
if (!earthborn)
{
    if (vspd >= 20)
        fall_damage += 10;
}
else
{
    if (fall_damage >= 25)
    {
        //DO FALLDAMAGE
        if (invincible == false)
        {          
            hp -= min(fall_damage, hp);
        }
       
        //TERMINATE
        fall_damage = 0;
    }
}

/// COLLISION
//HORIZONTAL COLLISION
if (grid_place_meeting(x+(hspd),y))
{
    while (!grid_place_meeting(x+sign(hspd),y))
    {
        x += sign(hspd);
    }
    hspd = 0;
}
x += hspd;

//VERTICAL COLLISION
if (grid_place_meeting(x,y+(vspd)))
{
    while (!grid_place_meeting(x,y+sign(vspd)))
    {
        y += sign(vspd);
    }
    vspd = 0;
}
y += vspd;

earthborn = (
getType(global.TILE[@ bbox_left    div 16, (bbox_bottom+1) div 16]) > 0 ||
getType(global.TILE[@ x            div 16, (bbox_bottom+1) div 16]) > 0 ||
getType(global.TILE[@ bbox_right   div 16, (bbox_bottom+1) div 16]) > 0);

/// ANIMATION
if (tilt) mask_index = spr_player_cbox;
else mask_index = spr_player_bbox;

switch (state)
{
    case STATES.CHILL:
    {
        if (earthborn)
        {
            var spd = abs(hspd);
            if (tilt) 
            {
                image_speed = 0;
                image_index = 0; 
                sprite_index = spr_player_crouch;                
            }
            else 
            {
                if (spd > 1.00)
                {
                    if (ground_spd == ground_walk_spd)
                    {
                        sprite_index = spr_player_move;
                        image_speed = spd / 12;
                    }                  
                    else 
                    {
                        sprite_index = spr_player_run;
                        image_speed = spd / 16;
                    }
                }
                else 
                {
                    sprite_index = spr_player_chill;
                    image_speed = 0;
                    image_index = 0;
                }
            }
        }
        else
        {
            sprite_index = spr_player_jump;
            image_speed = 0;
            image_index = 0;
        }
    }
    break;
}

/// PLAYER UPDATE
if (hp <= 0)
{
    state = STATES.DEAD;
}
else
{
    if (tick mod 60 == 0)
        hp += min(hpr, max_hp - hp);
}
Just as a refrence on how complex an engine can be.
 

TheouAegis

Member
Personally I feel the Begin Step should get the input, the Step should handle the movement, and the End Step should handle the post-movement/collisions. It makes moving platforms easier to handle, in my mind.

And as Simon showed, you should only check inputs one time and save the results to variables and then use those variables. Your code will run faster as a result.
 

Bentley

Member
Personally I feel the Begin Step should get the input, the Step should handle the movement, and the End Step should handle the post-movement/collisions. It makes moving platforms easier to handle, in my mind.

And as Simon showed, you should only check inputs one time and save the results to variables and then use those variables. Your code will run faster as a result.
I like that. Something like this?
Code:
BEGIN STEP

h = (key_right - key_left) * move_spd;

STEP

repeat (h)
{
    if (!place_meeting(x + sign(h), y, obj_wall)) x += sign(h);
    else break;
}
If I'm correct so far, what should go in the End Step? Thanks for the reply btw.
 

TheouAegis

Member
if you don't need anything in the End Step, don't put it in. anything that you would need to happen after every single instance in the room has moved would be done there.
 
Top