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

pokemon-style grid movement

Been searching via google and looked at a few youtube videos, but I can't find a good tutorial on how to do poke'mon-like movement. From most of the tutorial codes I've seen most suggest using a timer variable and a grid variable, but that didn't work well for me, so I took the code out. Was hoping somewhere here has an simpler solution.

Here is my code: (note my object is alligned to a 32x32 grid).

create event
Code:
movespeed = 4; // how fast player will move...?
moving = false; // Whether the player is moving or not

// movement keys
up_key = "W";
down_key = 'S';
left_key = 'A';
right_key = 'D';

// Other keys
confirm_key = 'N' // equal to X or A on a controller
back_key = 'K'; // equal to O or B on a controller

// Other important variables
can_move = true;
Step event
Code:
// Check if player can move first 
if can_move == true
{
    var can_move_vert = true, can_move_horiz = true; // prevent moving vertically and horizontally at the same time
   
    if can_move_vert == true
    if keyboard_check(ord(up_key)) //&& !keyboard_check(ord(left_key)) && !keyboard_check(ord(right_key))
    {
        can_move_horiz = false;
        moving = true;
        y -= movespeed;
    }
   
    if can_move_vert == true
    if keyboard_check(ord(down_key)) //&& !keyboard_check(ord(left_key)) && !keyboard_check(ord(right_key))
    {
        can_move_horiz = false;
        moving = true;
         y += movespeed
    }
   
    if can_move_horiz == true
    if keyboard_check(ord(left_key)) //&& !keyboard_check(ord(down_key)) && !keyboard_check(ord(up_key))
    {
        can_move_vert = false;
        moving = true;
        x -= movespeed;
    }
   
    if can_move_horiz == true
    if keyboard_check(ord(right_key)) //&& !keyboard_check(ord(down_key)) && !keyboard_check(ord(up_key))
    {
        can_move_vert = false;
        moving = true;
        x += movespeed;
    }
   

    // Change the state of moving variable to false when the player isn't moving
    if moving == true
    if keyboard_check_released(ord(left_key)) || keyboard_check_released(ord(right_key)) || keyboard_check_released(ord(up_key)) || keyboard_check_released(ord(down_key))
         { moving = false }
   
    // Reset the ability to move horiziontally or vertically
    if keyboard_check_released(ord(left_key)) || keyboard_check_released(ord(right_key)) && can_move_vert == false
    { can_move_vert = true; }
   
    if keyboard_check_released(ord(up_key)) || keyboard_check_released(ord(down_key)) && can_move_horiz == false
    { can_move_horiz = true; }
   
    // Prevent movement if all keys are held at the same time
    if keyboard_check(ord(right_key)) && keyboard_check(ord(left_key)) && keyboard_check(ord(up_key)) && keyboard_check(ord(down_key))
    {
        can_move_horiz = false
        can_move_vert = false
    }
   
}
 
T

TDSrock

Guest
Dang son this is a TON of complexity right here...

I would just simply this a whole lot, especially if diagonal is not a requirement.

Here are some tips.

