GML How to set Diagonal Speed for 8 Directional Movement

My players speed is set for 4 but for my code attached I noticed that when I move diagonal (Up Left/Right & Down Left/Right), the speed is faster than 4. What can I do to adjust the code so that the speed remains the same for each position?

GML:
if keyboard_check(vk_up) && place_free(x,y-4) {y-=4}
if keyboard_check(vk_down) && place_free(x,y+4) {y+=4}
if keyboard_check(vk_right) && place_free(x+4,y) {x+=4}
if keyboard_check(vk_left) && place_free(x-4,y) {x-=4}
 
A couple of points. Firstly, it's good to separate your input from the action that should happen. Secondly, it's good to use variables instead of typing in direct numbers (this is known as hard-coding and is considered bad practice). Thirdly, you can achieve the correct speed using lengthdir vectors like this:
Code:
var hor = keyboard_check(vk_right)-keyboard_check(vk_left) // This means hor is 1 when right is held, it's -1 when left is held and it's 0 when both are held or none are held
var ver = keyboard_check(vk_down)-keyboard_check(vk_up) // Same as above but 1 for down and -1 for up

var move_dir = point_direction(0,0,hor,ver); // This figures out what direction the pressed keys are equivalent to
var move_spd = 5;

if (abs(hor)+abs(ver) != 0) { // Make sure the player should be moving
   var xvec = lengthdir_x(move_spd,move_dir); // Find the x vector of movement
   var yvec = lengthdir_y(move_spd,move_dir); // Find the y vector of movement
   if (place_free(x+xvec,y+yvec)) { // Check for instances at the position you want to move to
      x += xvec; // Add the x vector to x
      y += yvec; // And y vector to y
   }
}
Haven't tested that code, but it should work. You can see that I'm storing the keyboard input separately in hor and ver, I'm using a temp variable for the the speed (move_spd), although in a proper project, I'd have this declared as an instance variable in the Create event, rather than a local variable in the Step event and finally, I'm using lengthdir, which naturally normalises the speed to a consistent number in all directions.
 
Last edited:
It worked! But the only issue is that once I release a directional key the players positioning reverts back to the origin sprite index no matter the direction. I already have a <No Key> event which states that if the sprite index is at a certain value (direction) during the <No Key> event then the sprite index would remain at the direction, but that event doesn't work in this case.
 

Sybok

Member
If it is only eight directional movement the diagonals will always be 0.707 of the movement speed. This will save the overhead of calculating the diagonal speed every frame, as it is unnecessary.
 
Create Event:

Code:
move_spd = 7;



Step Event:

GML:
 var hor = keyboard_check(vk_right)-keyboard_check(vk_left) // This means hor is 1 when right is held, it's -1 when left is held and it's 0 when both are held or none are held
var ver = keyboard_check(vk_down)-keyboard_check(vk_up) // Same as above but 1 for down and -1 for up

var move_dir = point_direction(0,0,hor,ver); // This figures out what direction the pressed keys are equivalent to


if (abs(hor)+abs(ver) != 0) { // Make sure the player should be moving
   var xvec = lengthdir_x(move_spd,move_dir); // Find the x vector of movement
   var yvec = lengthdir_y(move_spd,move_dir); // Find the y vector of movement
   if (place_free(x+xvec,y+yvec)) { // Check for instances at the position you want to move to
      x += xvec; // Add the x vector to x
      y += yvec; // And y vector to y
   }
}
  direction=point_direction(xprevious,yprevious,x,y); // update direction
  //Set Sprite

  switch(direction){
    case 0: sprite_index = sprite_RB_Run_Right_With_Football; break;
    case 45: sprite_index = sprite_RB_Run_Up_Right_With_Football; break;
    case 90: sprite_index = sprite_RB_Run_Up_With_Football break;
    case 135: sprite_index = sprite_RB_Run_Up_Left_With_Football break;
    case 180: sprite_index = sprite_RB_Run_Left_With_Football; break;
    case 225: sprite_index = sprite_RB_Run_Down_Left_With_Football; break;
    case 270: sprite_index = sprite_RB_Run_Down_With_Football; break;
    case 315: sprite_index = sprite_RB_Run_Down_Right_With_Football; break;
  }

