Fermer

décembre 20, 2018

Écrire un moteur d’aventure multijoueur dans Node.js


À propos de l'auteur

Fernando Doglio travaille depuis 13 ans en tant que développeur Web et en tant que responsable technique depuis quatre ans. À cette époque, il adorait les…
Pour en savoir plus sur Fernando

Avez-vous déjà entendu parler d'une aventure de texte? Si vous êtes assez vieux (comme moi!), Alors vous en avez probablement entendu parler ou même les avez rejoués dans la journée. Dans cet article, je vais vous montrer le processus suivi lors de la création, non pas une aventure textuelle, mais tout un moteur capable de vous permettre de jouer toutes les aventures textuelles que vous souhaitez, ainsi que vos amis. C'est vrai; nous allons pimenter un peu en ajoutant multijoueur au genre aventure de texte.

Les aventures en mode texte ont été l'une des premières formes de jeu de rôle numérique, à l'époque où les jeux n'avaient pas de graphisme et que vous n'aviez que votre propre imagination et la description que vous lisiez sur l'écran noir de votre moniteur CRT. [19659005] Si nous voulons avoir la nostalgie, peut-être que le nom Colossal Cave Adventure (ou tout simplement Adventure, comme il s’appelait à l’origine) sonne la cloche. C’était le tout premier jeu d’aventure textuel jamais créé.


 Une image d’une véritable aventure textuelle de l’époque
Une image d’une véritable aventure textuelle de l’époque. ( Grand aperçu )

L’image ci-dessus montre comment vous voyez le jeu, loin de nos jeux d’aventure AAA les plus populaires. Cela étant dit, ils étaient amusants à jouer et volaient des centaines d'heures de votre temps, alors que vous étiez seul devant ce texte, essayant de comprendre comment le battre.

Bien évidemment, les aventures textuelles ont été remplacées. au fil des ans, les jeux ont présenté de meilleurs graphismes (bien que beaucoup d’entre eux aient sacrifié l’histoire pour des graphismes) et, surtout ces dernières années, leur aptitude croissante à collaborer avec d’autres amis et à jouer ensemble. Cette fonctionnalité particulière fait défaut aux aventures du texte original et je souhaite la ramener dans cet article.

Notre objectif

