Comment gérer plusieurs fils d'histoire dans un jeu RPG?

26

J'ai conçu un jeu RPG qui a plusieurs fils d'histoire, ce qui signifie que, selon le choix de l'utilisateur, certaines choses peuvent ou non se produire, vous pouvez réaliser la même chose de plusieurs manières, la fin peut être différente et ainsi de suite.

J'ai implémenté un moteur de décision simple, qui fonctionne bien mais qui a un gros défaut, au moment où vous prenez une décision, l'histoire est immédiatement influencée par votre décision, ce qui signifie que vous ne pouvez pas prendre une décision qui vous affectera dans un avenir lointain . En effet, l'histoire se déroule comme une branche dans une structure arborescente, et elle doit toujours savoir quel nœud est le suivant. Sous le capot, les décisions sont mises en œuvre à l'aide d'une file d'attente: chaque nœud connaît le nœud précédent et le nœud suivant (ou s'il s'agit d'un nœud de décision, il attend l'entrée de l'utilisateur pour définir le nœud suivant)

J'ai vu beaucoup de jeux qui ont des moteurs de décision complexes, et je me demande comment sont-ils fabriqués? Existe-t-il un design spécial qui rend les choses vraiment faciles? Quelqu'un a-t-il fait quelque chose de similaire et peut-il me donner un indice sur la façon de résoudre ce problème?

MISE À JOUR 1:

Un aspect important est de réussir à garder le code de l'histoire en quelque sorte indépendant, afin qu'il puisse être manipulé à partir d'un fichier externe. J'ai l'intention de l'utiliser comme moteur, donc même les choix possibles doivent provenir d'un fichier externe. Le code doit être totalement abstrait.

De plus, je suis intéressé par une solution de conception, une belle façon de le faire, comment les autres le font ou l'ont fait.

Valentin Radu
la source
1
Lorsque les décisions importantes sont prises, il suffit de les suivre dans une variable accessible à l'échelle mondiale (un tableau de ces variables sera plus facile à gérer). Ces variables peuvent ensuite être référencées par des parties ultérieures de votre programme de jeu pour agir ou afficher les choses selon les besoins. Par exemple, le joueur décide de planter un petit arbre, et plus tard, cet arbre apparaît très grand - s'il n'a pas planté l'arbre, alors cet arbre ne serait plus du tout au même stade ultérieur du jeu.
Randolf Richardson
Oui, c'est ce que j'ai pensé au départ, cependant, j'ai besoin que cela soit indépendant du code. Cela signifie que l'histoire peut être entièrement manipulée à partir d'un fichier externe. Donc, je dois trouver un moyen de généraliser ce que vous venez de dire et de le faire de manière à ne pas perdre le contrôle du projet (il y a pas mal de décisions). Met à jour la question. Merci!
Valentin Radu
Donc, pour être plus précis, je ne peux pas vérifier if (isTree)ou conserver une isTreevariable globale car l'histoire peut ou non avoir ce choix. Sais ce que je veux dire? Cela ressemble plus à un moteur de choix qui servira à plusieurs histoires.
Valentin Radu
Cela a également un autre problème, disons que si l'utilisateur décide de planter un arbre que nous avons défini isTree=true, plus tard, il fait autre chose, comme combattre un camarade de classe qui, en retour, va couper son arbre alors que l'arbre est encore jeune parce qu'il s'est fait botter le cul. Maintenant, nous avons 2 variables qui influencent l'existence de l'arbre isTree==true' and didFightBrat == false`. Sais ce que je veux dire? Et la chaîne peut durer éternellement, l'existence de l'arbre peut être influencée par un nombre inconnu de facteurs. Sais ce que je veux dire?
Valentin Radu
Ensuite, stockez ces données dans un fichier sur le disque. Vous devrez créer deux sous-programmes pour charger et enregistrer les informations, puis utiliser ces routines à partir de chaque partie du code selon les besoins.
Randolf Richardson

Réponses:

18

Vous pouvez également généraliser la file d'attente en un graphique acyclique dirigé (DAG). Vous pouvez en lire plus sur Wikipédia. Fondamentalement, chaque nœud peut avoir un ou plusieurs nœuds parents dont il "dépend". Les cycles ne sont pas autorisés, c'est-à-dire que si A dépend de B, B ne peut pas dépendre de A (directement ou via une chaîne indirecte d'autres nœuds).

Chaque nœud est dans un état "actif" ou "inactif" et n'est autorisé à devenir actif que si tous ses parents sont déjà actifs. La structure du graphique (quels nœuds sont là et comment sont-ils connectés) fait partie des données du jeu, mais l'état actif / inactif fait partie des données de sauvegarde du joueur.

De cette façon, vous pouvez modéliser des choses comme: lorsque vous plantez un arbre, vous marquez une tâche "plantedTree" comme active; puis, plus tard dans le jeu, une autre tâche "treeGrown" nomme à la fois "plantedTree" et un autre nœud (partie de l'histoire) comme ses parents. Ensuite, "treeGrown" ne devient actif que lorsque le joueur arrive à ce point de l'histoire, et "plantedTree" est également actif.

Vous pouvez inclure d'autres fonctionnalités telles que des nœuds qui s'activent si l'un de leurs parents est activé, ou des nœuds qui sont activés par un parent et désactivés par un autre, etc. C'est un cadre assez général pour créer des histoires avec plusieurs fils interdépendants.

Nathan Reed
la source
Une très bonne réponse, merci. Cela résout en fait d'autres problèmes que j'ai également, comme l'enregistrement de la progression de l'utilisateur. C'est de quoi j'ai besoin.
Valentin Radu
@NathanReed Pourquoi cela ne pourrait-il pas être cyclique? Être acyclique n'est généralement pas une caractéristique, mais un sous-produit de la conception graphique. Je ne le créerais pas avec cette intention. Par exemple, imaginez si vous vouliez que votre arbre reconnaisse les saisons. Ils sont par nature cycliques et votre histoire peut être identique en fonction des choix disponibles au cours d'une saison.
Il doit être acyclique car s'il y a un cycle, vous entrez dans une boucle infinie lorsque vous essayez de déterminer si un nœud du cycle peut être actif, car vous vérifiez récursivement tous ses ancêtres, qui incluent le nœud lui-même. Si vous vouliez modéliser quelque chose comme les saisons, je ne le ferais pas dans le contexte de ce graphique.
Nathan Reed
@NathanReed Ah, désolé, j'ai raté la partie récursive.
3

D'après ce que je comprends, ce que vous voulez n'est pas seulement un moteur de décision, mais aussi un moteur de règles. Pour chaque décision, vous exécutez un sous-ensemble de règles défini par cette décision. L'exécution de ces règles dépend souvent de l'état de certaines entités comme votre exemple d'arborescence.

Fondamentalement, lorsque votre joueur prend une décision, vous recherchez cette décision, exécutez les règles, puis fournissez le prochain ensemble de décisions disponibles comme d'habitude. Cependant, vos règles sont dynamiques dans la mesure où certaines d'entre elles ne s'exécuteront que sur la base d'autres règles déjà exécutées.

Un peu plus sur Wikipédia .

De leur sous-titre Quand utiliser les moteurs de règles (c'est moi qui souligne):

  • Le problème est tout simplement trop complexe pour le code traditionnel.
  • Le problème n'est peut-être pas complexe, mais vous ne pouvez pas voir une manière robuste de le construire.
  • Le problème dépasse toute solution évidente basée sur un algorithme.
  • C'est un problème complexe à résoudre. Il n'y a pas de solutions traditionnelles évidentes ou le problème n'est pas entièrement compris.
  • La logique change souvent
  • La logique elle-même peut être simple mais les règles changent assez souvent. Dans de nombreuses organisations, les versions de logiciels sont rares et les règles peuvent aider à fournir "l'agilité" qui est nécessaire et attendue d'une manière raisonnablement sûre.
  • Les experts du domaine et les analystes commerciaux sont facilement disponibles, mais ne sont pas techniques.
  • Les experts en domaine sont souvent riches en connaissances sur les règles et processus métier. Ils ne sont généralement pas techniques, mais peuvent être très logiques. Les règles peuvent leur permettre d'exprimer la logique dans leurs propres termes. Bien sûr, ils doivent encore penser de manière critique et être capables de penser logiquement. De nombreuses personnes occupant des postes non techniques n'ont pas de formation en logique formelle, alors soyez prudent et travaillez avec elles. En codifiant les connaissances métier dans les règles, vous exposerez souvent des trous dans la manière dont les règles et processus métier sont actuellement compris.

Une chose à noter est qu'il est parfois préférable d'implémenter un moteur de règles en utilisant un "langage" spécifique au domaine simplifié, ou quelque chose comme YAML. Je ne suggérerais pas XML.


la source
1

Vous devez considérer qu'un événement n'est pas uniquement basé sur la décision de l'utilisateur. Comme vous l'avez noté, un événement doit être ajouté si, lorsqu'un ensemble de décisions est pris et puis quelque chose s'ajoute (comme deux jours après).

Je pense que vous avez besoin d'un moyen de modéliser les événements et de le déclencher. Alors que le premier est plus lié à votre cas spécifique, le second peut être modélisé par une machine à états hiérarchique (HSM) qui déclenche directement ou indirectement vos événements.

Gardez à l'esprit qu'une machine d'état souffre de la malédiction de la dimensionnalité qui n'est atténuée que par une structure hiérarchique. Bientôt, vous comprendrez que vous devez modéliser la signification complexe du statut à l'aide d'un HMS, mais également fournir un moyen de l'interroger.

Dans ce scénario, vous avez des événements de base (décisions de l'utilisateur, heure, changement de temps, etc.) qui sont traités à la fois par le HSM et par les rappels d'événement de base. Le HSM fournit un modèle pour la «mémoire» et les rappels fournissent un moyen de décrire comment cette mémoire doit être utilisée pour calculer les conséquences d'une séquence de décisions / événements externes.

Vous pouvez également finir par utiliser un dicton (ou une autre structure de collecte) de HMS, un pour chaque "aspect" de statut que vous devez calculer. Un exemple peut être d'utiliser un événement lié au HMS et un autre pour les décisions que les rappels prennent afin de déclencher des événements.

Toute cette infrastructure sert à imiter le comportement d'un maître de donjon humain: il prend généralement un enregistrement mental de la situation actuelle (HMS ["externe"]) en raison des décisions des joueurs et des conditions environnementales; quand quelque chose s'ajoute, il peut prendre des décisions en utilisant son dossier mental et enregistrer également un état de stratégie interne (HSM ["interne"]) afin d'éviter de réagir de la même manière si la situation en question s'ajoute par exemple.

FxIII
la source