How to Create Basic Player Movement in GameMaker

A game does not feel like a game until the player can actually move. Whether you are creating a top-down adventure, a puzzle game, or a fast arcade shooter, movement is usually one of the first systems you need to build.

Fortunately, creating basic player movement in GameMaker does not require hundreds of lines of code. With a sprite, an object, a room, and a few simple GML instructions, you can make a character respond to the keyboard in minutes.

The basic idea is straightforward. GameMaker checks which keys the player is holding, converts those inputs into horizontal and vertical directions, and then changes the character’s position.

From there, you can improve the system by fixing diagonal speed, adding wall collisions, changing animations, and supporting different control schemes.

This beginner-friendly tutorial focuses on four-directional movement for a top-down 2D game. It also explains what each line does, so you can adapt the code rather than simply copying it.

Prepare Your Player Object and Room

Before writing any movement code, create the basic assets your project needs. You can use temporary artwork, so there is no need to design a detailed character yet.

Create a small sprite and name it spr_player. A simple coloured square is enough for testing. Set its origin to the centre, as this generally makes positioning, rotation, and collision behaviour easier to manage.

Next, create an object called obj_player and assign spr_player to it. Objects are used to control game behaviour, while their visible appearance usually comes from an assigned sprite.

Finally, create a room named rm_game and drag one instance of obj_player into it. GameMaker’s basic workflow similarly combines a sprite, an object, object events, and a room containing an instance.

Run the game once. The character should appear but remain completely still. That is expected because you have not yet given it any movement instructions.

Set the Movement Speed in the Create Event

Open obj_player and add a Create Event. This event runs when an instance is created, making it a good place to establish starting values such as health, speed, ammunition, or character state.

Add the following GML code:

move_speed = 4;

This creates an instance variable named move_speed and stores the value 4 inside it. Later, the movement code will use this value to determine how far the character travels during each game step.

Keeping the speed in a named variable is better than placing the number directly throughout your code. When you want to make the character faster or slower, you only need to change one value.

You can experiment with different numbers:

move_speed = 2; // Slow movement
move_speed = 4; // Moderate movement
move_speed = 8; // Fast movement

The ideal value depends on your room size, camera scale, target frame rate, and the kind of experience you want. A horror game may benefit from slower movement, while an arcade title may feel better with a much faster character.

Read Keyboard Input in the Step Event

Now add a regular Step Event to obj_player. Object events represent particular moments in GameMaker’s game loop, and the Step Event is normally used for behaviour that must update continuously.

Enter this code:

var _move_x = keyboard_check(vk_right)
            - keyboard_check(vk_left);

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

The keyboard_check() function returns true while a key is being held. Unlike a pressed check that activates only once, it continues returning true during every step in which the specified key remains down.

The subtraction creates a convenient directional value. When the right arrow is held, _move_x becomes 1. When the left arrow is held, it becomes -1. If neither or both keys are held, the result is 0.

The same principle controls vertical input. In GameMaker rooms, increasing x moves an instance right, while increasing y moves it down.

Add these lines below the input code:

x += _move_x * move_speed;
y += _move_y * move_speed;

Run the game again. Your player should now move in four directions using the arrow keys.

Add WASD Controls

Many PC players expect both arrow-key and WASD controls. You can support both without rewriting the entire movement system.

Replace the original input code with this version:

var _right = keyboard_check(vk_right) || keyboard_check(ord("D"));
var _left  = keyboard_check(vk_left)  || keyboard_check(ord("A"));
var _down  = keyboard_check(vk_down)  || keyboard_check(ord("S"));
var _up    = keyboard_check(vk_up)    || keyboard_check(ord("W"));

var _move_x = _right - _left;
var _move_y = _down - _up;

The || operator means “or.” As a result, _right becomes true when the player holds either the right arrow or the D key.

The ord() function is used to identify letter and number keys. Writing the controls this way also makes them easier to understand and modify later.

For a larger game, you may eventually create a dedicated input system. That would allow players to remap controls or switch between a keyboard, gamepad, and touch interface. For a first project, however, this direct approach is perfectly practical.

Fix Faster Diagonal Movement

The current system has a small problem. When the player moves horizontally, the character travels four pixels per step. When moving diagonally, it travels four pixels horizontally and four pixels vertically at the same time.