Comme vous l'avez probablement déjà deviné dans le titre de cet article, le but de cette entreprise est de créer un moteur d'aventure en mode texte qui vous permet de partager l'aventure avec vos amis et de collaborer. avec eux de la même manière que vous le feriez lors d'un jeu Dungeons & Dragons (dans lequel, comme dans les bonnes aventures textuelles, il n'y a pas de graphismes à regarder).

Lors de la création du moteur, du serveur de discussion et du client C'est beaucoup de travail. Dans cet article, je vais vous montrer la phase de conception, en expliquant des éléments tels que l'architecture derrière le moteur, la manière dont le client va interagir avec les serveurs et les règles de ce jeu.

Juste pour vous en donner quelques unes. aide visuelle de ce à quoi cela va ressembler, voici mon objectif:


 Cadre filaire général pour l'interface utilisateur finale du client du jeu
Cadre filaire général pour l'interface utilisateur finale du client du jeu ( Grand aperçu )

C'est notre objectif. Une fois que nous y sommes arrivés, vous aurez des captures d’écran au lieu de maquettes rapides et sales. Alors commençons le processus. La première chose que nous allons couvrir est la conception de l’ensemble. Ensuite, nous aborderons les outils les plus pertinents que j’utiliserai pour coder ceci. Enfin, je vais vous montrer quelques-uns des éléments de code les plus pertinents (avec un lien vers le référentiel complet, bien sûr).

J'espère que vous finirez par créer de nouvelles aventures textuelles pour les essayer. avec des amis!

Phase de conception

Pour la phase de conception, je vais couvrir notre projet global. Je ferai de mon mieux pour ne pas vous ennuyer à mort, mais en même temps, je pense qu'il est important de montrer quelques-unes des choses qui doivent se produire en coulisse avant de définir votre première ligne de code.

Les quatre composants que je veux aborder ici avec une quantité décente de détails sont les suivants:

  • Le moteur
    Il s’agit du serveur principal du jeu. Les règles du jeu seront mises en œuvre ici et fourniront une interface technologiquement agnostique à tout type de client. Nous allons implémenter un client terminal, mais vous pouvez faire de même avec un client de navigateur Web ou tout autre type de votre choix.
  • Le serveur de discussion
    Parce qu'il est assez complexe pour avoir son propre article, ce service est va aussi avoir son propre module. Le serveur de discussion se charge de laisser les joueurs communiquer entre eux pendant le jeu.
  • Le client
    Comme indiqué précédemment, il s'agira d'un client terminal, idéalement similaire à la maquette de précédemment. Il utilisera les services fournis par le moteur et le serveur de discussion.
  • Jeux (fichiers JSON)
    Enfin, je vais passer en revue la définition des jeux réels. Le but de cette opération est de créer un moteur pouvant exécuter n’importe quel jeu, à condition que votre fichier de jeu soit conforme aux exigences du moteur. Donc, même si cela ne nécessitera pas de codage, je vais expliquer comment je structurerai les fichiers d’aventures afin d’écrire nos propres aventures dans le futur.

The Engine

Le moteur de jeu, ou serveur de jeux, sera être une API REST et fournir toutes les fonctionnalités requises.

J'ai opté pour une API REST simplement parce que, pour ce type de jeu, le retard ajouté par HTTP et sa nature asynchrone ne poseraient aucun problème. Nous devrons toutefois suivre un chemin différent pour le serveur de discussion. Mais avant de commencer à définir les points d'extrémité de notre API, nous devons définir les capacités du moteur. Allons-y donc.

Feature Description
Rejoindre une partie Un joueur pourra rejoindre une partie en spécifiant l'ID du jeu.
Créer une nouvelle partie A Le joueur peut également créer une nouvelle instance de jeu. Le moteur doit renvoyer un ID afin que d'autres puissent l'utiliser pour se joindre.
Renvoi de la scène Cette fonction doit renvoyer la scène actuelle où se trouve la partie. En gros, elle renverra la description, avec toutes les informations associées (actions possibles, objets qu'elle contient, etc.).
Interagir avec la scène Cette opération sera l'une des plus complexes, car prendra une commande du client et exécutera cette action – déplacer, pousser, prendre, regarder, lire, pour n'en nommer que quelques-uns.
Vérifier l'inventaire Bien que ce soit un moyen d'interagir avec le jeu, il ne concerne pas directement la scène. Ainsi, vérifier l'inventaire de chaque joueur sera considéré comme une action différente.
Un mot sur le mouvement

Nous avons besoin d'un moyen de mesurer les distances dans le jeu, car se déplacer dans l'aventure est l'une des actions principales qu'un joueur peut effectuer. . Nous utiliserons ce nombre comme mesure du temps, simplement pour simplifier le jeu. Mesurer le temps avec une horloge réelle peut ne pas être la meilleure solution, étant donné que ces types de jeux ont des actions au tour par tour, telles que le combat. Au lieu de cela, nous utiliserons la distance pour mesurer le temps (ce qui signifie qu'une distance de 8 nécessitera plus de temps que de parcourir une distance sur 2, nous permettant ainsi de faire des choses comme ajouter des effets aux joueurs qui durent pour un nombre défini de «points de distance». ).

Un autre aspect important à prendre en compte à propos du mouvement est que nous ne jouons pas seuls. Par souci de simplicité, le moteur ne laissera pas les joueurs diviser le parti (bien que cela puisse être une amélioration intéressante pour le futur). La version initiale de ce module ne laissera tout le monde bouger que lorsque la majorité du parti le décidera. Donc, le déplacement devra se faire par consensus, ce qui signifie que chaque action de mouvement attendra que la majorité du parti le demande avant de se dérouler.

Combat

Le combat est un autre aspect très important de ces types de jeux, et un que nous devrons envisager d'ajouter à notre moteur; sinon, nous risquons de manquer quelque chose d’amusant.

Ce n’est pas quelque chose qui mérite d’être réinventé, pour être honnête. Le combat de parti au tour par tour existe depuis des décennies, nous allons donc implémenter une version de ce mécanisme. Nous allons mélanger cela avec le concept d ’« initiative »de Dungeons & Dragons, qui lance un nombre aléatoire afin de garder le combat un peu plus dynamique.

En d’autres termes, l’ordre dans lequel toutes les personnes impliquées dans un combat obtiennent pour choisir leur action sera aléatoire, et cela inclut les ennemis.

Enfin (bien que j'expliquerai plus en détail ci-dessous), vous aurez des objets que vous pouvez prendre avec un nombre défini de "dégâts". Ce sont les objets que vous pourrez utiliser pendant le combat. tout ce qui n’a pas cette propriété causera 0 dégâts à vos ennemis. Nous allons probablement ajouter un message lorsque vous essayez d'utiliser ces objets pour combattre, de sorte que vous sachiez que ce que vous essayez de faire n'a aucun sens.

Interaction client-serveur

Voyons maintenant comment un client donné interagirait avec notre serveur en utilisant la fonctionnalité définie précédemment (sans penser aux terminaux, mais nous y arriverons dans une seconde):


( Grand aperçu )

L'interaction initiale entre le client et le serveur (du point de vue du serveur) est le début d'un nouveau jeu. Ses étapes sont les suivantes:

  1. Créez un nouveau jeu .
    Le client demande au serveur de créer un nouveau jeu.
  2. Créer une salle de conversation .
    Bien que le nom ne le spécifie pas, le serveur ne crée pas simplement une salle de discussion sur le serveur de discussion. , mais aussi tout ce qui est nécessaire pour permettre à un ensemble de joueurs de jouer une aventure.
  3. Rendez-vous dans les métadonnées du jeu .
    Une fois le jeu créé par le serveur et la salle de discussion en direct en place pour les joueurs, le client aura besoin de cette information pour les demandes ultérieures. Il s'agira principalement d'un ensemble d'identifiants que les clients pourront utiliser pour s'identifier et identifier le jeu auquel ils souhaitent se connecter (plus d'informations à ce sujet dans une seconde).
  4. Partager manuellement l'identifiant du jeu .
    Cette étape aura lieu être fait par les joueurs eux-mêmes. Nous pourrions proposer une sorte de mécanisme de partage, mais je vais laisser cela sur la liste des souhaits d’améliorations futures.
  5. Rejoignez le jeu .
    Celui-ci est assez simple. Dès que tout le monde a l'identifiant du jeu, ils rejoindront l'aventure en utilisant leurs applications clientes.
  6. Rejoignez leur salle de discussion .
    Enfin, les applications clientes des joueurs utiliseront les métadonnées du jeu pour rejoindre la salle de discussion de leur aventure. . C'est la dernière étape requise avant le match. Une fois que tout cela est fait, les joueurs sont prêts à commencer l'aventure!

 Ordre d'action pour un jeu existant
Ordre d'action pour un jeu existant ( Grand aperçu )

Une fois les conditions préalables remplies, les joueurs peuvent commencer à jouer à l'aventure, partager leurs pensées à travers le clavardage et faire avancer l'histoire. Le diagramme ci-dessus montre les quatre étapes nécessaires à cet effet.

Les étapes suivantes seront exécutées dans le cadre de la boucle de jeu, ce qui signifie qu'elles seront répétées jusqu'à la fin du jeu.

  1. Demande de scène .
    L'application client demandera les métadonnées pour la scène actuelle. Il s'agit de la première étape de chaque itération de la boucle.
  2. Renvoyez les métadonnées .
    Le serveur renvoie à son tour les métadonnées de la scène actuelle. Cette information comprendra notamment une description générale, les objets qu’elle contient et leur relation les uns avec les autres.
  3. Envoyer la commande .
    C’est là que commence le plaisir. C'est l'entrée principale du lecteur. Elle contiendra l'action qu'ils souhaitent exécuter et, éventuellement, la cible de l'action (par exemple, souffler une bougie, saisir un rocher, etc.).
  4. Renvoyez la réaction à la commande envoyée .
    Cela pourrait simplement être la deuxième étape, mais pour plus de clarté, je l’ajoute comme étape supplémentaire. La principale différence est que la deuxième étape peut être considérée comme le début de cette boucle, alors que celle-ci prend en compte le fait que vous jouez déjà. Le serveur doit donc comprendre à qui cette action va affecter (soit un seul joueur). Tous les joueurs.

En guise d’étape supplémentaire, bien que cela ne fasse pas vraiment partie du flux, le serveur informe les clients des mises à jour de statut qui les concernent.

La raison de cette étape récurrente supplémentaire est liée aux mises à jour un joueur peut recevoir des actions d'autres joueurs. Rappelez-vous la nécessité de se déplacer d'un endroit à un autre; comme je l'ai dit précédemment, une fois que la majorité des joueurs ont choisi une direction, tous les joueurs bougent (aucune entrée de tous les joueurs n'est requise).

Le bit intéressant ici est que HTTP (nous avons déjà mentionné que le serveur va être une API REST) ​​ne permet pas ce type de comportement. Nos options sont donc les suivantes:

  1. interroger le client toutes les X secondes,
  2. utiliser une sorte de système de notification fonctionnant en parallèle avec la connexion client-serveur.

Selon mon expérience, j'ai tendance à préférez l'option 2. En fait, j'utiliserais Redis pour ce type de comportement (et j'utiliserai cet article).

Le diagramme suivant illustre les dépendances entre les services.


 Interactions entre une application client et le moteur de jeu [19659007] Interactions entre une application client et le moteur de jeu (<a href= Grand aperçu
)

Le serveur de discussion

Je laisserai les détails de la conception de ce module pour la phase de développement (qui ne fait pas partie de cet article). Cela étant dit, il y a des choses que nous pouvons décider.

Une chose que nous pouvons définir est l'ensemble des restrictions applicables au serveur, ce qui simplifiera notre travail ultérieurement. Et si nous jouons bien nos cartes, nous pourrions nous retrouver avec un service qui fournit une interface robuste, nous permettant ainsi, éventuellement, d'étendre ou même de modifier l'implémentation afin de réduire les restrictions sans affecter le jeu.

  • être une seule pièce par parti.
    Nous ne permettrons pas de créer des sous-groupes. Cela va de pair avec la non séparation du parti. Une fois cette amélioration mise en place, il serait peut-être judicieux de permettre la création de sous-groupes et de salles de conversation personnalisées.
  • Il n'y aura pas de messages privés.
    Ceci est purement à des fins de simplification, mais une discussion de groupe suffit déjà. ; nous n’avons pas besoin de messages privés pour le moment. N'oubliez pas que chaque fois que vous travaillez sur votre produit minimum viable, essayez d'éviter de tomber dans le piège des fonctionnalités inutiles; c’est un chemin dangereux et difficile à échapper.
  • Nous ne tiendrons pas les messages
    En d’autres termes, si vous quittez le parti, vous perdrez les messages. Cela simplifiera énormément notre tâche, car nous n’aurons pas à gérer aucun type de stockage de données ni à perdre du temps à choisir la meilleure structure de données pour stocker et récupérer les anciens messages. Tout cela vivra dans la mémoire et y restera aussi longtemps que la salle de discussion sera active. Une fois la fermeture terminée, nous leur dirons simplement au revoir!
  • La ​​communication se fera via des sockets .
    Malheureusement, notre client devra gérer un double canal de communication: un canal RESTful pour le moteur de jeu et un socket pour le serveur de discussion. Cela pourrait augmenter un peu la complexité du client, mais en même temps, il utilisera les meilleures méthodes de communication pour chaque module. (Il n’ya aucun intérêt réel à forcer REST sur notre serveur de discussion ou à forcer des sockets sur notre serveur de jeu. Cette approche augmenterait la complexité du code côté serveur, qui est également celui qui gère la logique commerciale, alors concentrons-nous sur ce côté-ci. pour l'instant.)

Voilà pour le serveur de discussion. Après tout, ce ne sera pas complexe, du moins pas au début. Il reste encore beaucoup à faire au moment de commencer à coder, mais cet article est plus que suffisant.

Le client

Il s’agit du dernier module à coder, et ce sera notre plus bête. un des lots. En règle générale, je préfère avoir des clients stupides et des serveurs intelligents. De cette façon, créer de nouveaux clients pour le serveur devient beaucoup plus facile.

Juste pour être sur la même page, voici l'architecture de haut niveau avec laquelle nous devrions nous retrouver.


 Architecture de haut niveau finale de l'ensemble développement
Architecture de haut niveau finale de l'ensemble du développement ( Grand aperçu )

Notre simple client ClI n'implémentera rien de très complexe. En fait, le problème le plus compliqué que nous devrons aborder est l'interface utilisateur réelle, car il s'agit d'une interface textuelle.

Cela dit, les fonctionnalités que l'application cliente devra implémenter sont les suivantes:

  1. Créer un nouveau jeu .
    Comme je veux garder les choses aussi simples que possible, cela ne se fera que via l'interface CLI. L'interface utilisateur réelle ne sera utilisée qu'après avoir rejoint un jeu, ce qui nous amène au point suivant.
  2. Rejoindre un jeu existant .
    Etant donné que le code du jeu est renvoyé du point précédent, les joueurs peuvent l'utiliser pour se joindre. Encore une fois, c’est quelque chose que vous devriez pouvoir faire sans interface utilisateur, cette fonctionnalité fera donc partie du processus nécessaire pour commencer à utiliser l’UI texte.
  3. Les fichiers de définition de jeu de Parse .
    We '. J'en discuterai un peu, mais le client devrait être capable de comprendre ces fichiers afin de savoir quoi montrer et comment utiliser ces données.
  4. Interagissez avec l'aventure.
    En gros, cela donne au joueur la possibilité de capacité d'interagir avec l'environnement décrit à un moment donné.
  5. Maintenez un inventaire pour chaque joueur .
    Chaque instance du client contiendra une liste d'éléments en mémoire. Cette liste va être sauvegardée.
  6. Chat d'assistance .
    L'application client doit également se connecter au serveur de chat et connecter l'utilisateur à la salle de chat du parti.

la structure interne et la conception du client plus tard. En attendant, terminons la phase de conception par la dernière préparation: les fichiers du jeu.

Le jeu: fichiers JSON

C’est là que cela devient intéressant car j’ai abordé jusqu’à présent les définitions de base des microservices. Certains d'entre eux parlent peut-être REST et d'autres peuvent travailler avec des sockets, mais ils sont essentiellement identiques: vous les définissez, vous les codez et ils fournissent un service.

Pour ce composant particulier, je suis ne prévoyez rien coder, mais nous devons le concevoir. Fondamentalement, nous mettons en place une sorte de protocole pour définir notre jeu, les scènes qu’il contient et tout ce qu’il contient.

À bien y penser, une aventure textuelle est essentiellement un ensemble de pièces connectées les unes aux autres. d’autres, et à l’intérieur d’elles, sont des «choses» avec lesquelles vous pouvez interagir, toutes liées par une histoire décente, espérons-le. Maintenant, notre moteur ne s'occupera pas de cette dernière partie; cette partie sera à vous. Mais pour le reste, il y a de l'espoir.

Maintenant, pour revenir à l'ensemble des pièces interconnectées, cela ressemble à un graphique, et si nous ajoutons également le concept de distance ou de vitesse de déplacement que j'ai mentionné précédemment, nous avons: un graphique pondéré. Et c’est juste un ensemble de nœuds qui ont un poids (ou juste un nombre – ne vous inquiétez pas de son nom) qui représente ce chemin entre eux. Voici un visuel (j'aime apprendre en voyant, alors regardez l'image, d'accord?):


 Exemple de graphe pondéré
Un exemple de graphe pondéré ( Grand aperçu )

C’est un graphique pondéré – c’est tout. Et je suis sûr que vous avez déjà compris, mais par souci d’exhaustivité, laissez-moi vous montrer comment vous y prendreriez une fois notre moteur prêt.

Une fois que vous aurez commencé à préparer votre aventure, vous créez votre carte (comme vous le voyez à gauche de l'image ci-dessous). Ensuite, vous traduirez cela en un graphique pondéré, comme vous pouvez le voir à droite de l'image. Notre moteur pourra le prendre en charge et vous permettre de le parcourir dans le bon ordre.


 Exemple de graphique pour un donjon donné
Exemple de graphique pour un donjon donné ( Grand aperçu )

Avec le graphique pondéré ci-dessus, nous pouvons nous assurer que les joueurs ne pourront pas aller de l’entrée jusqu’à l'aile gauche. Ils devraient passer par les nœuds entre ces deux-là et cela prendrait du temps, que nous pouvons mesurer en utilisant le poids des connexions.

Maintenant, passons à la partie "amusante". Voyons à quoi ressemblerait le graphique au format JSON. Ours avec moi ici; Ce JSON contiendra beaucoup d’informations, mais je vais en parcourir le plus possible:

 {
    "graphe": [
            { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } },
     { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } ,
     { "id": "bigroom",
       "name": "Big room",
       "south": { "node": "1stroom", "distance": 1},
       "north": { "node": "bossroom", "distance": 2},
       "east":  { "node": "rightwing", "distance": 3} ,
       "west":  { "node": "leftwing", "distance": 3}
     },
     { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} }
     { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} }
     { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } }
    ],
    "Jeu": {
     "condition de victoire": {
       "source": "finalboss",
       "condition": {
         "type": "comparaison",
         "left": "hp",
         "right": "0",
         "symbole": "
                 {"action": "throw", "cible": "chaise"} // throw 
               ],
               "destination": "inventaire",
               "dégâts": 2
             }
           ]
         }
       ]
     },
     "grande chambre": {
       "la description": {
         "default": "Vous avez atteint la grande salle. Sur chaque mur, des torches éclairent chaque coin. Les murs sont peints en blanc et le plafond est haut et rempli d'étoiles blanches peintes sur un fond noir. Il y a une passerelle sur côté et une grande double porte en bois en face de vous. "
       },
       "exits": {
         "nord": {"id": "bossdoor", "nom": "grande double porte", "statut": "verrouillé", "détails": "Aig, double porte en bois. Il semble que quelque chose de grand vient habituellement par ici."}
       },
       "articles": []
     },
     "aile gauche": {
       "la description": {
         "default": "Une autre pièce sombre. Ça n'a pas l'air si grand, mais vous ne pouvez pas vraiment savoir ce qu'il y a à l'intérieur. Cependant, vous sentez la viande pourrie quelque part à l'intérieur.",
         "conditionals": {
           "a la lumière": "Vous semblez avoir trouvé la cuisine. Il y a des tables pleines de viande partout, et un grand couteau dépasse de ce qui semble être la tête d'une vache."
         }
       },
       "articles": [
         { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10}
       ]
     },
     "Aile droite": {
       "la description": {
         "default": "Cela semble être une sorte de bureau. Il y a un bureau en bois au milieu, des torches allumant chaque mur, et une clé reposant sur le dessus du bureau."
       },
       "articles": [
         {"id": "clé",
           "nom": "clé d'or",
           "détails": "Une petite clé en or. Quel usage pourriez-vous en tirer?",
           "destination": "inventaire",
           "déclencheurs": [{
             "action": "use", // use  à la sortie nord (contextuel)
             "cible": {
               "chambre": "bigroom",
               "sortie": "nord"
             },
             "effet": {
               "statusUpdate": "déverrouillé",
               "cible": {
                 "chambre": "bigroom",
                 "sortie": "nord"
               }
             }
           }
         ]
         }
       ]
     },
     "bossroom": {
       "la description": {
         "default": "Vous semblez avoir atteint la fin du donjon. Il n'y a pas d'autre sortie que celle par laquelle vous venez d'entrer. La seule autre chose qui vous dérange, c'est le géant gigantesque qui a l'air de vouloir vous tuer, debout environ 10 pieds de vous. "
       },
       "npcs": [
         {
           "id": "finalboss",
           "name": "Hulking Ogre",
           "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.",
           "stats":  {
             "hp": 10,
             "damage": 3
           }
         }
       ]
     }
    }
}

