Skip to main content

Control Overview

Cub3D uses a simple keyboard-based control scheme inspired by classic first-person games. All controls are responsive and support smooth movement through key press/release detection.

Movement

W, A, S, D keys for directional movement

Camera

Left/Right arrow keys for rotation

Exit

ESC key to close the game

Window

Close button to exit

Movement Controls

Cub3D implements smooth, direction-relative movement using the WASD key layout.

Key Definitions

From cub3.h:38-41, the movement keys are defined as:
#define W 119  // Forward
#define A 97   // Strafe left
#define S 115  // Backward
#define D 100  // Strafe right

Movement Keys

Move forward in the direction you’re facing.
if (mapa->key_w)
{
    mapa->new_x += cos_angle * mapa->speed;
    mapa->new_y += sin_angle * mapa->speed;
}
The player moves along their current viewing direction using trigonometric calculations.

Movement Speed

From main.c:55:
mapa->speed = 1;
The default movement speed is 1 pixel per frame. With the game’s continuous rendering loop, this provides smooth, responsive movement.

Camera Rotation

Camera rotation is controlled using the arrow keys and directly modifies the player’s viewing angle.

Rotation Keys

Left Arrow

Rotate camera counter-clockwise (left)
if (mapa->key_left)
    mapa->starting_angle -= 0.05;

Right Arrow

Rotate camera clockwise (right)
if (mapa->key_right)
    mapa->starting_angle += 0.05;

Rotation Speed

From utils6.c:58-64:
void rotate_cubed(t_map *mapa)
{
    if (mapa->key_left)
        mapa->starting_angle -= 0.05;
    if (mapa->key_right)
        mapa->starting_angle += 0.05;
}
The rotation speed is 0.05 radians per frame, providing smooth camera rotation without being too fast or slow.

Angle Normalization

The camera angle is kept within 0 to 2π radians:
if (mapa->starting_angle > 2 * PI)
    mapa->starting_angle = 0;
if (mapa->starting_angle < 0)
    mapa->starting_angle = 2 * PI;
This ensures angles wrap around smoothly without overflow issues.

Direction and Plane Calculation

After rotation, the direction vector and camera plane are recalculated:
void set_player_dir_and_plane(t_map *mapa)
{
    mapa->dir_x = cos(mapa->starting_angle);
    mapa->dir_y = sin(mapa->starting_angle);
    mapa->plane_x = -mapa->dir_y * 0.66;
    mapa->plane_y = mapa->dir_x * 0.66;
}
The camera plane is perpendicular to the direction vector and scaled by 0.66, which determines the field of view.

Key Event System

Cub3D uses a key press/release system for responsive controls:

Key Press Handler

From utils5.c:29-46, when a key is pressed:
int handle_key(int keysym, t_map *mapa)
{
    if (keysym == XK_Escape)
        closewindow(mapa);
    if (keysym == XK_a)
        mapa->key_a = 1;
    else if (keysym == XK_d)
        mapa->key_d = 1;
    else if (keysym == XK_w)
        mapa->key_w = 1;
    else if (keysym == XK_s)
        mapa->key_s = 1;
    else if (keysym == XK_Left)
        mapa->key_left = 1;
    else if (keysym == XK_Right)
        mapa->key_right = 1;
    return (0);
}
Each key press sets a flag to 1, enabling continuous movement while the key is held down.

Key Release Handler

From utils7.c:62-77, when a key is released:
int key_release(int keysym, t_map *mapa)
{
    if (keysym == XK_a)
        mapa->key_a = 0;
    else if (keysym == XK_d)
        mapa->key_d = 0;
    else if (keysym == XK_w)
        mapa->key_w = 0;
    else if (keysym == XK_s)
        mapa->key_s = 0;
    else if (keysym == XK_Left)
        mapa->key_left = 0;
    else if (keysym == XK_Right)
        mapa->key_right = 0;
    return (0);
}
Key release sets the flag back to 0, stopping movement for that direction.

Event Hook Registration

From main.c:92-95, event hooks are registered on window initialization:
mlx_hook(mapa.win_ptr, 2, KeyPressMask, handle_key, &mapa);
mlx_hook(mapa.win_ptr, 3, KeyReleaseMask, key_release, &mapa);
mlx_hook(mapa.win_ptr, DestroyNotify, StructureNotifyMask,
    closewindow, &mapa);

Collision Detection

