Couverture de livre électronique gratuite Programmation de jeux : concevoir un game design document (GDD) pour un petit jeu

Programmation de jeux : concevoir un game design document (GDD) pour un petit jeu

Nouveau cours

12 pages

Spécifications techniques minimales et règles d’implémentation du jeu dans le GDD

Capítulo 10

Temps de lecture estimé : 8 minutes

+ Exercice

Objectif : transformer le design en contraintes techniques minimales

Cette section du GDD fixe les spécifications techniques minimales nécessaires pour implémenter le jeu de façon cohérente et testable. Elle sert de pont entre les intentions de design et la réalité de production : quels systèmes doivent exister, quelles données ils consomment, quels événements ils émettent, et quelles valeurs doivent être configurables pour faciliter l’équilibrage sans modifier le code.

Principes à respecter

  • Déterminisme fonctionnel : à entrée identique, le résultat doit être reproductible (utile pour debug et tests).
  • Données séparées du code : les paramètres de gameplay (vitesses, dégâts, timers, etc.) vivent dans un fichier/asset de données.
  • Contrats clairs : chaque système expose des champs et événements attendus (contrat de données).
  • Minimum viable : on spécifie le nécessaire (pas d’architecture “parfaite”), mais on évite les impasses (ex. sauvegarde extensible).

Système de scènes (niveaux, menus, transitions)

Contraintes techniques

  • Le jeu est découpé en scènes : MainMenu, Options, Level_X, GameOver, Victory (ou équivalents).
  • Un SceneManager central gère : chargement, déchargement, transitions, et passage de paramètres (ex. identifiant de niveau, point d’apparition).
  • Les transitions doivent être non bloquantes (écran de chargement simple ou fondu) si le moteur le nécessite.

Règles d’implémentation

  1. Définir une liste de scènes et leurs rôles (menu, gameplay, fin).
  2. Définir un identifiant unique par niveau : levelId.
  3. Définir un point d’entrée standard pour un niveau : SpawnPointId (par défaut start).
  4. Définir les transitions autorisées (ex. menu → niveau, niveau → game over, niveau → victoire).

Événements clés (scènes)

  • OnSceneLoadStart(sceneId)
  • OnSceneLoaded(sceneId)
  • OnSceneUnloadStart(sceneId)
  • OnSceneUnloaded(sceneId)

Gestion des entrées (input) : actions, mapping, priorités

Concept

On ne code pas “touche X” dans la logique gameplay. On code des actions (Sauter, Attaquer, Pause) et on mappe ces actions à des entrées (clavier/manette). Cela rend le jeu configurable et testable.

Contraintes techniques

  • Un InputManager expose des actions : Move (axe), Jump, Attack, Interact, Pause.
  • Gestion de contexte : gameplay vs UI (quand un menu est ouvert, les actions gameplay sont ignorées).
  • Optionnel minimal : remappage (si prévu), sinon mapping fixe documenté.

Étapes pratiques

  1. Écrire la liste des actions (nom stable) et leur type : bouton, axe, maintien.
  2. Définir les règles de répétition : ex. Jump déclenché sur “press”, pas sur “hold”.
  3. Définir la priorité : UI > Pause > Gameplay.
  4. Documenter les valeurs normalisées : Move.x ∈ [-1, 1].
ActionTypeContexteNotes
MoveAxeGameplayNormalisé, lissage optionnel
JumpBoutonGameplayDéclenchement sur appui
AttackBoutonGameplayCooldown géré côté gameplay
PauseBoutonGlobalOuvre/ferme menu pause

Sauvegarde simple : progression et options

Concept

La sauvegarde minimale couvre : (1) la progression (niveau débloqué, checkpoints) et (2) les options (volume, sensibilité, affichage). Elle doit être robuste aux mises à jour (versionnement).

Contraintes techniques

  • Un SaveManager sérialise un objet de données (JSON/binaire selon moteur) dans un emplacement persistant.
  • Le fichier de sauvegarde contient un champ saveVersion pour migration simple.
  • La sauvegarde est déclenchée sur événements : checkpoint atteint, fin de niveau, changement d’options.