<NO KEY> EVENT (DRAG AND DROP)

Code:
start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Left_With_Football
      set the sprite to sprite_RB_Run_Left_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Right_With_Football
      set the sprite to sprite_RB_Run_Right_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Up_With_Football
      set the sprite to sprite_RB_Run_Up_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Down_With_Football
      set the sprite to sprite_RB_Run_Down_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Up_Left_With_Football
      set the sprite to sprite_RB_Run_Up_Left_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Up_Right_With_Football
      set the sprite to sprite_RB_Run_Up_Right_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Down_Left_With_Football
      set the sprite to sprite_RB_Run_Down_Left_With_Football with subimage 0 and speed .5
else
      start moving in directions 000010000 with speed set to 0
if sprite_index is equal to sprite_RB_Run_Down_Right_With_Football
      set the sprite to sprite_RB_Run_Down_Right_With_Football with subimage 0 and speed .5
else
 

Felbar

Member
This is what I do
it handles keeping diagonal movement at proper speed and
sub pixel rounding errors

In object create
Code:
spd = 2;
hsp = 0;
vsp = 0;
cx = 0;
cy = 0;
dir = 0;

In step
Code:
var k_left = keyboard_check(ord("A"));
var k_right = keyboard_check(ord("D"));
var k_up = keyboard_check(ord("W"));
var k_down = keyboard_check(ord("S"));

//make a move vector from input
var vx = k_right - k_left;
var vy = k_down - k_up;

//get length of move vector
var len = sqrt(vx*vx + vy*vy);

//if we moved
if( len !=0 ) {

    //turn vector into angle
    //used for picking the sprite
    dir = point_direction(0,0,vx,vy);

    //normalise vector
    vx = vx / len;
    vy = vy / len;

    //scale the normalized vector by the speed
    hsp = vx * spd;
    vsp = vy * spd;

    //the cx cy stuff here is for sub pixel movement
    //basically accumulating the rounded off bits
    cx += hsp;
    cy += vsp;
    hsp = round(cx);
    vsp = round(cy);

    cx -= hsp;
    cy -= vsp;

    //finally collision
    repeat(abs(hsp)) {
        if(!place_meeting(x+sign(hsp), y, oWall)) {
            x += sign(hsp);
        } else {
            hsp = 0;
            break;
        }
    }

    repeat(abs(vsp)) {
        if(!place_meeting(x, y +sign(vsp), oWall)) {
            y += sign(vsp);
        } else {
            vsp = 0;
            break;
        }
    }    
}

//animation script
anim_set(len);

Finally the anim_set script
I name the sprites based on their angle
This really should be a switch statement but ... lazy
Code:
var len = argument0;
image_xscale = 1;
if(dir == 0) {
    sprite_index = spr_p0_stop;
    if(len != 0) sprite_index = spr_p0_run;
}
if(dir == 45) {
    image_xscale = -1;
    sprite_index = spr_p135_stop;
    if(len != 0) sprite_index = spr_p135_run;
}
if(dir == 90) {
    sprite_index = spr_p90_stop;
    if(len != 0) sprite_index = spr_p90_run;
}

if(dir == 135) {
    sprite_index = spr_p135_stop;
    if(len != 0) sprite_index = spr_p135_run;
}
if(dir == 180) {
    image_xscale = -1;
    sprite_index = spr_p0_stop;
    if(len != 0) sprite_index = spr_p0_run;
}
if(dir == 225) {
    image_xscale = -1;
    sprite_index = spr_p315_stop;
    if(len != 0) sprite_index = spr_p315_run;
}
if(dir == 270) {
    sprite_index = spr_p270_stop;
    if(len != 0) sprite_index = spr_p270_run;
}
if(dir == 315) {
    sprite_index = spr_p315_stop;
    if(len != 0) sprite_index = spr_p315_run;
}
 
Last edited:

Sybok

Member
What about something like this:

GML:
speed = 0;