Je sais que cela a l'air beaucoup, mais si vous résumez cela à une simple description du jeu, vous disposez d'un cachot comprenant six salles, chacune interconnectée les unes avec les autres, comme indiqué dans le schéma ci-dessus.

La tâche est de le parcourir et de l'explorer. Vous constaterez qu’il ya deux endroits différents où vous pouvez trouver une arme (dans la cuisine ou dans la pièce sombre, en cassant le fauteuil). Vous serez également confronté à une porte verrouillée; ainsi, une fois que vous aurez trouvé la clé (située dans la pièce semblable à un bureau), vous pourrez l’ouvrir et combattre le boss avec l’arme de votre choix.

Vous gagnerez en le tuant ou en perdant par se faire tuer par elle.

Entrons maintenant dans un aperçu plus détaillé de la structure entière de JSON et de ses trois sections.

Graphe

Celle-ci contiendra la relation entre les nœuds. Fondamentalement, cette section est directement traduite dans le graphique que nous avons examiné précédemment.

La structure de cette section est assez simple. Il s'agit d'une liste de nœuds, où chaque nœud comprend les attributs suivants:

  • un identifiant identifiant de manière unique le nœud parmi tous les autres dans le jeu;
  • un nom, qui est fondamentalement une version lisible par l'utilisateur, [[19659078] un ensemble de liens vers les autres nœuds. Ceci est démontré par l’existence de quatre clés possibles: nord ”, sud, est et ouest. Nous pourrions éventuellement ajouter d'autres instructions en ajoutant des combinaisons de ces quatre éléments. Chaque lien contient l'ID du nœud associé et la distance (ou le poids) de cette relation.