Étapes pratiques

  1. Définir le schéma de données (voir “Contrat de données”).
  2. Implémenter Load() au lancement (menu) et ApplyOptions().
  3. Implémenter SaveProgress() sur OnCheckpointReached et OnLevelCompleted.
  4. Prévoir une valeur par défaut si aucune sauvegarde n’existe.

Événements clés (sauvegarde)

  • OnOptionsChanged(options) → sauvegarde immédiate
  • OnCheckpointReached(levelId, checkpointId) → sauvegarde progression
  • OnLevelCompleted(levelId) → sauvegarde progression + déblocage

Gestion des collisions : couches, règles et réponses

Concept

Les collisions doivent être prévisibles : on définit des couches (player, enemy, world, projectile, pickup) et des règles d’interaction. La réponse à collision (bloquer, déclencher, ignorer) est spécifiée dans le GDD pour éviter les incohérences.

Contraintes techniques

  • Définir une matrice de collision (qui collide avec qui).
  • Différencier collision physique (sol/murs) et trigger (zones de dégâts, pickups, checkpoints).
  • Les dégâts ne sont pas “dans la collision” : la collision déclenche un événement, le système de combat applique les règles (invincibilité, knockback, etc.).

Matrice minimale (exemple)

De \ VersWorldPlayerEnemyProjectilePickup
PlayerBloqueTrigger (dégâts)Trigger (dégâts)Trigger (ramasser)
EnemyBloqueTrigger (dégâts)Bloque/IgnoreTrigger (dégâts)Ignore
ProjectileTrigger (impact)TriggerTriggerIgnoreIgnore

Événements clés (collisions)

  • OnHit(attackerId, targetId, hitData)
  • OnPickupCollected(pickupId, collectorId)
  • OnEnteredTrigger(triggerId, entityId)

États (state machine) : joueur, ennemis, jeu

Concept

Les états évitent les “if” contradictoires. On définit des machines à états au minimum pour : (1) l’état global du jeu, (2) le joueur, (3) chaque ennemi.

Continuez dans notre application.

Vous pouvez écouter le livre audio écran éteint, recevoir un certificat gratuit pour ce cours et accéder également à 5 000 autres cours en ligne gratuits.

Ou poursuivez votre lecture ci-dessous...
Download App

Téléchargez l'application

Contraintes techniques

  • GameState : Menu, Playing, Paused, GameOver, Victory.
  • PlayerState : Idle, Run, Jump, Fall, Attack, Hurt, Dead.
  • EnemyState (ex. patrouilleur) : Patrol, Chase, Attack, Stunned, Dead.
  • Chaque transition doit avoir une condition claire (ex. Hurt → Dead si HP ≤ 0).

Étapes pratiques

  1. Écrire la liste des états par entité.
  2. Pour chaque état : actions à l’entrée, actions à la sortie, mises à jour (tick).
  3. Définir les transitions autorisées (table ou liste).
  4. Relier les transitions aux événements (hit, input, fin d’animation, timer).

Paramètres configurables : stratégie “data-driven” pour l’équilibrage

Objectif

Rendre modifiables sans code : vitesses, HP, dégâts, cooldowns, densité d’ennemis, récompenses, timers, paramètres de caméra, etc. Le GDD doit indiquer où vivent ces valeurs et comment elles sont chargées.

Règles

  • Les valeurs sont stockées dans un asset de configuration (JSON, YAML, ScriptableObject, table interne) versionné.
  • Une valeur doit avoir : nom stable, unité (secondes, unités/s), valeur par défaut, plage recommandée.
  • Les paramètres sont séparés en : globaux (communs) et par entité (joueur, ennemi, projectile, niveau).

