Présentation

cub3D est un projet 42 qui consiste à recréer un rendu 3D “à l’ancienne” à partir d’une carte 2D, en implémentant un pipeline complet : parsing de fichier .cub, validation de map, gestion du joueur, DDA / raycasting, textures, interface, et boucle de jeu.

Ce dépôt va plus loin qu’un rendu minimal : le moteur intègre des éléments de gameplay (portes, sprites, ennemis) et des fonctionnalités techniques orientées performance/qualité (multi‑threading optionnel, post‑process vignette/blur, anti‑aliasing configurable, audio via OpenAL Soft).

Stack technique

  • Langage : C
  • Graphique : MiniLibX (backend macOS OpenGL / Linux X11)
  • Audio : OpenAL Soft + chargement WAV via un wrapper interne autour de dr_wav
  • Build : make (Linux/macOS)

Fonctionnalités principales

  • Raycasting DDA : rendu 3D temps réel depuis une grille 2D
  • Textures : murs (NO/SO/WE/EA), sol/plafond (couleur ou texture), portes/sprites/ennemis via identifiants
  • Contrôles joueur : déplacement (WASD / flèches) + actions (interaction porte)
  • Minimap (toggle)
  • Post‑process vignette/blur : réglage en runtime (radius/gradient), optimisé (cache + dirty flag)
  • Anti‑aliasing : activation + réglages (intensity)
  • Mirrors : surfaces miroir (réflexions limitées (configurable))
  • Audio : musique/ambiance + SFX (structure de dossier attendue configurable via la map)
  • Menus / state machine : séparation des états (menu, options, jeu)

Format de map (aperçu)

Le moteur charge un fichier .cub qui contient :

  • Des lignes de configuration (textures/couleurs, assets optionnels)
  • Puis la grille (map) composée de caractères (murs, sol, spawn, portes, etc.)

Exemples d’identifiants de config présents dans le projet :

  • Murs : NO, SO, WE, EA → chemins .xpm
  • Sol / plafond : F et CR,G,B ou chemin de texture (./...)
  • Porte : D → texture de porte
  • Ennemi : X → texture d’ennemi
  • Sprites : 2..9 → textures associées à un id
  • Audio : A → chemin vers un dossier de sons

Objectif

Le projet met en avant des compétences “systèmes” et “moteur” : parsing robuste, validation, gestion mémoire, performance (optimisations ciblées), architecture modulaire, et intégration multi‑plateforme.

Compilation

make

Binaire généré : ./cub3D

Lancement

./cub3D maps/good/subject_map.cub

Contrôles (clavier)

  • Quitter : ESC
  • Déplacement : W A S D (ou flèches ↑ ↓ ← →)
  • Interaction porte : E
  • Sprint / action (selon config) : SHIFT

Toggles & réglages runtime

  • Afficher/masquer minimap : M
  • Ouvrir/fermer le menu TAB (overlay options) : TAB
  • Multi‑threading (rendu) : H (toggle)
  • Anti‑aliasing :
  • Activer/désactiver : K
  • Intensité : L
  • Vignette/blur (post-process) :
  • Gradient : 7 (−) / 8 (+)
  • Rayon : 9 (−) / 0 (+)

Audio (runtime)

  • Play/Pause (source principale) : P
  • Enable global audio : O

Format .cub (structure)

Un fichier .cub est composé de :

1) Bloc configuration (textures / couleurs / assets)
2) Grille (la map), ligne par ligne

Identifiants de configuration supportés

  • Textures de murs :
  • NO <path> (North), SO <path> (South), WE <path> (West), EA <path> (East)
  • Extension attendue : .xpm

  • Sol / plafond :

  • F <R,G,B> ou F <./path_to_texture.xpm>
  • C <R,G,B> ou C <./path_to_texture.xpm>

  • Porte : D <./path_to_door_texture.xpm>

  • Ennemi (texture) : X <./path_to_enemy_texture.xpm>

  • Sprites (textures par id) :

  • 2 <./path.xpm>9 <./path.xpm>

  • Audio (optionnel) :

  • A <./path_to_sound_folder>