Jeu

Cette section contient les paramètres et conditions généraux. En particulier, dans l'exemple ci-dessus, cette section contient les conditions de gain et de perte. En d’autres termes, avec ces deux conditions, nous indiquerons au moteur quand le jeu pourra se terminer.

Pour simplifier les choses, j’ai ajouté deux conditions:

  • vous gagnez soit en tuant le patron, [19659078] ou perdre en se faisant tuer.
Chambres

Voici d'où proviennent la plupart des 163 lignes, et c'est la plus complexe des sections. C’est ici que nous allons décrire toutes les pièces de notre aventure et tout ce qu’elles contiennent.

Il y aura une clé pour chaque pièce, en utilisant l’ID défini précédemment. Et chaque salle aura une description, une liste d’articles, une liste de sorties (ou portes) et une liste de personnages non lisibles (NPC). Parmi ces propriétés, le seul qui devrait être obligatoire est la description, car celle-ci est requise pour que le moteur vous indique ce que vous voyez. Les autres ne seront là que s'il y a quelque chose à montrer.

Voyons ce que ces propriétés peuvent faire pour notre jeu.

Description

Cet élément n'est pas aussi simple. comme on pourrait le penser, parce que votre vue d’une pièce peut changer selon les circonstances. Si, par exemple, vous regardez la description de la première pièce, vous remarquerez que, par défaut, vous ne pouvez rien voir, à moins bien sûr que vous ayez une torche allumée avec vous.

