- Project Overview
- Summary
- Project Goals
- Project Requirements
- Screenshots and Demos of our project
- Team Development Steps
- Project Structure
- I. Project Setup
- II. Parsing Configuration File
- III. Initialize Graphics
- Resources
Welcome to our cub3d project! As a team, we'll be diving into the exciting world of raycasting and basic 3D graphics programming. Our main goal is to create a simple 3D maze game from scratch using raycasting techniques.
This project is inspired by the world-famous Wolfenstein 3D game, which was the first FPS ever. It is a remarkable technique to explore, and a great opportunity to create a game with a good design and a nice user interface.
Developed by id Software and published by Apogee Software, Wolfenstein 3D was released in 1992. It was a revolutionary game that popularized the FPS genre and helped establish the PC as a gaming platform.
- Raycasting Engine: Develop a raycasting engine to render a 3D world from a first-person perspective.
- Parsing Configuration File: Read and parse a configuration file (.cub) to define our game settings, including map layout, textures, and other parameters.
- Texture Mapping: Apply textures to walls and potentially the floor and ceiling to enhance our game's visual appearance.
- Player Movement and Controls: Implement controls to allow the player to move within the 3D world, including basic movement (e.g., forward, backward, strafing) and rotation.
- Basic Game Mechanics: Create a playable game where the player can navigate through a maze-like environment, interact with objects, and complete objectives.
- Error Handling and Optimization: Implement robust error handling to handle invalid input and prevent crashes. Optimize our code for performance to ensure smooth gameplay.
- Language: We'll be using C for this project.
- Libraries: We can use standard libraries like
math
. External libraries likeminilibx
are allowed for graphics rendering. - Map Configuration: Our game should read a map configuration file (.cub) that defines game settings, including map layout, textures, colors, and more.
- Graphics: The game must render a 3D perspective view of the game world using raycasting techniques. Textures should be applied to walls to enhance the visual appearance.
- Controls: Implement controls to allow the player to move and navigate within the 3D world using keyboard input.
- Bonus Features: While not required, implementing additional features like a minimap, HUD (Heads-Up Display), or more complex game mechanics can earn us extra points.
freecompress-Screen.Recording.2024-05-10.at.3.12.45.PM.mp4
As a team, we'll be tackling the cub3d project in a systematic manner to ensure a successful outcome. Here's our proposed development roadmap:
Set up the project repository, establish communication channels, and assign roles.
- Create Repository: Set up a Git repository for the project.
- Establish Communication: Choose communication tools (Discord) for team collaboration.
- Assign Roles: Define roles and responsibilities for each team member.
Implement the code to read and parse the configuration file (.cub) to extract game settings.
- Read File: Open and read the .cub configuration file.
- Validate Data: Check for valid map settings, resolution, textures, etc.
- Parse Data: Extract and store relevant information from the configuration file.
Set up the graphics rendering engine using the minilibx
library.
- Initialize Minilibx: Set up the Minilibx library for graphics rendering.
- Create Window: Create a window for rendering the game.
- Setup Buffer: Set up a buffer for rendering pixels to the window.
Develop the raycasting engine to render a 3D perspective view of the game world.
- Initialize Ray: Start with the player's position and direction.
- Cast Rays: Cast rays from the player's position to calculate distances to walls.
- Calculate Wall Strips: Determine the height and texture of each wall strip to draw.
Apply textures to walls to enhance the visual appearance of the game.
- Load Textures: Load wall and sprite textures from files.
- Implement Texture Mapping: Apply textures to the rendered walls.
Implement controls to allow the player to move and navigate within the 3D world.
- Handle Input: Implement controls for player movement (e.g., WASD for movement, arrow keys for rotation).
- Update Player Position: Update the player's position based on input and collision detection.
Render the game world using the raycasting results and textures.
- Draw Walls: Render the walls of the 3D world using the raycasting results.
- Draw Floor and Ceiling: Render the floor and ceiling of the 3D world.
- Draw Sprites: Implement sprite rendering if necessary.
Implement error handling to prevent crashes and ensure a smooth user experience.
- Error Checks: Implement error handling to catch and handle any runtime errors.
- Memory Cleanup: Free allocated memory and resources.
- Close Window: Properly close the graphics window when exiting the game.
Optimize the code for performance and refactor as needed for better readability.
- Optimize Raycasting: Improve raycasting performance if necessary.
- Refactor Code: Clean up and organize the code for better readability and maintainability.
Implement additional features to enhance the game experience and earn extra points.
- Add Features: Implement additional features like a minimap, HUD, or more complex game mechanics.
- Testing: Thoroughly test the game to ensure all features work as expected.
The project will be structured as follows:
cub3d/
│
└── src # Source code files
│ ├── parsing # Functions for parsing the configuration file
│ │ ├── color
│ │ ├── config
│ │ ├── map
│ │ └── texture
│ ├── rendering # Functions for rendering the 3D world
│ └── utils # Utility functions
│
├── includes # Header files
│ ├── cub3d.h
│ └── engine.h
│
├── assets/
│ ├── textures/ # Directory for storing texture files (.xpm or .png)
│ └── maps/ # Directory for storing map configuration files (.cub)
│
├── libft/ # If you're using your own libft library
│ ├── libft.a # Compiled library
│ └── includes/ # Header files for libft
│
└── Makefile # Makefile for compiling the project
In this structure:
- src/: Contains the source code files for the project.
- parsing/: Functions for parsing the configuration file.
- color/: Functions for parsing and validating color settings.
- config/: Functions for parsing and validating configuration settings.
- map/: Functions for parsing and validating map settings.
- texture/: Functions for parsing and validating texture settings.
- rendering/: Functions for rendering the 3D world.
- utils/: Utility functions for common tasks.
- parsing/: Functions for parsing the configuration file.
- includes/: Contains header files for the project.
- cub3d.h: Main header file for the project.
- engine.h: Header file for the graphics rendering engine.
- assets/: Directory for storing texture files (.xpm or .png) and map configuration files (.cub).
- textures/: Directory for storing texture files.
- maps/: Directory for storing map configuration files.
- libft/: If you're using your own libft library, this directory contains the compiled library and header files.
- Makefile: Makefile for compiling the project.
Create the project directory structure and necessary files.
mkdir -p cub3d/src/main cub3d/src/parsing cub3d/src/rendering cub3d/src/utils
mkdir -p cub3d/includes cub3d/assets/textures cub3d/assets/maps
Create the main source files and header file for the project.
touch cub3d/src/main/main.c
touch cub3d/src/parsing/parse.c
touch cub3d/src/rendering/render.c
touch cub3d/src/utils/utils.c
touch cub3d/includes/cub3d.h
touch cub3d/assets/textures/.gitkeep
touch cub3d/assets/maps/.gitkeep
touch cub3d/Makefile
The first step in our project is to read and parse the configuration file (.cub) that defines our game settings. The configuration file contains information about the map layout, textures, colors, resolution, and other parameters needed to set up the game environment.
Here's an example of a simple configuration file:
NO ./assets/textures/north.png
SO ./assets/textures/south.png
WE ./assets/textures/west.png
EA ./assets/textures/east.png
F 220,100,0
C 0,0,100
1111111 111111111
110000011 10000000001
100000001 1000000000001
1000001 100000000000001
11011 1000000000000001
101 1000000100000001
101 100001 100000001
101111111111100000100000001
10000000000000000000000001
1011111111111000000000001
101 100000000001
101 100000000001
11011 100000000001
1000001 1000000N0001
100000001 10000000001
110000011 1000000001
1111111 111111111
In this example, the configuration file specifies the following settings:
- Textures:
- North wall texture:
./assets/textures/north.png
- South wall texture:
./assets/textures/south.png
- West wall texture:
./assets/textures/west.png
- East wall texture:
./assets/textures/east.png
- North wall texture:
- Floor Color: RGB value (220,100,0)
- Ceiling Color: RGB value (0,0,100)
- Map Layout:
1
: Wall0
: Empty spaceN
,S
,W
,E
: Player starting position and direction
Our task is to read and parse this configuration file to extract these settings and use them to set up the game environment.
To achieve this, we'll need to implement functions to:
- Open and read the configuration file.
- Parse the data to extract relevant settings.
- Validate the data to ensure it's in the correct format.
- Store the settings in appropriate data structures for later use.
- Map Elements: The map must contain only the following elements:
- 0: Empty space
- 1: Wall
- Map Shape: The map must be surrounded by walls (1) on all sides. The map's shape can be a rectangle, but it can also have holes inside it.
- Valid Characters: Only the map elements mentioned above and spaces are valid characters in the map. Any other character is considered invalid.
- Starting Position: The map must have a starting position for the player. This starting position must be represented by one of the following characters:
- N: North
- S: South
- W: West
- E: East
To parse the configuration file, we'll need to:
- Read the File: Open and read the configuration file line by line.
- Parse the Data: Extract relevant settings from each line of the file.
- Store the Data: Store the settings in appropriate data structures for later use.
Here's a basic outline of the steps involved in parsing the configuration file:
- Open File: Open the configuration file for reading using the
open
system call. - Read Lines: Read each line of the file using the
get_next_line
function. - Parse Data: Parse the data from each line to extract relevant settings (e.g., textures, colors, map layout).
To store the configuration settings, we'll define a t_config
structure to hold the extracted data:
typedef struct s_config
{
char *no_texture;
char *we_texture;
char *so_texture;
char *ea_texture;
int floor_color;
int ceiling_color;
char **map;
char **map_copy;
size_t map_width;
size_t map_height;
int map_started;
} t_config;
The t_config
structure contains fields to store the configuration settings extracted from the configuration file. We'll use this structure to store the textures, colors, and map layout.
└── src
├── parsing
│ ├── color
│ │ ├── color_parsing.c
│ │ └── color_validation.c
│ ├── config
│ │ ├── config_file_parsing.c
│ │ └── config_validation.c
│ ├── map
│ │ ├── map_parsing.c
│ │ └── map_validation.c
│ └── texture
│ ├── texture_parsing.c
│ └── texture_validation.c
The color
, config
, map
, and texture
directories contain functions for parsing and validating the color, configuration, map, and texture settings, respectively. Each directory contains two files: one for parsing the data and one for validating it.
By parsing and storing the configuration file, we'll be able to extract the game settings needed to set up the game environment.
The next step in our project is to initialize the graphics rendering engine using the minilibx
library. The Minilibx library is a simple graphics library that provides functions for creating windows, drawing pixels, and handling user input.
To set up the graphics rendering engine, we'll need to:
- Initialize the Minilibx library.
- Create a window for rendering the game.
- Set up a buffer for rendering pixels to the window.
- Handle user input for player controls.
Here's a basic outline of the steps involved in setting up the graphics rendering engine:
- Initialize Minilibx: Start by initializing the Minilibx library to set up the graphics environment.
- Create Window: Create a window for rendering the game using the
mlx_new_window
function. - Setup Buffer: Set up a buffer for rendering pixels to the window using the
mlx_new_image
function. - Handle User Input: Implement functions to handle user input for player controls (e.g., movement, rotation).
By setting up the graphics rendering engine, we'll be able to display the game world and interact with it using player controls.
- Wolfenstein 3D
- How do games render their scenes? | Bitwise
- How do Video Game Graphics Work?
- How Rendering Graphics Works in Games!
Trigonometry: Essential for dealing with angles and rotations in 3D space. You'll frequently use trigonometric functions like sine, cosine, and tangent to calculate positions and angles.
Trigonometry is a branch of mathematics that deals with the relationships between the sides and angles of triangles. It is essential for understanding 3D graphics programming, as it helps you calculate positions, rotations, and other transformations in 3D space.
- Trigonometry for Games (Making a Homing Rocket)
- Trigonometry - Easy to understand 3D animation
- Trigonometry in Game Development
- Trigonometry | Gamedev Math
- So how does your computer ACTUALLY compute sine? Basics of trig and more…
- Trigonometry • Math for Game Devs [Part 3]
- Trigonometry - Khan Academy
- Trigonometry - Math is Fun
- من أين جاءت النسب المثلثية وما أهميتها؟
linear algebra: Linear algebra is a branch of mathematics that deals with vectors, matrices, and linear transformations. It is essential for understanding 3D graphics programming, as it helps you represent and manipulate objects in 3D space.
Vectors and Matrices: Vectors and matrices are fundamental concepts in 3D graphics programming. You'll use them to represent positions, directions, transformations, and more.
Vector and Matrix is a very important concept in computer graphics. It is used to represent the position, direction, and transformation of objects in 3D space. It is also used to represent the color of the object, the texture of the object, and the light source in the scene.
- Game Math Theory - VECTORS
- Vectors and Matrices - 3Blue1Brown
- Vectors and Matrices - Khan Academy
- Vectors and Matrices - Math is Fun
- Vectors and Matrices in Game Development
- Vectors and Matrices | Gamedev Math
- Linear Algebra - Matrix Transformations
Geometry: Geometry is the branch of mathematics that deals with shapes, sizes, and properties of space. It is essential for understanding 3D graphics programming, as it helps you calculate positions, angles, and distances in 3D space.
Calculus: Calculus is a branch of mathematics that deals with rates of change and accumulation. It is essential for understanding 3D graphics programming, as it helps you calculate velocities, accelerations, and other dynamic properties of objects in 3D space.
Computer graphics is a field of study that focuses on the creation, manipulation, and rendering of images using computers. It is essential for understanding how to create 2D and 3D graphics, animations, and visual effects.
- History of computer graphics with Aymane Biri | BlaBlaConf 2021
- Computer Graphics - Scratchapixel
- Computer Graphics from Scratch
- Quick Understanding of Homogeneous Coordinates for Computer Graphics
- What Is A Graphics Programmer?
- How do Video Game Graphics Work?
Raycasting is a rendering technique used to create a 3D perspective view of a scene from a 2D map. It is commonly used in video games to simulate 3D environments using 2D graphics.
- Raycasting
- Super Fast Ray Casting in Tiled Worlds using DDA
- RayCasting Tutorials by Lode Vandevenne
- Matt Godbolt - Wolfenstein 3D's map renderer ⭐
- 3DSage - Make Your Own Raycaster - Part 1
- 3DSage - Make Your Own Raycaster - Part 2
- 3DSage - Make Your Own Raycaster - Part 3
- Pikuma - Raycasting
- Ray-Casting Tutorial by F. Permadi