{"id":21031959,"url":"https://github.com/bima42/cub3d","last_synced_at":"2026-05-20T15:39:21.879Z","repository":{"id":40303105,"uuid":"483643574","full_name":"Bima42/cub3d","owner":"Bima42","description":"Implement raycasting in a video game using C","archived":false,"fork":false,"pushed_at":"2022-11-18T15:13:48.000Z","size":20651,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-20T15:32:07.045Z","etag":null,"topics":["c","raycasting","video-game"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Bima42.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-04-20T12:23:06.000Z","updated_at":"2022-11-13T22:34:33.000Z","dependencies_parsed_at":"2023-01-22T11:16:22.636Z","dependency_job_id":null,"html_url":"https://github.com/Bima42/cub3d","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bima42%2Fcub3d","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bima42%2Fcub3d/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bima42%2Fcub3d/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bima42%2Fcub3d/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Bima42","download_url":"https://codeload.github.com/Bima42/cub3d/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243473938,"owners_count":20296664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","raycasting","video-game"],"created_at":"2024-11-19T12:39:13.145Z","updated_at":"2025-12-28T15:57:34.622Z","avatar_url":"https://github.com/Bima42.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cub3d\n- [1. Get Started](#get-started)\n- [2. Resume](#resume)\n- [3. Parsing](#parsing)\n  - [3.1 Map file](#map-file)\n  - [3.2 Map](#map)\n  - [3.3 Map Checker](#map-checker)\n- [4. Raycasting](#raycasting)\n  - [4.1 Explanation](#explanation)\n  - [4.2 Horizontal Check](#horizontal-check)\n  - [4.3 Vertical Check](#vertical-check)\n  - [4.5 Digital Differential Analyzer](#digital-differential-analyzer)\n  - [4.4 Fisheye Effect](#fisheye-effect)\n- [5. Sources](#sources)\n\n# Get Started\n- Implement raycasting in a simple video game\n\n```\ngit clone git@github.com:Bima42/cub3d.git\n```\n```\nmake\n```\n```\n./cub3d maps/map.cub\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/assets/cub3d.gif\"\u003e\n\u003c/p\u003e\n\n- Use W, A, S, D key to move\n- Use left and right arrow to rotate camera\n- Esc quit the game\n\n- [FIND MY GENERATOR HERE](https://github.com/Bima42/cub3d_map_generator)\n\n# Resume\n- First part was the parsing\n- Then, we split works in two parts : initialisation part (graphics, mlx ...) and raycasting part\n\n# Parsing\n## Map file\n- Extension file is .cub only\n- Begin by texture path (N, S, E, W)\n- Then some rgb data for ceiling and floor\n- Empty lines can be found between those datas\n\n## Map\n- Must be surrounded by '1', filled by '0'\n- Only one player spawn (N, S, E or W indicate player orientation)\n- Q for exit map\n\n## Map Checker\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/assets/parsing_map.png\"\u003e\n\u003c/p\u003e\n\nFor every rows and colums (x and y) :\n- Start with skip white spaces\n- Then we have to hit a wall\n- We skip all char except wall (1) until white space or \\0\n- If the char in pos - 1 is not a wall, it's an error\n- The fact is if we hit a wall we have to found another one, walls works by pair. If the second wall was not hit, row (or column) is false\n\n_Example for the x axis_\n\n```c\nint\tcontrol_axis_x(char **map, int y, int max_y)\n{\n\tint\tx;\n\n\tx = 0;\n\tif (y == max_y)\n\t\treturn (1);\n\tif (map[y][x] != '\\0')\n\t{\n\t\tskip_white_space(map[y], \u0026x);\n\t\twhile (map[y][x] == WALL \u0026\u0026 map[y][x] != '\\0')\n\t\t{\n\t\t\twhile (map[y][x] != '\\0' \u0026\u0026 !is_w_space(map[y][x]))\n\t\t\t\tx++;\n\t\t\tif (map[y][x - 1] != WALL)\n\t\t\t\treturn (0);\n\t\t\tskip_white_space(map[y], \u0026x);\n\t\t}\n\t\tif (map[y][x] == '\\0')\n\t\t\treturn (control_axis_x(map, y + 1, max_y));\n\t\telse\n\t\t\treturn (0);\n\t}\n\treturn (0);\n}\n```\n- During this check, we extract the higher x and y to reformat_map, using white_spaces to reformat and put every lines at the same length.\n\n- Last line is filled with \\0.\n\t\n# Raycasting\n## Explanation\n- Transforming a limited form of data into a three-dimensional projection with the help of tracing rays from the view point into the viewing volume. \n- Rays can be cast and traced in groups based on certain geometric constraints.\n- A ray from the pixel through the camera is obtained and the intersection of all objects in the picture is computed.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/assets/raycasting.png\"\u003e\n\u003c/p\u003e\n\n- There some steps to cast rays:\n  - 1 : Based on the viewing angle, subtract 30 degrees (half of the FOV).\n  - 2 : Starting from column 0.\n  - 3 : Cast a ray.\n  - 4 : Trace the ray until it hits a wall.\n  - 5 : Record the distance to the wall (the distance = length of the ray).\n  - 6 : Add the angle increment so that the ray moves to the right\n  - 7 : Repeat steps from step 3 with a column+1. We gonna cast one ray per unit width\n\n- We have to create a maze world that has the following geometric constraints:\n  - Walls are always at 90° angle with the floor.\n  - Walls are made of cubes that have the same size.\n  - Floor is always flat.\n\n- Need to define some attributes :\n  - Player/viewer’s height, player’s field of view (FOV), and player’s position.\n  - Projection plane’s dimension.\n  - Relationship between player and projection plane.\n  - FOV of 60 is generally a good choice.\n  - Player height reprensent half of window height.\n\n```c\nvoid\traycasting(t_game *game)\n{\n\tgame-\u003ecolumn = 0;\n\tgame-\u003erays.ang = game-\u003ep.vis + FOV / 2;\n\twhile (game-\u003ecolumn \u003c WIN_W)\n\t{\n\t\tcheck_rays_angle(game);\n\t\thorizontal_raycasting(game);\n\t\tvertical_raycasting(game);\n\t\tdraw(game);\n\t\tgame-\u003erays.ang -= FOV / (double)WIN_W;\n\t\tgame-\u003ecolumn++;\n\t}\n}\n```\n- WARNING : You want to protect the player to go outside the 360 degrees available for rotation. \n```c\nvoid check_rays_angle(t_game *game)\n{\n\twhile (game-\u003erays.ang \u003e= 360)\n\t\tgame-\u003erays.ang -= 360;\n\twhile (game-\u003erays.ang \u003c 0)\n\t\tgame-\u003erays.ang += 360;\n}\n```\n\n- To ensure that each ray stops at the first wall touched, it is necessary to carry out a horizontal and vertical check\n\n## Horizontal Check\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/assets/horizontal_check.png\"\u003e\n\u003c/p\u003e\n\n```c\ndouble\thorizontal_raycasting(t_game *game)\n{\n\tgame-\u003erays.tan = tan(deg_to_rad(game-\u003erays.ang));\n\tif (game-\u003erays.ang \u003e EAST \u0026\u0026 game-\u003erays.ang \u003c WEST)\n\t{\n\t\tgame-\u003erays.step_y = -TILE;\n\t\tgame-\u003erays.hit_y = floor(game-\u003ep.y / TILE) * TILE - 0.00001;\n\t}\n\telse\n\t{\n\t\tgame-\u003erays.step_y = TILE;\n\t\tgame-\u003erays.hit_y = floor(game-\u003ep.y / TILE) * TILE + TILE;\n\t}\n\tgame-\u003erays.hit_x = game-\u003ep.x\n\t\t+ (game-\u003ep.y - game-\u003erays.hit_y) / game-\u003erays.tan;\n\tif (game-\u003erays.ang == NORTH || game-\u003erays.ang == SOUTH)\n\t\tgame-\u003erays.step_x = 0;\n\telse\n\t\tgame-\u003erays.step_x = TILE / game-\u003erays.tan;\n\tif (game-\u003erays.ang \u003e WEST)\n\t\tgame-\u003erays.step_x *= -1;\n\tdigital_differential_analyzer(game);\n\treturn (sqrt(square((game-\u003ep.x - game-\u003erays.hit_x))\n\t\t\t+ square((game-\u003ep.y - game-\u003erays.hit_y))));\n}\n```\n\n## Vertical Check\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/assets/vertical_check.png\"\u003e\n\u003c/p\u003e\n\n```c\ndouble\tvertical_raycasting(t_game *game)\n{\n\tgame-\u003erays.tan = tan(deg_to_rad(game-\u003erays.ang));\n\tif (game-\u003erays.ang \u003c NORTH || game-\u003erays.ang \u003e SOUTH)\n\t{\n\t\tgame-\u003erays.step_x = TILE;\n\t\tgame-\u003erays.hit_x = floor(game-\u003ep.x / TILE) * TILE + TILE;\n\t}\n\telse\n\t{\n\t\tgame-\u003erays.step_x = -TILE;\n\t\tgame-\u003erays.hit_x = floor(game-\u003ep.x / TILE) * TILE - 0.00001;\n\t}\n\tgame-\u003erays.hit_y = game-\u003ep.y\n\t\t+ (game-\u003ep.x - game-\u003erays.hit_x) * game-\u003erays.tan;\n\tif (game-\u003erays.ang == WEST || game-\u003erays.ang == EAST)\n\t\tgame-\u003erays.step_y = 0;\n\telse\n\t\tgame-\u003erays.step_y = TILE * (game-\u003erays.tan * -1);\n\tif (game-\u003erays.ang \u003e= NORTH \u0026\u0026 game-\u003erays.ang \u003c= SOUTH)\n\t\tgame-\u003erays.step_y *= -1;\n\tdigital_differential_analyzer(game);\n\treturn (sqrt(square((game-\u003ep.x - game-\u003erays.hit_x))\n\t\t\t+ square((game-\u003ep.y - game-\u003erays.hit_y))));\n}\n```\n## Digital Differential Analyzer\n- This algorithm is used to progress by step in each checker (vertical and horizontal)\n- We have to move for 1 Tile width for horizontal checker, and 1 Tile height for vertical checker\n\n```c\nvoid\tdigital_differential_analyzer(t_game *game)\n{\n\tgame-\u003emap_pos_x = game-\u003erays.hit_x / (int)TILE;\n\tgame-\u003emap_pos_y = game-\u003erays.hit_y / (int)TILE;\n\twhile (game-\u003emap_pos_x \u003e 0 \u0026\u0026 game-\u003emap_pos_x \u003c game-\u003emap_w\n\t\t\u0026\u0026 game-\u003emap_pos_y \u003e 0 \u0026\u0026 game-\u003emap_pos_y \u003c game-\u003emap_h\n\t\t\u0026\u0026 game-\u003emap[game-\u003emap_pos_y][game-\u003emap_pos_x] != '1')\n\t{\n\t\tif (game-\u003emap[game-\u003emap_pos_y][game-\u003emap_pos_x] == 'Q')\n\t\t{\n\t\t\tgame-\u003eflag_exit = 1;\n\t\t\tbreak ;\n\t\t}\n\t\tgame-\u003erays.hit_x += game-\u003erays.step_x;\n\t\tgame-\u003erays.hit_y += game-\u003erays.step_y;\n\t\tgame-\u003emap_pos_x = game-\u003erays.hit_x / (int)TILE;\n\t\tgame-\u003emap_pos_y = game-\u003erays.hit_y / (int)TILE;\n\t}\n}\n```\n- When horizontal and vertical intersections are merged, you have to check the lowest distance between player and wall using Pythagore theorem.\n- Now you know which side of the wall you have to drawn.\n\n## Fisheye effect\n- This way will show distortion called \"fish-eye\".\n- To remove the viewing distortion : ```correct_dist = distorted_dist * cos(angle);```\n\n# Sources\n- https://permadi.com/1996/05/ray-casting-tutorial-3/#CREATING%20A%20WORLD\n- https://guy-grave.developpez.com/tutoriels/jeux/doom-wolfenstein-raycasting/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbima42%2Fcub3d","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbima42%2Fcub3d","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbima42%2Fcub3d/lists"}