While moving, Check how far away the character is from the desired location. If the desired location is within the velocity, just put them at the location and wait for the next input.
When handling the input you could just add the values toghter like so:
Code:
horizontal_input = left_button - right_button;
horizontal input will now be -1(for right input), 0 (for no input or both) or 1(for left input;
This makes handleing the direction easier as all you now have to do is:
Code:
hsp = horizontal_input * movement_speed;

//Dedicate movement
x += hsp;
y += vsp;
As you can see I split up my speed values into two values, hsp and vsp. This is mainly so I have more control and can do some check with them before applying them to my object.
 
Okay, so I took your advice thus far, but now I can't move my character at all. Here are the changes to the code:

create event
Code:
movement_speed = 4; // how fast player will move...?
moving = false; // Whether the player is moving or not

// movement keys
up_key = "W";
down_key = 'S';
left_key = 'A';
right_key = 'D';

// Variables for checking whether the arrow buttons were pressed
horizontal_input = 0; // 0 - for no input(or both), 1 = right, -1 for left
vertical_input = 0;// 0 for no input (or both), 1 = down, -1 for up
hsp = 0; // horiziontal speed
vsp = 0; // vertical speed

// Other keys
confirm_key = 'N' // equal to X or A on a controller
back_key = 'K';

// Other important variables
can_move = true;
step event
Code:
if moving == false { hsp = 0; vsp = 0; } // Reset variables if your not moving

// Check if player can move first
if can_move == true
{
    // Check to see whether the player has pressed a directional button
    if keyboard_check(ord(up_key)) { vertical_input = -1; }
    if keyboard_check(ord(down_key)) { vertical_input = 1; }
    if keyboard_check(ord(left_key)) { horizontal_input = -1; }
    if keyboard_check(ord(right_key)) { horizontal_input = 1; }
 
    if keyboard_check_released(ord(up_key)) || keyboard_check_released(ord(down_key))
    { vertical_input = 0; moving = false; }
 
    if keyboard_check_released(ord(left_key)) || keyboard_check_released(ord(right_key))
    { horizontal_input = 0;  moving = false; }
 
 
//
    if keyboard_check(ord(up_key)) && horizontal_input == 0
    {
        moving = true;
    }
 
    if keyboard_check(ord(down_key)) && horizontal_input == 0
    {
        moving = true;
    }
 
 
    if keyboard_check(ord(left_key)) && vertical_input == 0
    {
        moving = true;
    }
 
    if keyboard_check(ord(right_key)) && vertical_input == 0
    {
        moving = true;
    }
 
    // Move the player
    if moving == true
    {
        if horizontal_input != 0 { hsp = horizontal_input * movement_speed; } // If left or right button pressed, move the player horizontally
        if vertical_input != 0 { vsp = vertical_input * movement_speed; } // If up or down button pressed, move the player vertically
    }

 
 
}
Also, you said,
While moving, Check how far away the character is from the desired location. If the desired location is within the velocity, just put them at the location and wait for the next input.
Not sure how to do that... Would I do something like this?:
Code:
var current_x = x;
if ( current_x < current_x + horizontal_input * movement_speed )
{
// place movement code here
}

EDIT: I forgot to add this code and that is why I couldn't move my character:
Code:
x += hsp;
y += vsp;
However, I'm able to move diagonally, which is not what I want. I would think these lines like these would prevent that:

if keyboard_check(ord(right_key)) && vertical_input == 0
if keyboard_check(ord(down_key)) && horizontal_input == 0
 
Last edited:
T

tserek

Guest
This should do, not allowing to move diagonally. Keyboard press returns 1 or 0, true or false, press or not press. Sum of direction keys pressed can be 1, 0 or -1 (because the -key is set). If hsp or vsp is not 0, we se the opposite value to 0 and start move. When the both x and y modulo 32 returns 0 we set move to false. Just be sure the spd is divisible with 32, otherwise the player may keep moving forever.

create:
Code:
move=false;
step:
Code:
spd = 4;

if !move
{
 hsp = keyboard_check(vk_right) + (-keyboard_check(vk_left));
 vsp = keyboard_check(vk_down) + (-keyboard_check(vk_up));

 if hsp <> 0 && place_free(x+hsp,y) {move=true;vsp=0;}
 if vsp <> 0 && place_free(x,y+vsp) {move=true;hsp=0;}
}

if move
{
 x += spd *hsp;
 y += spd *vsp;
 
 if x mod 32=0
 && y mod 32=0
 {
  move=false;
 }
}
 
Last edited by a moderator:
This should do, not allowing to move diagonally. Keyboard press returns 1 or 0, true or false, press or not press. Sum of direction keys pressed can be 1, 0 or -1 (because the -key is set). If hsp or vsp is not 0, we se the opposite value to 0 and start move. When the both x and y modulo 32 returns 0 we set move to false. Just be sure the spd is divisible with 32 and it should work.

create:
Code:
move=false;
step:
Code:
spd = 4;

if !move
{
 hsp = keyboard_check(vk_right) + (-keyboard_check(vk_left));
 vsp = keyboard_check(vk_down) + (-keyboard_check(vk_up));

 if hsp <> 0 && place_free(x+hsp,y) {move=true;vsp=0;}
 if vsp <> 0 && place_free(x,y+vsp) {move=true;hsp=0;}
}

if move
{
 x += spd *hsp;
 y += spd *vsp;
 
 if x mod 32=0
 && y mod 32=0
 {
  move=false;
 }
}
That seemed to work, thanks. One question, mod function is for dividing, right? Never used the function, so I just want to be sure I know what I'm using.
 
Turns out after doing more testing, I can move around fine, but after moving around a lot if I hold the down key I move up for some reason and can't stop. I haven't changed anything, but the name of a few variables in the code tserek provided.
 
T

tserek

Guest
Modulo is a remaineder, or a "left over". For example 10 mod 4=2 (4+4=8 and 10-8=2).
 
Last edited by a moderator:
Modulo is a remaineder, or a "left over". For example 10 mod 4=2 (4+4=8 and 10-8=2).
Okay, I understand. Was wondering what the difference was. Also, now my character won't stop moving up when I press the down key. This occurs randomly after I have moved around a lot.
 
Create event (note I didn't get rid of any of my old variables yet, just in case I need to revert back to the other code)
Code:
movement_speed = 4; // how fast player will move...?
moving = false; // Whether the player is moving or not


// movement keys
up_key = "W";
down_key = 'S';
left_key = 'A';
right_key = 'D';

// Variables for checking whether the arrow buttons were pressed
horizontal_input = 0; // 0 - for no input(or both), 1 = right, -1 for left
vertical_input = 0;// 0 for no input (or both), 1 = down, -1 for up
hsp = 0; // horiziontal speed
vsp = 0; // vertical speed

// Other keys
confirm_key = 'N' // equal to X or A on a controller
back_key = 'K';

// Other important variables
can_move = true;

move_player = false; // Tell the game to move the player
Step event
Code:
if move_player == false
{
 hsp = keyboard_check(ord(right_key)) + (-keyboard_check(ord(left_key)));
 vsp = keyboard_check(ord(down_key)) + (-keyboard_check(ord(up_key)));

 if hsp <> 0 && place_free( x + hsp, y ) { move_player = true; vsp = 0; }
 if vsp <> 0 && place_free(x, y + vsp ) { move_player = true; hsp = 0; }
}

if move_player == true
{
 moving = true; // change the state of the moving variable to true
 x += movement_speed * hsp;
 y += movement_speed * vsp;


 // mod returns remainder
 if x mod 32 == 0 && y mod 32 == 0 // All this does is check whether current x / 32 = 0 and current y / 32 = 0 and if so, "tells" the game to stop moving the player
 {
    move_player = false; // stop moving player
    moving = false; // change variable state
 }

}
 
T

TDSrock

Guest
I suspect it's something similar to floating point errors.

Again I would advice to simply check in the step event how far away you are from the goal cordinates. Much simpler, but a bit less efficient. But It doesn't require your movementspeed to be mod == 0 as your step distance.
Code:
if(goal_x >= x + hsp){
   goal_reached = true;
   x = goal_x
   hsp = 0;
   moving = false;
}
Note I am really bad at '>' and '<' so I may have put the wrong one down.
 
Last edited by a moderator:

Freddy Jones

Your Main Detective
This should work -- didn't test it. But should work. Took it as a challenge to make something fairly simple with your problem, hopefully it helped.

Code:
/// create event
// where you are on the grid
gridX = 0;
gridY = 0;
// where you're moving to
toX = gridX;
toY = gridY;
// grid scale related variables
slide = 1/32; // how smooth you want the transition to be from points
gridScale = 32;
// Remove the need to always call "ord" in the step event
up_key = ord('W');
down_key = ord('S');
left_key = ord('A');
right_key = ord('D');
Code:
/// step event
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( gridX - toX, -slide, slide );
    gridY += clamp( gridY - toY, -slide, slide );
    x = gridX * gridScale;
    y = gridY * gridScale;
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));

    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