Étapes pratiques (workflow)

  1. Créer un fichier GameConfig (global) et des fichiers LevelConfig / EnemyConfig.
  2. Charger la config au démarrage (menu) et la mettre en cache.
  3. Au chargement d’un niveau, charger LevelConfig correspondant à levelId.
  4. Lors du spawn d’une entité, injecter sa config (par enemyTypeId).

Variables globales vs paramètres par entité

Variables globales (exemples)

  • gravity (float)
  • timeScale (float, utilisé pour pause/slow)
  • maxLives (int) ou règle équivalente
  • defaultInvulnerabilityTime (float)
  • audioMasterVolume, audioSfxVolume, audioMusicVolume (0..1)
  • language (string)

Paramètres par entité (exemples)

  • Joueur : moveSpeed, jumpForce, maxHp, attackCooldown, hurtKnockback.
  • Ennemi : maxHp, contactDamage, patrolSpeed, aggroRange, attackRange, dropTableId.
  • Projectile : speed, damage, lifeTime, pierceCount.

Événements clés du gameplay (niveau, checkpoint, fin de partie)

Liste d’événements à standardiser

  • OnLevelStart(levelId, spawnPointId) : initialise HUD, timers, spawns.
  • OnCheckpointReached(levelId, checkpointId) : met à jour respawn + sauvegarde.
  • OnPlayerDeath(cause) : déclenche respawn ou game over.
  • OnRespawn(levelId, checkpointId) : replace joueur, réinitialise certains éléments selon règle.
  • OnLevelCompleted(levelId, stats) : progression + écran de fin.
  • OnGameOver(runStats) : fin de partie.

Règles d’implémentation (checkpoint minimal)

  1. Chaque checkpoint a un checkpointId unique dans le niveau.
  2. Quand le joueur entre dans la zone : déclencher OnCheckpointReached.
  3. Stocker lastCheckpointId dans la progression sauvegardée.
  4. Au respawn : utiliser SpawnPointId associé au checkpoint.

Livrables à inclure dans le GDD (section technique)

1) Liste des systèmes à implémenter (minimum)

  • SceneManager : chargement/transition + passage de paramètres.
  • InputManager : actions + contextes (UI/Gameplay).
  • SaveManager : progression + options + versionnement.
  • Collision/Physics Setup : couches + triggers + matrice.
  • State Machines : game state + player state + enemy state.
  • Spawner : instanciation entités depuis données (niveau).
  • EventBus (ou équivalent) : publication/abonnement aux événements clés.
  • Config Loader : chargement des assets de données (global/level/entity).

2) Contrat de données (champs nécessaires)

Le contrat ci-dessous sert de référence : les développeurs savent quels champs attendre, et les designers savent quoi remplir.

{  "gameConfig": {    "saveVersion": 1,    "gravity": 30.0,    "defaultInvulnerabilityTime": 0.8,    "maxLives": 3  },  "options": {    "audioMaster": 0.9,    "audioMusic": 0.7,    "audioSfx": 0.8,    "screenShake": true,    "language": "fr"  },  "progress": {    "unlockedLevelIds": ["level_01"],    "lastPlayedLevelId": "level_01",    "checkpointByLevel": {      "level_01": "cp_02"    }  }}

Contrat LevelConfig (minimum)

{  "levelId": "level_01",  "displayName": "Niveau 1",  "timeLimitSec": 0,  "spawnPoints": [    {"id": "start", "x": 2.0, "y": 1.0},    {"id": "cp_02", "x": 18.0, "y": 1.0}  ],  "checkpoints": [    {"checkpointId": "cp_02", "triggerRect": {"x": 17.5, "y": 0.5, "w": 2.0, "h": 2.0}, "spawnPointId": "cp_02"}  ],  "entities": [    {"entityId": "e_001", "type": "enemy", "enemyTypeId": "enemy_patroller", "x": 10.0, "y": 1.0},    {"entityId": "p_001", "type": "pickup", "pickupTypeId": "coin", "x": 6.0, "y": 1.0}  ],  "winCondition": {"type": "reachExit", "exitRect": {"x": 28.0, "y": 0.0, "w": 2.0, "h": 3.0}},  "loseCondition": {"type": "noLives"}}