Caractères de la grille (map)

  • 1 : mur
  • 0 : sol / case vide
  • N, S, E, W : position + orientation de spawn du joueur
  • D : porte
  • M : miroir
  • 2..9 : sprite (id)
  • X : ennemi (spawn)
  • (espace) : padding/void (utilisé dans certaines maps)

Vue d’ensemble

Le projet est organisé en modules clairs :

  • src/01_parser/ : chargement .cub, validation, construction de la grille, init joueur
  • src/02_state_machine/ : gestion des écrans/états (menu, options, jeu)
  • src/03_game_engine/ : moteur (raycasting, rendu, minimap, post-process, miroir, audio “in-game”)
  • src/00_init/ : bootstrap (init data, init mlx, events, textures, audio, cleanup)
  • includes/ : headers, types, keycodes, config
  • libs/ : libft, dépendances audio, helpers mouvement, etc.

Pipeline “chargement → jeu”

  1. Init : allocations + init MLX + init systèmes (textures/audio) + hooks clavier/souris
  2. Parsing :
    - lecture du fichier .cub
    - parsing des identifiants (textures/couleurs/portes/sprites/ennemi, audio)
    - parsing de la grille et validation (cohérence + fermeture de la map, flood fill)
  3. Initialisation gameplay :
    - spawn joueur (position/orientation)
    - instanciation objets (portes, sprites, ennemis)
  4. Boucle de rendu :
    - calcul caméra + inputs
    - raycasting (DDA) → distances, faces, sampling textures
    - rendu sol/plafond, objets, overlays (minimap, menu TAB)
    - post-process (vignette/blur) + AA (si activés)

Systèmes notables

  • Raycasting DDA : cœur du rendu, avec option de rendu multi-thread (toggle)
  • Miroir : réflexion de rayons avec limite de rebonds (MAX_REFLECTIONS)
  • Vignette/blur : cache + dirty flag + optimisations (lookup, merge par blocs)
  • Audio : abstraction t_audio_system (OpenAL device/context, buffers, sources)

1) Compiler et lancer une map existante

make
./cub3D maps/good/dungeon.cub

Si vous voulez une map “baseline” simple :

./cub3D maps/good/subject_map.cub

2) Créer une map minimale .cub

Créez par exemple maps/my_map.cub :

NO ./textures/test/north.xpm
SO ./textures/test/south.xpm
WE ./textures/test/west.xpm
EA ./textures/test/east.xpm

F 220,100,0
C 225,30,0

111111
100001
10N001
100001
111111

Règles de base :

  • exactement un spawn joueur (N/S/E/W)
  • la map doit être fermée (pas de “trou” vers l’extérieur)
  • murs obligatoires : NO/SO/WE/EA
  • sol/plafond : couleur ou texture (au moins une définition valide pour F et C)

3) Ajouter portes / sprites / miroir

Dans le bloc config :

D ./assets/castle/wooddoor.xpm
2 ./assets/castle/firecamp.xpm
X ./assets/castle/enemy.xpm

Dans la grille :

  • placez D pour une porte (ouvrable avec E)
  • placez 2..9 pour un sprite
  • placez M pour des surfaces miroir
  • placez X pour un ennemi

4) Activer l’audio via la map

Ajoutez une ligne A dans le bloc config :

A ./assets/sounds/test

Le dossier doit respecter une arborescence attendue (porte, mob, player + WAV). Si elle n’est pas respectée, le jeu bascule en mode “sans son”.

5) Arborescence audio obligatoire (tree)

Le projet attend une structure de dossier précise pour charger automatiquement les WAV (SFX + musiques). Exemple (identique à celle attendue par le parser) :