Also, refrain from the non-standard operator duo known as <>. The conventional and actually documented operator duo you should be writing in this case is !=. The use of <> is simply a novelty and feature in GM:S used to support older versions.
 
Last edited:
This should work -- didn't test it. But should work. Took it as a challenge to make something fairly simple with your problem, hopefully it helped.

Code:
/// create event
// where you are on the grid
gridX = 0;
gridY = 0;
// where you're moving to
toX = gridX;
toY = gridY;
// grid scale related variables
slide = 1/32; // how smooth you want the transition to be from points
gridScale = 32;
// Remove the need to always call "ord" in the step event
up_key = ord('W');
down_key = ord('S');
left_key = ord('A');
right_key = ord('D');
Code:
/// step event
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( gridX - toX, -slide, slide );
    gridY += clamp( gridY - toY, -slide, slide );
    x = gridX * gridScale;
    y = gridY * gridScale;
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));

    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
Also, refrain from the non-standard operator duo known as <>. The conventional and actually documented operator duo you should be writing in this case is !=. The use of <> is simply a novelty and feature in GM:S used to support older versions.
Your code didn't work. As soon as I pressed a button my character went flying off screen.

I suspect it's something similar to floating point errors.

Again I would advice to simply check in the step event how far away you are from the goal cordinates. Much simpler, but a bit less efficient. But It doesn't require your movementspeed to be mod == 0 as your step distance.
Code:
if(goal_x >= x + hsp){
goal_reached = true;
x = goal_x
hsp = 0;
moving = false;
}
Note I am really bad at '>' and '<' so I may have put the wrong one down.
Tried adding that in the code... Maybe it is the way I wrote it in, but I can only move using S and D now.