Contrat EnemyConfig (minimum)

{  "enemyTypeId": "enemy_patroller",  "maxHp": 3,  "contactDamage": 1,  "move": {    "patrolSpeed": 2.0,    "patrolDistance": 4.0,    "turnCooldownSec": 0.2  },  "senses": {    "aggroRange": 6.0,    "loseAggroRange": 8.0  },  "attack": {    "attackRange": 1.2,    "attackCooldownSec": 1.0,    "windupSec": 0.2  },  "drops": {    "dropTableId": "basic_enemy"  }}

Exemples complets : paramètres pour 1 niveau et 1 ennemi

Exemple 1 — LevelConfig : level_01 (simple, testable)

But : fournir un niveau jouable avec un checkpoint, quelques entités, et une condition de victoire claire. Les valeurs sont volontairement simples pour faciliter l’itération.

{  "levelId": "level_01",  "displayName": "Niveau 1",  "timeLimitSec": 0,  "spawnPoints": [    {"id": "start", "x": 1.5, "y": 1.0},    {"id": "cp_01", "x": 12.0, "y": 1.0}  ],  "checkpoints": [    {"checkpointId": "cp_01", "triggerRect": {"x": 11.5, "y": 0.5, "w": 2.0, "h": 2.0}, "spawnPointId": "cp_01"}  ],  "entities": [    {"entityId": "enemy_01", "type": "enemy", "enemyTypeId": "enemy_patroller", "x": 7.0, "y": 1.0},    {"entityId": "enemy_02", "type": "enemy", "enemyTypeId": "enemy_patroller", "x": 16.0, "y": 1.0},    {"entityId": "coin_01", "type": "pickup", "pickupTypeId": "coin", "x": 4.0, "y": 2.0}  ],  "winCondition": {"type": "reachExit", "exitRect": {"x": 22.0, "y": 0.0, "w": 2.0, "h": 3.0}},  "loseCondition": {"type": "noLives"}}

Exemple 2 — EnemyConfig : enemy_patroller (ennemi de base)

But : un ennemi qui patrouille, poursuit si le joueur s’approche, et inflige des dégâts au contact ou via une attaque courte (selon votre règle). Les champs ci-dessous permettent d’ajuster la difficulté sans toucher au code.

{  "enemyTypeId": "enemy_patroller",  "maxHp": 3,  "contactDamage": 1,  "invulnerabilityOnHitSec": 0.1,  "move": {    "patrolSpeed": 1.8,    "patrolDistance": 3.5,    "chaseSpeed": 2.4,    "turnCooldownSec": 0.15  },  "senses": {    "aggroRange": 5.5,    "loseAggroRange": 7.5,    "lineOfSightRequired": false  },  "attack": {    "mode": "contactOrMelee",    "attackRange": 1.0,    "attackCooldownSec": 1.2,    "windupSec": 0.15,    "hitbox": {"x": 0.8, "y": 0.6, "w": 1.0, "h": 1.0}  },  "drops": {    "dropTableId": "basic_enemy",    "dropChance": 0.35  }}

Répondez maintenant à l’exercice sur le contenu :

Quel est l’intérêt principal de définir des actions d’input (Jump, Attack, Pause, etc.) séparées des touches dans le GDD ?

Tu as raison! Félicitations, passez maintenant à la page suivante

Vous avez raté! Essayer à nouveau.

En définissant des actions plutôt que des touches, la logique gameplay reste indépendante du matériel et devient configurable et testable. Cela facilite aussi la gestion des contextes (UI vs gameplay) et des priorités d’input.

Chapitre suivant

Plan de production et découpage en tâches : finaliser un GDD actionnable pour un petit jeu

Arrow Right Icon
Téléchargez l'application pour obtenir une certification gratuite et écouter des cours en arrière-plan, même avec l'écran éteint.