That creates a total diagonal distance of approximately 5.66 pixels. In other words, the player moves about 41% faster diagonally.

You can fix this by normalising the direction before multiplying it by move_speed:

if (_move_x != 0 || _move_y != 0)
{
    var _length = point_distance(0, 0, _move_x, _move_y);

    _move_x /= _length;
    _move_y /= _length;
}

The point_distance() function calculates the length of the vector created by the horizontal and vertical input. Dividing both values by that length turns the input into a direction with a maximum length of one.

Your complete movement calculation can now look like this:

var _right = keyboard_check(vk_right) || keyboard_check(ord("D"));
var _left  = keyboard_check(vk_left)  || keyboard_check(ord("A"));
var _down  = keyboard_check(vk_down)  || keyboard_check(ord("S"));
var _up    = keyboard_check(vk_up)    || keyboard_check(ord("W"));

var _move_x = _right - _left;
var _move_y = _down - _up;

if (_move_x != 0 || _move_y != 0)
{
    var _length = point_distance(0, 0, _move_x, _move_y);

    _move_x /= _length;
    _move_y /= _length;
}

var _x_speed = _move_x * move_speed;
var _y_speed = _move_y * move_speed;

x += _x_speed;
y += _y_speed;

The character should now move at a consistent speed in every direction.

Add Basic Wall Collisions

Free movement is useful for testing, but your player will probably need to stop at walls, trees, furniture, or other solid objects.

Create a wall sprite named spr_wall, followed by an object called obj_wall. Assign the sprite to the object and place several wall instances around your room.

Instead of changing x and y directly, use GameMaker’s move_and_collide() function:

move_and_collide(_x_speed, _y_speed, obj_wall);

Remove or replace these original lines:

x += _x_speed;
y += _y_speed;

The move_and_collide() function moves an instance by the supplied horizontal and vertical distances while avoiding a specified object or tile map. It can also return an array containing the instances or tile maps that affected the movement.

For several obstacle types, you can give them a shared parent object or pass an array of collision targets. GameMaker’s collision functions can work with objects, multiple object types, and tile maps, depending on how your level is structured.

Make sure both the player and wall sprites have suitable collision masks. A badly sized mask may cause the character to stop too early, overlap a wall, or become caught on corners.

Connect Movement to Character Animation

Movement feels more polished when the character visually reacts to its direction. Even a basic two-frame walking animation can make a large difference.

You can pause the animation while the character is standing still:

if (_move_x == 0 && _move_y == 0)
{
    image_speed = 0;
    image_index = 0;
}
else
{
    image_speed = 0.2;
}

For a side-facing sprite, you can flip it when moving left or right:

if (_move_x != 0)
{
    image_xscale = sign(_move_x);
}

This works best when the sprite origin is centred. A non-centred origin can make the image appear to jump when its horizontal scale changes.

Games with separate animations for each direction may use variables such as spr_player_up, spr_player_down, and spr_player_side. The current movement direction can then determine which sprite is displayed.

Keep the movement logic separate from animation selection where possible. That organisation becomes valuable when you later add attacking, rolling, swimming, or taking damage.

Common Movement Problems to Check

When the player does not move, confirm that obj_player has been placed inside the room. Also check that move_speed is created before the Step Event attempts to use it.

If the player moves only once when a key is pressed, you may be using keyboard_check_pressed() instead of keyboard_check(). The pressed version is useful for actions such as jumping or opening a menu, while continuous walking normally needs a held-key check.

When collisions feel inaccurate, inspect each sprite’s origin and collision mask. Transparent space around an image may still affect its automatic mask unless the mask settings are adjusted.

Finally, avoid adding many new features before testing. Run the game after every meaningful change. When an error appears, you will have a much smaller section of code to investigate.

Basic player movement in GameMaker begins with a simple process: read the keyboard, calculate a direction, multiply that direction by a movement speed, and apply the result to the player object.

Normalising the input prevents diagonal speed boosts, while move_and_collide() provides an approachable way to introduce solid obstacles.

Animation, multiple control schemes, and carefully configured collision masks can then make the system feel more responsive and polished. Create a small test room and type the code yourself rather than pasting everything at once.

Once the movement works, experiment with acceleration, sprinting, gamepad input, directional animations, or moving between rooms. Each small improvement will strengthen both your GML knowledge and your understanding of 2D game design.