Code:
if move_player == false
{
    goal_x = movement_speed * hsp + x;
    goal_y = movement_speed * vsp + y;

 hsp = keyboard_check(ord(right_key)) + (-keyboard_check(ord(left_key)));
 vsp = keyboard_check(ord(down_key)) + (-keyboard_check(ord(up_key)));

 if hsp < 0 || hsp > 0 && place_free( x + hsp, y ) { move_player = true; vsp = 0; }
 if vsp < 0 || vsp > 0 && place_free(x, y + vsp ) { move_player = true; hsp = 0; }
}

if move_player == true
{
 moving = true; // change the state of the moving variable to true
 x += movement_speed * hsp;
 y += movement_speed * vsp;


if(goal_x >= x + hsp){
   x = goal_x
   hsp = 0;
   move_player = false;
   moving = false;
}

if(goal_y >= y + vsp){

   y = goal_y
   vsp = 0;
   move_player = false;
   moving = false;
}



}
 
T

TDSrock

Guest
Goal_x and goal_y should be set as the cordinate that you are moving towards. So you MUST know the direction the user wants to move in first.

Say they want to move right and they are currently at x = 20, along with each tile is 10 apart from eachoter. This means that goal_x = x + 10; therefor 30.
 

Freddy Jones

Your Main Detective
Your code didn't work. As soon as I pressed a button my character went flying off screen.
Easy fix, I never tested it, but the problem was with the subraction in the clamp. It's never delta 1 - delta 2, it's delta 2 - delta 1.

Here's a fixed and smoother version.


Code:
/// create event
// grid scale related variables
slide = 1/4; // how smooth you want the transition to be from points
gridScale = 32;


// where you are on the grid
gridX = round( x/gridScale );
gridY = round( y/gridScale );
// where you're moving to
toX = gridX;
toY = gridY;
// where you are
x = gridX * gridScale;
y = gridY * gridScale;

// Remove the need to always call "ord" in the step event
up_key = ord('W');
down_key = ord('S');
left_key = ord('A');
right_key = ord('D');
Code:
/// step event
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( toX - gridX, -slide, slide );
    gridY += clamp( toY - gridY, -slide, slide );
    x = gridX * gridScale;
    y = gridY * gridScale;
    // smooth out our movement...
    if( gridX == toX && gridY == toY ){
        event_perform(ev_step, 0);
    }
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));

    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
 
Easy fix, I never tested it, but the problem was with the subraction in the clamp. It's never delta 1 - delta 2, it's delta 2 - delta 1.

Here's a fixed and smoother version.


Code:
/// create event
// grid scale related variables
slide = 1/4; // how smooth you want the transition to be from points
gridScale = 32;