var hor = keyboard_check(vk_right) - keyboard_check(vk_left);
var ver = keyboard_check(vk_down) - keyboard_check(vk_up);

if (abs(hor) + abs(ver) != 0) {
    direction = point_direction(0, 0, hor, ver);
    speed = move_spd;
}

switch(direction) {
    case 0: sprite_index = sprite_RB_Run_Right_With_Football; break;
    case 45: sprite_index = sprite_RB_Run_Up_Right_With_Football; break;
    case 90: sprite_index = sprite_RB_Run_Up_With_Football break;
    case 135: sprite_index = sprite_RB_Run_Up_Left_With_Football break;
    case 180: sprite_index = sprite_RB_Run_Left_With_Football; break;
    case 225: sprite_index = sprite_RB_Run_Down_Left_With_Football; break;
    case 270: sprite_index = sprite_RB_Run_Down_With_Football; break;
    case 315: sprite_index = sprite_RB_Run_Down_Right_With_Football; break;
}
 

Sybok

Member
Seems like an ideal scenario for the inbuilt handling to me.

No reason to use a complicated system for such a simple task like this.
 
@Sybok I tried to use the code in the Step Event & and also I tried using it for the <No Key> event and it didn't work. Instead the object began moving on its own horizontal.
 

Sybok

Member
I just tested with a blank project and this is my code in its entirety, in the step event, and it works perfectly for me.

Code:
move_spd = 10;

speed = 0;

var hor = keyboard_check(vk_right) - keyboard_check(vk_left);
var ver = keyboard_check(vk_down) - keyboard_check(vk_up);

if (abs(hor) + abs(ver) != 0) {
    direction = point_direction(0, 0, hor, ver);
    speed = move_spd;
}
 
Would I place the code below the Code that I already have in my Step Event which causes the player to move in 8 directions?

Create Event:

GML:
move_spd = 7;



Step Event:

GML:
var hor = keyboard_check(vk_right)-keyboard_check(vk_left) // This means hor is 1 when right is held, it's -1 when left is held and it's 0 when both are held or none are held
var ver = keyboard_check(vk_down)-keyboard_check(vk_up) // Same as above but 1 for down and -1 for up

var move_dir = point_direction(0,0,hor,ver); // This figures out what direction the pressed keys are equivalent to


if (abs(hor)+abs(ver) != 0) { // Make sure the player should be moving
var xvec = lengthdir_x(move_spd,move_dir); // Find the x vector of movement
var yvec = lengthdir_y(move_spd,move_dir); // Find the y vector of movement
if (place_free(x+xvec,y+yvec)) { // Check for instances at the position you want to move to
x += xvec; // Add the x vector to x
y += yvec; // And y vector to y
}
}
direction=point_direction(xprevious,yprevious,x,y); // update direction
//Set Sprite

switch(direction){
case 0: sprite_index = sprite_RB_Run_Right_With_Football; break;
case 45: sprite_index = sprite_RB_Run_Up_Right_With_Football; break;
case 90: sprite_index = sprite_RB_Run_Up_With_Football break;
case 135: sprite_index = sprite_RB_Run_Up_Left_With_Football break;
case 180: sprite_index = sprite_RB_Run_Left_With_Football; break;
case 225: sprite_index = sprite_RB_Run_Down_Left_With_Football; break;
case 270: sprite_index = sprite_RB_Run_Down_With_Football; break;
case 315: sprite_index = sprite_RB_Run_Down_Right_With_Football; break;
}
 

Sybok

Member
Nope. In total it would look like this;

Code:
move_spd = 7;
speed = 0;

var hor = keyboard_check(vk_right) - keyboard_check(vk_left);
var ver = keyboard_check(vk_down) - keyboard_check(vk_up);

if (abs(hor) + abs(ver) != 0) {
    direction = point_direction(0, 0, hor, ver);
    speed = move_spd;
}

