Wolfenstein 3D, released on May 5th, 1992, established the First Person Shooter genre. The game design, powered by an engine enabling beautiful 256 color graphics, speed, high framerate, clever AI, crisp sound effects, and engaging music, was universally acclaimed.
A Cub3D a 1337's projects that aims to explore raycasting and making a dynamic view inside a maze, in which you’ll have to find your way.
1️⃣ Creating our window with :
WIN_W = 1920;
WIN_H = 1080;
mlx.window = mlx_new_window(wind.mlx, WIN_W, WIN_H, "Cub3D");
2️⃣ Getting the player position in the map and define the field of view based on the character inserted in the map :
if (map[y][x] == 'N')
fov = 90
else if (map[y][x] == 'S')
fov = 270
else if (map[y][x] == 'E')
fov = 360
else if (map[y][x] == 'W')
fov = 180
3️⃣ Starting to cast some rays :
Once we've got the player [y,x] so starting from his position we draw pixels as line we'll be using the minilibx function (mlx_pixel_put) to draw rays
The number of rays will be the width of the window is 1920
void player_view()
{
int i = -1;
double fov = the fov setted in the map - 30;
while (++i < nbr_of_rays)
{
cast_ray(fov);
fov += 0.0333;
}
}
0.0333 this number came from dividing our player angle 64 on window's witdh 1920, with it we ensure to cast every pixel in our angle
So as you know if our player has a 4 direction 2 vertical and 2 horizontal it's gonna be easy to implement his moves by simply increasing and decreasing his current positions [x,y],
But what if the player want to go to diffrent angle :
To go to that direction we need the intersection in term of the a-axis and the y-axis, here is a short refresher on a piece of high school math which forms the foundation of most calculations in the game : SOH-CAH-TOA
You can see it like this way the player moves up to the px then the py like this ^_^
As you know the raycasting doesn't draw the whole scene once each column represents a single ray cast out from the player at a particuler angle if the ray hits the wall we calcualted the distance
void cast_ray(double fov)
{
double px = player x position;
double py = player y position;
while (True)
{
// now we need to starting casting ray from the angle that the player see's
py += cos(fov) * distance between each ray;
px += sin(fov) * distance between each ray;
if (map[py][px] == '1'])
break ;
// which means that we've reached the wall, remembre we need that intersection for the 3d part
}
}
void cast_ray(double fov)
{
double px = player x position;
double py = player y position;
while (True)
{
py += cos(fov) * distance between each ray;
px += sin(fov) * distance between each ray;
// Protect you self (Seg Fault)
if (map[py][px] == '1'] || map[py + 1][px + 1] == '1'] || || map[py - 1][px - 1] == '1'])
break ;
mlx_pixel_put(wind->mlx, wind->window, px, py, HEX_Color);
}
}
4️⃣ For the key's part here's what you should do :
int keys()
{
if (KeyPressed = 124)
look_right();
if (KeyPressed = 123)
look_left();
if (KeyPressed = 2)
move_right();
if (KeyPressed = 0)
move_left();
if (KeyPressed = 13)
move_forward();
if (KeyPressed = 1)
move_backword();
}
5️⃣ Another challange we have is to set which direction the player sees of the Cub :
We already know that a cub has 4 interfaces (not included bottom and top)
anyway we need to each interfaces with the corrspondant color the process we'll be :
- Let's start by casting a single ray :
- We've got the player [y,x] and the end of ray [y,x] by a simple calculation we can remove 2 options as follows :
- So how to set directly which directions he sees we'll since each cub i've got i don't only have the position of the player and the end of ray in the map only but also with pixels, so as you know every cub tbc ...
6️⃣ 2d to 3d :
7️⃣ Adding Textures :
ℹ️ And if you wanna take you game to the next level to look like this
Here's your path
❓ How computer store and dispplay graphics ?
As we know every image divided into small squares called pixels and each pixel requires 1 bit of memory let's take a simple example :
each pixel has it's own color an example is that the top left has a green color in pixel so the computer define it with a numerical value and these numbers are called Pixel Values. These pixel values denote the intensity of the pixels, we have pixel values ranging from 0 to 255. The smaller numbers closer to zero represent the darker shade while the larger numbers closer to 255 represent the lighter or the white shade. This image is composed of many colors and almost all colors can be generated from the three primary colors ❤️ 💚 and 💙,
❓ Why mlx_pixel_put so slow ?
Because it tries to put pixel instantly on window without waiting for the frame to be rendered entirely
❓ So what's the solution ?
W need to buffer to put our pixels in an image then push it into the window , starting to casting rays with the help of mlx_pixel_put it turn up that it works but in the 3D part we're going to face a few problems becuase that latter it tries to put pixel instantly on window without waiting for the frame to be rendered entirely so the solution we'll be to buffer or to put our pixels in an image then push it into the window that's what my_mlx_pixel_put do :
-
Creating an image with the width and height:
mlx_new_image(mlx->mlx_ptr, height, width)
-
We need some informations about the created image, we'll be using
mlx_get_data_addr
as follows :for more informations about
mlx_get_data_addr
check it down below 🙂 -
We need to put pixels on top of our image :
we're using
my_mlx_pixel_put(&my_mlx, height, width++, hex_color)
The above fomula it's actually in my_mlx_pixel_put
: We'll discuss about it latter
-
After that fire up your
mlx_put_image_to_window
to put it on the windowsmlx_put_image_to_window(wind->mlx_ptr, wind->window, wind->image_created_before, starting_x, staring_y)
-
Then after every move clear your window with the help of
mlx_clear_window
and repeat again,mlx_clear_window(wind->mlx, wind->window);
❓ What an mlx image requires ?
We need to pass some pointers to mlx_pointer :
🔹 bits per pixel : pixels are basicly int (4 bytes)
❓ Why in my_mlx_pixel_put bpp it's 32 and not 24 ?
An RGBA color value is specified with: rgba(red, green, blue, alpha).
The alpha parameter is a number between 0.0 (fully transparent) and 1.0 (fully opaque).
❓ What does mlx_get_data_addr do ?
it provide us with information about the generated image, if it works as it should it should provide you with 3 informations :
🔹 bpp : how many bits required to express a color of a pixel
🔹 size_line : how many bytes required to store one horizontal line in memory
🔹 endian : techinque to store data (Hex Format) in your RAM
❓ What's endian means ?
Endianness is the representation of word of digital data in computer memory
❓ How to write pixels on top of image ?
So after creating your image, you'll need to call mlx_get_address to retrieve the memory address of that pixels ??, We pass the reference in order to set that current data address, now that we have created our image it's time to push them to the window with the help of mlx_put_image_to_window we can do it quickly.
❓ Does printf slow down my program ?
Absolutly yes I/O is always slow in comparison to straight computation how i don't know actually sorry but you can try to redirect output in shell from console to a file and also don't forget that scrolling is usually a killer
❓ Who's better float or double to work with ?
Double is more precise than float and can store 64 bits (double of the number of bits float can stor) Double is more precise and for storing large numbers, we prefer double over float
- Minilibx Library :
https://gontjarow.github.io/MiniLibX/
https://github.com/sejinpark12/MiniLibX_man_kor
- Raycasting
Very Very Verrrrrrry useful 👌 :
https://permadi.com/1996/05/ray-casting-tutorial-table-of-contents/
http://www.playfuljs.com/a-first-person-engine-in-265-lines/
This amazing book which explains every details in the game :
https://fabiensanglard.net/gebbwolf3d/
- Math
https://blog.prepscholar.com/unit-circle-chart-radians-degrees https://www.khanacademy.org/math/algebra2/x2ec2f6f830c9fb89:trig/x2ec2f6f830c9fb89:unit-circle/v/unit-circle-definition-of-trig-functions-1
- Understnading 3D:
https://www.youtube.com/watch?v=U0_ONQQ5ZNM
https://www.youtube.com/watch?v=wiYTxjJjfxs
- Raycasting visualiser:
https://editor.p5js.org/KernelOverseer/full/Ilst053ZC
bitwise operator is much faster than traditional computation