Movement includes wall collision detection to prevent walking through walls.

Wall Check Function

From utils6.c:15-27:
int is_wall(t_map *mapa, double pos_x, double pos_y)
{
    if (mapa->final_map[(int)(pos_y + 0.8) / SIZE][(int)(pos_x + 0.8)
            / SIZE] == '1' ||
            mapa->final_map[(int)(pos_y + 0.8) / SIZE][(int)(pos_x - 0.8)
            / SIZE] == '1' ||
            mapa->final_map[(int)(pos_y - 0.8) / SIZE][(int)(pos_x - 0.8)
            / SIZE] == '1' ||
            mapa->final_map[(int)(pos_y - 0.8) / SIZE][(int)(pos_x + 0.8)
            / SIZE] == '1')
        return (0);
    return (1);
}
This function checks four corners around the player position with a 0.8 pixel buffer to detect walls.

Collision Application

From utils6.c:51-56:
if (is_wall(mapa, mapa->new_x, mapa->new_y))
{
    mapa->xx = mapa->new_x;
    mapa->yy = mapa->new_y;
}
Movement is only applied if the destination position is not inside a wall. If a collision is detected, the player position remains unchanged.

Exit Controls

ESC Key

Pressing ESC immediately closes the window and exits:
if (keysym == XK_Escape)
    closewindow(mapa);

Window Close Button

Clicking the window’s close button (X) triggers the same cleanup:
mlx_hook(mapa.win_ptr, DestroyNotify, StructureNotifyMask,
    closewindow, &mapa);

Cleanup Process

Both exit methods call the same cleanup function from utils5.c:15-27:
int closewindow(t_map *mapa)
{
    mlx_destroy_image(mapa->mlx_ptr, mapa->img_ptr);
    mlx_destroy_image(mapa->mlx_ptr, mapa->tex_north.img_ptr);
    mlx_destroy_image(mapa->mlx_ptr, mapa->tex_south.img_ptr);
    mlx_destroy_image(mapa->mlx_ptr, mapa->tex_east.img_ptr);
    mlx_destroy_image(mapa->mlx_ptr, mapa->tex_west.img_ptr);
    mlx_destroy_window(mapa->mlx_ptr, mapa->win_ptr);
    mlx_destroy_display(mapa->mlx_ptr);
    freeall(mapa);
    free(mapa->mlx_ptr);
    exit(EXIT_SUCCESS);
}
All resources (images, textures, window, display connection, and memory) are properly freed before exit.

Initial Player Direction

The player’s starting direction is determined by the character in the map file:

Direction Mapping

From utils5.c:75-85:
void know_starting_angle(t_map *map, int i, int ii)
{
    if (map->toflood_map[i][ii] == 'S')
        map->starting_angle = PI / 2;
    else if (map->toflood_map[i][ii] == 'N')
        map->starting_angle = 3 * PI / 2;
    else if (map->toflood_map[i][ii] == 'E')
        map->starting_angle = 2 * PI;
    else if (map->toflood_map[i][ii] == 'W')
        map->starting_angle = PI;
}

N - North

Starting angle: 3π/2 radians (270°)Player faces upward (north) in the map

S - South

Starting angle: π/2 radians (90°)Player faces downward (south) in the map

E - East

Starting angle: 2π radians (360° / 0°)Player faces right (east) in the map

W - West

Starting angle: π radians (180°)Player faces left (west) in the map

Control Summary

Here’s a quick reference table for all controls:
KeyActionImplementation
WMove forwardmapa->key_w = 1
SMove backwardmapa->key_s = 1
AStrafe leftmapa->key_a = 1
DStrafe rightmapa->key_d = 1
Rotate leftmapa->key_left = 1
Rotate rightmapa->key_right = 1
ESCExit gameImmediate closewindow()

Tips for Smooth Movement

1

Combine movement keys

You can press multiple keys simultaneously for diagonal movement:
  • W + A = Forward-left diagonal
  • W + D = Forward-right diagonal
  • S + A = Backward-left diagonal
  • S + D = Backward-right diagonal
2

Strafe while rotating

Hold a strafe key (A or D) while rotating with arrow keys for smooth circular movement around objects.
3

Use backward for quick escapes

The S key allows backing away from walls while keeping them in view, useful for navigating tight corridors.

Next Steps

Now that you understand the controls, learn how to create your own custom maps:

Creating Maps

Learn the map file format and create custom levels