switch(direction){
case 0: sprite_index = sprite_RB_Run_Right_With_Football; break;
case 45: sprite_index = sprite_RB_Run_Up_Right_With_Football; break;
case 90: sprite_index = sprite_RB_Run_Up_With_Football break;
case 135: sprite_index = sprite_RB_Run_Up_Left_With_Football break;
case 180: sprite_index = sprite_RB_Run_Left_With_Football; break;
case 225: sprite_index = sprite_RB_Run_Down_Left_With_Football; break;
case 270: sprite_index = sprite_RB_Run_Down_With_Football; break;
case 315: sprite_index = sprite_RB_Run_Down_Right_With_Football; break;
}
 
@Sybok That worked! but the only issue is that once I release a directional key the players positioning reverts back to the origin sprite index no matter the direction. I already have a <No Key> event which states that if the sprite index is at a certain value (direction) during the <No Key> event then the sprite index would remain at the direction, but that event doesn't work in this case.
 

Sybok

Member
From your post, it's a bit unclear what is not working. The position or the sprite index?

In either case, you probably have code somewhere making it revert, which you don't need.

With the code above x, y, and direction, will always remain at their last values. If the direction was 45 degrees, it will stay 45 degrees, unless you tell it otherwise. So there has gotta be code elsewhere that is causing it to revert to something else.
 
Yes the position of the sprite index.

With the code below when no keys are being pressed does the sprite index stop on the facing direction

GML:
 if keyboard_check(vk_up) && place_free(x,y-7) {y-=7} // y-= is speed
if keyboard_check(vk_down) && place_free(x,y+7) {y+=7} // y+= is speed
if keyboard_check(vk_right) && place_free(x+7,y) {x+=7} // x+= is speed
if keyboard_check(vk_left) && place_free(x-7,y) {x-=7} // x-= is speed

if (x!=xprevious or y!=yprevious) //If the player has moved this frame
{
  direction=point_direction(xprevious,yprevious,x,y); // update direction
  //Set Sprite
  switch(direction){
    case 0: sprite_index = sprite_RB_Run_Right_With_Football; break;
    case 45: sprite_index = sprite_RB_Run_Up_Right_With_Football; break;
    case 90: sprite_index = sprite_RB_Run_Up_With_Football break;
    case 135: sprite_index = sprite_RB_Run_Up_Left_With_Football break;
    case 180: sprite_index = sprite_RB_Run_Left_With_Football; break;
    case 225: sprite_index = sprite_RB_Run_Down_Left_With_Football; break;
    case 270: sprite_index = sprite_RB_Run_Down_With_Football; break;
    case 315: sprite_index = sprite_RB_Run_Down_Right_With_Football; break;
  }
}
With the others the sprite index reverts back to the orgin position each time <NO Keys> are being pressed
 

Nidoking

Member
You could use what you have above and just move the switch inside the if that checks for whether you're moving.

"If I'm moving, change the sprite. If I'm not moving, don't." Very basic logic that you can translate into computer think if you bother.
 

Sybok

Member
Or set the idle sprite index on the start of the step. If there's no input, no further action is required. If there is input, the sprite index will be set accordingly.
 
@Nidoking @Sybok
The code below works better with keep all movements (8 directions) the same speed

GML:
move_spd = 7;
speed = 0;

var hor = keyboard_check(vk_right) - keyboard_check(vk_left);
var ver = keyboard_check(vk_down) - keyboard_check(vk_up);

if (abs(hor) + abs(ver) != 0) {
    direction = point_direction(0, 0, hor, ver);
    speed = move_spd;
}

switch(direction){
case 0: sprite_index = sprite_RB_Run_Right_With_Football; break;
case 45: sprite_index = sprite_RB_Run_Up_Right_With_Football; break;
case 90: sprite_index = sprite_RB_Run_Up_With_Football break;
case 135: sprite_index = sprite_RB_Run_Up_Left_With_Football break;
case 180: sprite_index = sprite_RB_Run_Left_With_Football; break;
case 225: sprite_index = sprite_RB_Run_Down_Left_With_Football; break;
case 270: sprite_index = sprite_RB_Run_Down_With_Football; break;
case 315: sprite_index = sprite_RB_Run_Down_Right_With_Football; break;
}
Could I also use your recommendations for the above code as well?
(Recommendations) just moving the switch inside the if that checks for whether you're moving. Or set the idle sprite index on the start of the step.
 

