Les quêtes et dialogues constituent l'armature narrative de tout jeu d'aventure sur Roblox. Sans architecture rigoureuse, les déconnexions imprévues détruisent la progression des joueurs.
En combinant des graphes DAG pour les prérequis, des bus de signaux découplés, des arbres de dialogue conditionnels et la compression par bitfields, les développeurs créent des quêtes fiables et modulaires.
1. Les Quêtes sous forme de Graphes Acycliques Dirigés (DAG)
Structuration d'intrigues profondes sans impasses circulaires:
- Arbres de Prérequis: Chaque quête forme un nœud débloqué uniquement lorsque tous les nœuds antérieurs sont validés.
- Branches Mutuellement Exclusives: Permet de créer des choix d'allégeance désactivant les factions rivales sans casser l'histoire.
- Tri Topologique Préventif: Vérification automatique de la base de données pour éradiquer les dépendances circulaires insolubles.
2. Machine à États Finis (FSM) pour le Cycle de Vie
Des transitions d'états inviolables pour contrer les exploits:
- Les 6 États Canoniques:
Verrouillée ➔ Disponible ➔ Active ➔ Accomplie ➔ Récompensée(ouÉchouée). - Autorité Côté Serveur: Le client ne peut jamais décréter qu'un objectif est rempli; le serveur vérifie chaque action.
- Retour Visuel Optimiste: Dès confirmation serveur, l'interface client anime la progression et déclenche des sons valorisants.
-- Gestionnaire de Quêtes par FSM
local QuestManager = {}
QuestManager.__index = QuestManager
local QuestDatabase = require(game.ReplicatedStorage.QuestDatabase)
function QuestManager.new(player, profileData)
local self = setmetatable({}, QuestManager)
self.Player = player
self.ActiveQuests = profileData.ActiveQuests or {}
self.CompletedQuests = profileData.CompletedQuests or {}
return self
end
function QuestManager:CanStartQuest(questId)
local data = QuestDatabase[questId]
if not data then return false end
if self.CompletedQuests[questId] or self.ActiveQuests[questId] then return false end
for _, prereqId in ipairs(data.Prerequisites) do
if not self.CompletedQuests[prereqId] then return false end
end
return true
end
function QuestManager:AcceptQuest(questId)
if not self:CanStartQuest(questId) then return false end
self.ActiveQuests[questId] = {
Step = 1,
Counters = table.create(#QuestDatabase[questId].Steps[1].Objectives, 0)
}
return true
end
return QuestManager
3. Suivi d'Objectifs Découplé par Bus d'Événements
Séparation complète entre le gameplay et la validation de quête:
- Bus d'Événements Centralisé: Au lieu de coder les quêtes dans les scripts des monstres, on émet un signal générique
EntityKilled. - Filtrage Dynamique: Le gestionnaire de quêtes ne traite que les événements concernant les tâches en cours, économisant le CPU.
- Objectifs Mixtes: Gestion simultanée de plusieurs étapes (collecter des herbes et vaincre des ennemis) dans des dictionnaires dédiés.
4. Arbres de Dialogue Ramifiés & Choix Interactifs
Conversations avec PNJ aux conséquences concrètes sur la partie:
- Structure de Dialogue Nodale: Les échanges sont agencés en nœuds textuels reliant répliques, plans de caméra et réponses au choix.
- Réponses Conditionnelles: Masquage ou affichage d'options selon les objets en inventaire ou la réputation du héros.
- Déclencheurs d'Actions: Lancement d'une cinématique, remise d'un objet ou combat de boss initié directement depuis le dialogue.
5. Sauvegarde Ultralégère dans DataStore avec ProfileService
Enregistrer des centaines de quêtes sans saturer la limite de stockage:
- Compactage par Champ de Bits (Bitfield): Mémorisation des quêtes finies sous forme de bits dans des entiers (32 quêtes par bloc de 4 octets).
- Sauvegarde des Seules Quêtes Actives: Stockage exclusif des IDs en cours et de leurs compteurs, sans métadonnées superflues.
- Gestion des Versions: Hooks de migration dans ProfileService pour adapter la progression des joueurs lors des mises à jour narratives.
Frequently Asked Questions
Pourquoi structurer les quêtes sous forme de DAG?
Car cela empêche mathématiquement les blocages cycliques et permet de modéliser des arbres d'objectifs complexes.
Quel est l'intérêt d'un bus d'événements pour les quêtes?
Il supprime les vérifications continuelles par frame en avertissant uniquement les quêtes concernées par une action précise.
Comment éviter les pertes de progression lors d'une déconnexion?
En utilisant ProfileService avec verrouillage de session pour synchroniser instantanément les compteurs en mémoire.
Comment sécuriser les récompenses remises lors d'un dialogue?
Le dialogue s'affiche en local, mais la remise du lot est validée et effectuée exclusivement par le serveur.