.
├── door
│   ├── Close_door.wav
│   └── Open_door.wav
├── Env.wav
├── Game_over.wav
├── Menu_effect.wav
├── Menu_exit.wav
├── Menu_option.wav
├── Menu_start_game.wav
├── Menu.wav
├── mob
│   ├── Mob_footsteps.wav
│   └── Mob_voice.wav
└── player
  ├── Breating_fast.wav
  ├── Breating_medium.wav
  ├── Breating_slow.wav
  ├── footsteps
  │   ├── Player_footsteps_1.wav
  │   ├── Player_footsteps_2.wav
  │   ├── Player_footsteps_3.wav
  │   ├── Player_footsteps_4.wav
  │   ├── Player_footsteps_5.wav
  │   ├── Player_footsteps_6.wav
  │   ├── Player_footsteps_7.wav
  │   └── Player_footsteps_8.wav
  ├── Heart.wav
  └── Wall_collision.wav

Pourquoi cette structure est imposée ?

  • Simplicité de parsing : au lieu de parcourir récursivement un dossier, de gérer des variantes de noms, ou de maintenir une table de correspondance, le loader sait exactement quels fichiers ouvrir.
  • Moins de cas d’erreur : pas d’ambiguïté (ex: deux fichiers candidats pour le même son), et des messages d’erreur plus clairs si un fichier manque.
  • Code plus léger et robuste : logique déterministe, sans dépendre d’un “scan filesystem” complexe (utile dans un projet C avec contraintes de temps/architecture).

Pourquoi un config.h ?

Le fichier includes/config.h centralise des paramètres compile-time (macros) pour éviter des “magic numbers” dispersés : résolution, vitesses, options de rendu, minimap, portes, miroirs, et une partie de la configuration audio.

L’avantage : on peut ajuster rapidement le comportement du moteur sans toucher aux fichiers du game loop.

Comment modifier la config (2 méthodes)

Méthode A — modifier directement includes/config.h

Simple pour un usage local.

Méthode B — surcharger au moment de la compilation (recommandé)

Beaucoup de macros sont protégées par #ifndef ... #define ... #endif, donc vous pouvez les override via des flags -D.

Exemples :

make fclean
make CFLAGS="-DWIN_WIDTH=1920 -DWIN_HEIGHT=1080 -DRENDER_THREADS=8 -DMOUSE_SENSITIVITY=0.03"

Réglages utiles (extraits)

Fenêtre & rendu

  • WIN_WIDTH, WIN_HEIGHT : résolution (par défaut 1280×720)
  • RENDER_THREADS : nombre de threads pour le rendu (par défaut 8)
  • FPS_MAX : cap FPS (défini à 20)

Déplacements & caméra

  • MOVE_SPEED : vitesse de déplacement
  • ROT_SPEED : vitesse de rotation
  • SPRINT_FACTOR : multiplicateur sprint
  • MOUSE_SENSITIVITY : sensibilité souris
  • TILE_SIZE : taille logique d’une case

Miroirs

  • MAX_REFLECTIONS : limite de rebonds (attention : valeur élevée = coût CPU)
  • MIRROR_EPSILON : epsilon anti “self-hit” lors de la réflexion

Portes

  • MAX_DOORS : max portes
  • DOOR_ANIM_STEP, DOOR_OPEN_SPEED : paramètres d’animation
  • DOOR_OPEN_TIME, DOOR_CLOSE_DELAY : timings
  • DEFAULT_DOOR_TYPE : type par défaut

Minimap

Macros de taille/couleurs : MINIMAP_SCALE, MINIMAP_SIZE, MINIMAP_RADIUS, etc.

Anti-aliasing

  • AA_INTENSITY_DEFAULT
  • AA_MERGE_MAX

Audio (volumes + limites)

  • Limites : MAX_AUDIO_SOURCES, MAX_AUDIO_BUFFER
  • Volumes (exemples) : VOLUME_MENU, VOLUME_GAME_AMBIENT, VOLUME_OPENING_DOOR, VOLUME_MOB_CRY, etc.
  • SLEEP_EXIT : durée (microsecondes) liée au son de sortie

Activer / régler les logs debug

Le fichier includes/debug.h expose des flags compile-time :

  • DEBUG_MODE : active le mode debug global
  • DEBUG_PARSER, DEBUG_POS, DEBUG_KEY, DEBUG_AA : logs ciblés

Exemple :

make fclean
make CFLAGS="-DDEBUG_MODE=1 -DDEBUG_PARSER=1 -DDEBUG_KEY=1"