Nidoking

Member
I stand by my recommendation. I wouldn't trust the built-in direction variable to retain its value if you set speed to 0, which you're explicitly doing here. If you don't need to change the sprite, you shouldn't change it. Relying on the condition that determines the sprite to remain the same is another layer of things that can go wrong.
 

Sabnock

Member
For a little bit fun i tried a different way of doing this

Creat Event
GML:
hspd = 0;
vspd = 0;

move_spd = 4;
Step Event
GML:
var _hmove = keyboard_check(vk_right) - keyboard_check(vk_left);

hspd = _hmove * move_spd;

var _vmove = keyboard_check(vk_down) - keyboard_check(vk_up);

vspd = _vmove * move_spd;

x += hspd; y += vspd;


switch _hmove {
    case 1 :
        switch _vmove {
            case  0 : sprite_index = s_player_right; break;
            case  1 : sprite_index = s_player_down_right; break;
            case -1 : sprite_index = s_player_up_right;      
        }
    break;

    case -1 :
        switch _vmove {
            case  0 : sprite_index = s_player_left; break;
            case  1 : sprite_index = s_player_down_left; break;
            case -1 : sprite_index = s_player_up_left;       
        }
    break;

    case 0 :
        switch _vmove {
            case  1 : sprite_index = s_player_down; break;
            case -1 : sprite_index = s_player_up;       
        }
    break;

    default : //  whatever you idle sprite is
}
 
Last edited:

Nidoking

Member
You seem to be missing the entire point of your thread here, which was to calculate horizontal and vertical components for diagonal movement so that the player doesn't move faster. You've gotten hung up on the sprite manipulation and have not yet figured out how to take the part that solves your problem and combine it with any of the many, many ways of handling the sprites that have worked for you.
 

Sabnock

Member
You seem to be missing the entire point of your thread here, which was to calculate horizontal and vertical components for diagonal movement so that the player doesn't move faster. You've gotten hung up on the sprite manipulation and have not yet figured out how to take the part that solves your problem and combine it with any of the many, many ways of handling the sprites that have worked for you.
you are correct. Code updated to include diagonal speed scaling;

Creat Event
GML:
hspd = 0;
vspd = 0;

move_spd = 4;
Step Event
GML:
var _hmove = keyboard_check(vk_right) - keyboard_check(vk_left);

hspd = _hmove * move_spd;

var _vmove = keyboard_check(vk_down) - keyboard_check(vk_up);

vspd = _vmove * move_spd;


// diagonal speed scaling
scale = abs((_hmove & _vmove) * .707);

if (scale != 0) {
    hspd *= scale;
    vspd *= scale;
}

x += hspd; y += vspd;


switch _hmove {
    case 1 :
        switch _vmove {
            case  0 : sprite_index = s_player_right; break;
            case  1 : sprite_index = s_player_down_right; break;
            case -1 : sprite_index = s_player_up_right;
        }
    break;

    case -1 :
        switch _vmove {
            case  0 : sprite_index = s_player_left; break;
            case  1 : sprite_index = s_player_down_left; break;
            case -1 : sprite_index = s_player_up_left; 
        }
    break;

    case 0 :
        switch _vmove {
            case  1 : sprite_index = s_player_down; break;
            case -1 : sprite_index = s_player_up; 
        }
    break;

    default : //  whatever you idle sprite is
}
 
Last edited:

Sabnock

Member
if you want constant movement just change the code at the start of the step event with this


GML:
var _hmove = keyboard_check(vk_right) - keyboard_check(vk_left);
var _vmove = keyboard_check(vk_down) - keyboard_check(vk_up);

if (_hmove | _vmove != 0) {
    hspd = _hmove * move_spd;
    vspd = _vmove * move_spd;

    scale = abs((_hmove & _vmove) * .707);

    if (scale != 0) {    
        hspd *= scale;
        vspd *= scale;    
    }
}

x += hspd; y += vspd;
 
Top