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

Correct movement in FPS game

flerpyderp

Member
Trying to make a simple Wolfenstein/Doom clone, and I can't find many resources on the topic of movement in FPS games in Game Maker. The tutorials that I've watched ignored the problem of incorrect diagonal movement speed entirely, and I've relied mainly on looking in threads about top-down game movement to try to find a solution.

Code:
//Mouse control
mouseL = mouse_check_button_pressed(mb_left);
mouseR = mouse_check_button_pressed(mb_right);

direction -= sensitivity * (display_mouse_get_x() - display_get_width() / 2);
display_mouse_set(display_get_width() / 2,display_get_height() / 2);

//Keyboard control
keyF = keyboard_check(ord("W"));
keyB = keyboard_check(ord("S"));
keyR = keyboard_check(ord("D"));
keyL = keyboard_check(ord("A"));

keysFB = keyboard_check(ord("W")) - keyboard_check(ord("S"));
keysRL = keyboard_check(ord("D")) - keyboard_check(ord("A"));

//Accelerate
velFB += keysFB * accel;
velRL += keysRL * accel;

//Diagonal
if (keyF && keyR) || (keyF && keyL) || (keyB && keyR) || (keyB && keyL)
{
    velFinal = velMax / 1.41421356237;
}
else velFinal = velMax;

//Limit
velFB = clamp(velFB,-velFinal,velFinal);
velRL = clamp(velRL,-velFinal,velFinal);

//Decelerate
if (keysFB == 0) && (abs(velFB) >= accel)
{
    velFB -= sign(velFB) * accel;
 
    if (velFB > -accel) && (velFB < accel) velFB = 0;
}
if (keysRL == 0) && (abs(velRL) >= accel)
{
    velRL -= sign(velRL) * accel;
 
    if (velRL > -accel) && (velRL < accel) velRL = 0;
}

//Translate velocities to x and y
var velX = lengthdir_x(velFB,direction) + lengthdir_x(velRL,direction - 90);
var velY = lengthdir_y(velFB,direction) + lengthdir_y(velRL,direction - 90);

//Collision
if (!place_meeting(x+velX,y,par_solid))
{
    x += velX;
}
else
{
    while (!place_meeting(x+sign(velX),y,par_solid))
    {
        x += sign(velX);
    }
 
    velX = 0;
}

if (!place_meeting(x,y+velY,par_solid))
{
    y += velY;
}
else
{
    while (!place_meeting(x,y+sign(velY),par_solid))
    {
        y += sign(velY);
    }
 
    velY = 0;
}
The square root of 2 approach I used here doesn't give the correct diagonal speed, and I'm wondering if my approach to making movement for this type of game is fundamentally wrong.

If anyone has experience making this type of movement, please give me a nudge in the right direction.
 

CloseRange

Member
i think you are doing way more work than you need to.
you are facing a direction and going at a certain speed. perfect for lengthdir_x and y
Code:
var forward = keyboard_check(ord("W"));
var dx = lengthdir_x(spd, dir) * forward;
var dy = lengthdir_y(spd, dir) * forward;

//  ... collision code ...

x += dx;
y += dy;
the hard part is actually rotating the player to get the correct direction.
You also need to deal with collisions but you already seem like you have that under control via your code.

So here is my example rotation code:
Code:
var turnL = keyboad_check(ord("A"));
var turnR = keyboard_check(ord("D"));
dir += (turnL - turnR) * 10;
it doesn't use the mouse like a fps normally would this is just to test that it works
 

flerpyderp

Member
i think you are doing way more work than you need to.
you are facing a direction and going at a certain speed. perfect for lengthdir_x and y

the hard part is actually rotating the player to get the correct direction.

it doesn't use the mouse like a fps normally would this is just to test that it works
Excellent, this appears to be the way to go. Thanks.

I'm using the direction code I already had in the first post. I can move forward, backwards and turn using the mouse.

Code:
direction -= sensitivity * (display_mouse_get_x() - display_get_width() / 2);
display_mouse_set(display_get_width() / 2,display_get_height() / 2);

var dx = lengthdir_x(velWalk,direction) * (keyF - keyB);
var dy = lengthdir_y(velWalk,direction) * (keyF - keyB);
 
x += dx;
y += dy;
I can change the lengthdir_x and y to (velWalk, direction - 90) * (keyR - keyL) to strafe.

The problem now is that I can't figure out how to combine the forward/backward movement with the left/right without doubling the speed.
 
Last edited:

flerpyderp