// where you are on the grid
gridX = round( x/gridScale );
gridY = round( y/gridScale );
// where you're moving to
toX = gridX;
toY = gridY;
// where you are
x = gridX * gridScale;
y = gridY * gridScale;

// Remove the need to always call "ord" in the step event
up_key = ord('W');
down_key = ord('S');
left_key = ord('A');
right_key = ord('D');
Code:
/// step event
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( toX - gridX, -slide, slide );
    gridY += clamp( toY - gridY, -slide, slide );
    x = gridX * gridScale;
    y = gridY * gridScale;
    // smooth out our movement...
    if( gridX == toX && gridY == toY ){
        event_perform(ev_step, 0);
    }
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));

    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
Been moving around for the past 10 minutes or so in my game and it seems like it works! Finally! Only thing now, this code does have some stuff in it I am unfamiliar with that I'll have to look up. Anyway, Thanks, Fred & you too TDSrock for taking the time out to help me.
 

Freddy Jones

Your Main Detective
Been moving around for the past 10 minutes or so in my game and it seems like it works! Finally! Only thing now, this code does have some stuff in it I am unfamiliar with that I'll have to look up. Anyway, Thanks, Fred & you too TDSrock for taking the time out to help me.
Glad I could help. There's only one thing about my approach that is a really bad idea. It will probably come back to haunt you, so I'll go ahead and refactor it for you in a way that is more appropriate.

Code:
///grid_move()
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( toX - gridX, -slide, slide );
    gridY += clamp( toY - gridY, -slide, slide );
    
    x = gridX * gridScale;
    y = gridY * gridScale;
    // smooth out our movement...
    if( gridX == toX && gridY == toY ){ grid_move(); }
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));
    
    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
Simply put it into a script, and place the script in your step event. Just do grid_move();
 
J

Jed

Guest
Glad I could help. There's only one thing about my approach that is a really bad idea. It will probably come back to haunt you, so I'll go ahead and refactor it for you in a way that is more appropriate.

Code:
///grid_move()
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( toX - gridX, -slide, slide );
    gridY += clamp( toY - gridY, -slide, slide );
   
    x = gridX * gridScale;
    y = gridY * gridScale;
    // smooth out our movement...
    if( gridX == toX && gridY == toY ){ grid_move(); }
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));
   
    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
Simply put it into a script, and place the script in your step event. Just do grid_move();
I'm trying to implement a "steps" counter with this code, so that "steps" increments 1 each time we arrive at the destination, but I'm having issues with it adding a step when I first begin pressing the button and then when it arrives at its destination (2) instead of just 1 for arriving at the destination. Do you have any ideas on how to implement this?
 

Freddy Jones

Your Main Detective
@Jed

You'd probably want to supply the code you're using to do it. Anyhow, I wrote this several years ago and I don't even use GML anymore. Though, this is pretty simple looking.


I imagine you're probably just placing your step counter somewhere and hoping for the best . . . but what you need to do is make the counter increment when you meet the destination. The only way to guarantee you're stepping on arrival is to add it inside the function right after a movement happens . . .


Code:
///grid_move()
// slide our character to the designated point if we're not on it
if( gridX != toX || gridY != toY ){
    gridX += clamp( toX - gridX, -slide, slide );
    gridY += clamp( toY - gridY, -slide, slide );
    
    x = gridX * gridScale;
    y = gridY * gridScale;
    // smooth out our movement...
    if( gridX == toX && gridY == toY ){
        step_count ++; // <-------------------------------- we've arrived at destination
        grid_move();
    }
}
else{
    // otherwise check if the player wants / if they can move.
    var hsp = keyboard_check(right_key) + (-keyboard_check(left_key));
    var vsp = keyboard_check(down_key) + (-keyboard_check(up_key));
    
    if( abs(hsp + vsp) == 1 ){
        // doesn't matter that we're adding both, only 1 will ever be effected
        if( place_free( (toX+hsp) * gridScale, (toY+vsp) * gridScale) ){
            toX += hsp;
            toY += vsp;
        }
    }
}
 

Bentley

Member
Grid movement is (barebones):
if snapped...
Allow the player 1 of 4 inputs and cancel out diagonal.
Set target position (ex: targ_x = x + cell_size).
else
move to that position.
 
Top