les objets et leur utilisation peuvent déclencher des conditions globales qui affecteront d'autres parties du jeu.

Les objets

Ils représentent tout ce que vous pouvez "trouver dans une pièce". Chaque élément partage le même identifiant et le même nom que les nœuds de la section graphique.

Ils auront également une propriété «destination», qui indique où cet élément doit être stocké, une fois récupéré. Ceci est pertinent car vous ne pourrez avoir qu'un seul objet dans vos mains, tandis que vous pourrez en avoir autant que vous le souhaitez dans votre inventaire.

Enfin, certains de ces objets pourraient déclencher d'autres actions ou actions. mises à jour de statut, en fonction de ce que le joueur décide de faire avec eux. Un exemple de ceci est les torches allumées de l'entrée. Si vous en saisissez un, vous déclencherez une mise à jour du statut dans le jeu, ce qui le fera afficher une description différente de la pièce suivante.

Les objets peuvent également avoir des «sous-éléments», qui entrent en jeu. une fois que l’article original est détruit (par l’action «break», par exemple). Un élément peut être divisé en plusieurs éléments, définis dans l'élément “subitems”.

Cet élément est essentiellement un tableau de nouveaux éléments, qui contient également l'ensemble des actions pouvant déclencher leur création. Cela ouvre fondamentalement la possibilité de créer différents sous-éléments en fonction des actions que vous effectuez sur l'élément d'origine.

