Skip to content

mbueno-g/cub3d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cub3d

📚 Introduction

📣 Rules

💥 New concept: Raycasting

🌈 Textures

🔥 Bonus

🕹️ Time to play

Introduction

The aim of the cub3D proyect is to create a 3D game using the raycasting technique which is a rendering method implemented in the world-famous Wolfenstein 3D game. This was a group proyect and I had the honor to repeat with @madebypixel02 :)

Rules

This 3D game must follow the following rules:

Files rules

  • The executable cub3D must receive at least one argument, a map.
  • The map is a .cub file
  • The first few lines must contain a direction, written as NO,SO,WE,EA, followed by a path to an .xpm image. If one of these directions appears more than once, the image or texture is updated if the new path is valid.
  • After the textures and an empty line, the RGB color for the floor and ceiling is shown as F or C followed by the RGB coordinates separated by commas.
  • Finally, the map is display.

Maps rules

  • These are the possible characters: (empty), 0 (floor), 1 (wall) and N,S, W, E (player's view direction). Additionally, for the bonus we have c (close door) and o (open door).
  • The map must be surrounded by walls, even the empty spaces.

So a valid map would be seen as this one:

NO textures/wall_1.xpm
NO textures/wall_1.xpm
SO textures/wall_2.xpm
WE textures/wall_3.xpm
EA textures/wall_3.xpm

F 255,113,39
C 51,198,227

11111111111111111111111111111
11111111110000000000000000011111
        10110000011100000000000111111
        100000000110000011111111111
11111111111111011100000010001
11111111000001111100000010001
       1000001 100000001100011111111
    11110000111101000N00000011111
11111111111111111111111111111

11111111111111111111111111111
10000000111100010000000001001
100000001  100110101110000001
10000000111110000001 10100001
11111111111111111111 11111111

Game rules

  • The W, A, S and D keys move up, down, left and right the player's point of view.
  • The left and right arrow keys rotate the field of view of the player.
  • Pressing ESC or the red cross on the window's frame must close the window and quit the program cleanly.

Additionally for the bonus part, if you press the E key in front of a door you can open or close it.

New concept

Raycasting

Raycasting is a rendering technique to create a 3D perspective in a 2D map. The logic behind RayCasting is to throw rays in the direction of the player view. Basically, we need to check the distance between the player and the nearest wall (i.e. the point the ray hits a wall) to caculate the height of the vertical lines we draw.

Screenshot 2022-02-15 at 23 07 13 Screenshot 2022-02-15 at 22 58 00

To calculate the distance between the player and the nearest wall, we can use the following algorithm:

  1. Initialize some basic attributes needed for the projection:
Attribute Description Value
FOV The field of view of the player Screenshot 2022-02-20 at 22 17 46 30º
Ray angle Angle of the player view's direction N (270º), S (90º), W (180º), E (0º)
Ray increment angle Angle difference between one ray and the next one 2 * HFOV / window_width
Precision 70
Limit Limit of the distance the player can view 11
Player's position Center of the square where the player is pl.x += 0.5 ; pl.y += 0.5
  1. From the the player's position, we move the ray forward incrementing the x's and y's coordinates of the ray.

Screenshot 2022-02-20 at 22 35 23

ray.x += ray_cos;
ray.y += ray_sin;

where ray_cos and ray_sin are of the form:

ray_cos = cos(degree_to_radians(ray_angle)) / g->ray.precision;
ray_sin = sin(degree_to_radians(ray_angle)) / g->ray.precision;
  1. Repeat step 2 until we reach the limit or we hit a wall.

  2. Calculate the distance between the player's and the ray's position using the euclidean distance:

distance = sqrt(powf(x - pl.x - 0.5, 2.) + powf(y - pl.y - 0.5, 2.));
  1. Fix fisheye
distance = distance * cos(degree_to_radians(ray_angle - g->ray.angle))

This algorith is repeated window_width times, i.e. in every iteration we increment the angle until we have been through all the field of view. This distance is really helpful to calculate the height of the wall height:

wall_height = (window_height / (1.5 * distance));

Textures

One last thing we need to do is to read pixels (x,y) from a texture image i to get the colors of the wall.

x = (int)(i->width * (g->x + g->y)) % i->width;
// y = image height
color = my_mlx_pixel_get(i, x, y);

Once we know the wall height and all the colors, we have everything we need to draw the ceiling, the wall and the floor.

  • Ceiling: from screen top to screen half height minus wall height
  • Wall: from half height minus wall height to half height plus wall height
  • Floor: from half height plus wall height to screen botton

Bonus

  • Wall collisions Once the player come accross a wall, instead of stopping in front of it, the player can move in the x or y direction.

  • Minimap As the name says, the minimap is a small 2D map of the maze that shows the players' path on the map.

  • Doors
    The doors appear in the map as a c for a close door and a o for an open door. If a ray in the field of view of the player hits a door, you can open or close it pressing E key.

  • Animations Our animated sprites are the walls. In order to do that, we read multiple textures path in the file and we save them in a linked list. Iterating over the list, the wall textures is changed for the next one.

  • Rotation with mouse
    There is an event on the minilibx library that tell us the position of the mouse. If the position changes, the field of view of the players changes accordingly.

Time to play

Here are a few samples of how our maps look

  • 1.cub

  • 2.cub

  • pac.cub

  • pac2.cub

To check some of our favorite layouts, see MAPS.md

Releases

No releases published

Packages

No packages published