Member
I have simply added seperate checks to see what combination of keys are currently being held, and changed dx/dy to lengthdir_x/y(velwalk,direction - (45,90,135, etc) accordingly. I now have consistent speed in all 8 directions, while using the mouse to rotate. Thanks again to @CloseRange for the advice.
 

Roa

Member
I came up with this little gem that I stick in all my buttery smooth FPS controllers.
Code:
joyx=(keyboard_check(Right)-keyboard_check(Left))+gamepad_axis_value(gamepad_assigned,gp_axislh);
joyy=(keyboard_check(Down)-keyboard_check(Up))+gamepad_axis_value(gamepad_assigned,gp_axislv);
joy_dir=point_direction(0,0,joyx,joyy);
joy_dist=point_distance(0,0,joyx,joyy);
last_joy_dir=joy_dir;


if joy_dist>0{
    motion_add(dir-90+joy_dir,accel);
    if speed>speed_max{
        speed=speed_max;
        
    }
 

flerpyderp

Member
I came up with this little gem that I stick in all my buttery smooth FPS controllers.
Code:
joyx=(keyboard_check(Right)-keyboard_check(Left))+gamepad_axis_value(gamepad_assigned,gp_axislh);
joyy=(keyboard_check(Down)-keyboard_check(Up))+gamepad_axis_value(gamepad_assigned,gp_axislv);
joy_dir=point_direction(0,0,joyx,joyy);
joy_dist=point_distance(0,0,joyx,joyy);
last_joy_dir=joy_dir;


if joy_dist>0{
    motion_add(dir-90+joy_dir,accel);
    if speed>speed_max{
        speed=speed_max;
       
    }
I'm confused, you're using the keyboard buttons + a gamepad stick to control the movement and direction? Also, how do you manage collisions when using the built in speed/motion_add?

I'm having a new problem using the lengthdir method. Since I'm updating x/y based on lengthdir * key input, not pressing a key sets the speed immediately to zero so I can't figure out how to add deceleration.
 

Roa

Member
I'm confused, you're using the keyboard buttons + a gamepad stick to control the movement and direction? Also, how do you manage collisions when using the built in speed/motion_add?

I'm having a new problem using the lengthdir method. Since I'm updating x/y based on lengthdir * key input, not pressing a key sets the speed immediately to zero so I can't figure out how to add deceleration.
more like keyboard OR controller.
I use a completely separate script for checking collisions in the end_step.

scr_col() //if place_meeting(x,y,solid_base){scr_col();}
Code:
var xmot, ymot,spd;
spd=abs(point_distance(x,y,xprevious,yprevious))
x=xprevious;
y=yprevious;

for (i = 0; i < 90; i += 2)
    {
    xmot = x + lengthdir_x(spd, direction + i);
    ymot = y + lengthdir_y(spd, direction + i);
    if !place_meeting(xmot,ymot,solid_base) then {x = xmot; y = ymot; exit}
    xmot = x + lengthdir_x(spd, direction - i);
    ymot = y + lengthdir_y(spd, direction - i);
    if !place_meeting(xmot,ymot,solid_base) then {x = xmot; y = ymot; exit}
    }
solid_base is just a parent for colidable objects.

my look script

Code:
if using_gamepad==true{
    dir-=(gamepad_axis_value(gamepad_assigned,gp_axisrh)*abs(gamepad_axis_value(gamepad_assigned,gp_axisrh)))*gamepad_sensitivity;
    zdir+=((gamepad_axis_value(gamepad_assigned,gp_axisrv)*abs(gamepad_axis_value(gamepad_assigned,gp_axisrv)))*gamepad_sensitivity)/y_axis_sensitivity_differential;
}

if mouse_lock==true{
    var ww=window_get_width();
    var wh=window_get_height();
    dir-=(window_mouse_get_x()-(ww/2))/10
    zdir+=(window_mouse_get_y()-(wh/2))/16
    window_mouse_set(ww/2,wh/2)
    }

if keyboard_check_pressed(ord("M")){
    if mouse_lock=true {mouse_lock=false; window_set_cursor(cr_default);} else {mouse_lock=true; window_set_cursor(cr_none);}
    }


zdir=clamp(zdir,-89,89);
var c=lengthdir_x(1,zdir);
vx=lengthdir_x(1*c,dir);
vy=lengthdir_y(1*c,dir);
vz=lengthdir_y(1,zdir);
a combo of these 3 scripts gets me perfect FPS controllers every time.
adding deceleration to my scripts is as simple as using friction.

the starting values are pretty self explanatory too
Code:
dir //facing direction
zdir// look up direction
vx, vy, vz// looking vector: perfect for shooting trajectories and camera matrix
mouse_lock// snaps the mouse
gamepad_sensitivity // how sensitive the gamepad analog is
y_axis_differential// the number for the up and down looking to divide the sensitivity by, as yaw and pitch having the same sensitivity feels like 💩💩💩💩, looking up and is less used and there for should be more constrained.
 
Last edited:

flerpyderp

Member
@Roa I've tried your method of FPS controller and it works incredibly well, very smooth like you said. Extremely grateful for you sharing this and providing an explanation.

EDIT: The only thing I'd like to change would be the way it handles sliding against walls. Currently, the player will slide along the wall at full speed rather than be slowed down depending on the angle they are colliding into it from. I think changing "spd" depending on the value of "i" is how to do it, but I've been struggling to figure it out.
 
Last edited:

Roa

Member
@Roa I've tried your method of FPS controller and it works incredibly well, very smooth like you said. Extremely grateful for you sharing this and providing an explanation.

EDIT: The only thing I'd like to change would be the way it handles sliding against walls. Currently, the player will slide along the wall at full speed rather than be slowed down depending on the angle they are colliding into it from. I think changing "spd" depending on the value of "i" is how to do it, but I've been struggling to figure it out.
The collision code is not inheriently part of the control, its just " A collision code" for general purpose use. It's not even mine honestly, its a heavily modfied version of something @Nocturne did a long long time ago that I just keep passing to projects for a quick fix. Its not even that efficient honestly. I really can't tell you the best way to modify that code to go slower along a wall, as the code is made to preemptively avoid the intersecting collision by judging the speed and vector in the first place.

My only suggestion is simply dividing xmot and ymot before checking.
 
Last edited:
Top