Enfin, certains éléments auront une propriété «dommages». Ainsi, si vous utilisez un objet pour frapper un PNJ, cette valeur sera utilisée pour soustraire sa vie.

Les sorties

Il s'agit simplement d'un ensemble de propriétés indiquant le sens de la sortie. et ses propriétés (une description, au cas où vous voudriez l'inspecter, son nom et, dans certains cas, son statut).

Les sorties sont une entité distincte des éléments, car le moteur doit comprendre si vous pouvez réellement traversez-les en fonction de leur statut. Les sorties verrouillées ne vous laisseront pas les parcourir, à moins que vous ne découvriez comment modifier leur statut.

Les PNJ

Enfin, les PNJ feront partie d'une autre liste. Ce sont essentiellement des éléments avec des statistiques que le moteur utilisera pour comprendre le comportement de chacun. Ceux que nous avons définis dans notre exemple sont "hp", qui représente les points de vie, et "dégâts", qui, tout comme les armes, correspond au nombre que chaque coup soustraira de la santé du joueur.

pour le donjon que j'ai créé. C'est beaucoup, oui, et à l'avenir, je pourrais envisager de créer un éditeur de niveaux, afin de simplifier la création des fichiers JSON. Mais pour l'instant, cela ne sera pas nécessaire.

Si vous ne le comprenez pas encore, le principal avantage de définir notre jeu dans un fichier comme celui-ci est que nous pourrons changer de fichier JSON comme vous. a fait des cartouches à l'époque de Super Nintendo. Il suffit de charger un nouveau fichier et de commencer une nouvelle aventure. Facile!

Réflexions finales

Merci d'avoir lu jusqu'à présent. J'espère que vous avez apprécié le processus de conception que j'ai suivi pour donner vie à une idée. Rappelez-vous, cependant, que je raconte cela au fur et à mesure, de sorte que nous pourrions nous rendre compte plus tard que quelque chose que nous avons défini aujourd'hui ne fonctionnera pas, auquel cas nous devrons revenir en arrière et le corriger.

Je ' Je suis sûr qu’il existe une tonne de façons d’améliorer les idées présentées ici et de faire de l’enfer un moteur. Mais cela nécessiterait beaucoup plus de mots que je ne peux en mettre dans un article sans le rendre ennuyeux pour tout le monde, nous allons donc en rester là pour l'instant.

 Smashing Editorial (rb, ra, al, il)




Source link