Écrire une arène PvP au tour par tour avec des mouvements simultanés

Dans cet article, je parlerai de ce qui a motivé la création du jeu dans un genre aussi inhabituel, de quel genre de genre il s'agissait, de la façon dont le développement a progressé, des difficultés que nous avons rencontrées et du fait que moins d'un an de travail le soir, nous avons réussi à créer un prototype entièrement jouable.



Au printemps 2017, je suis tombé sur une incitation Atlas Reactor . Le jeu était une sorte de mélange sauvage d'échecs, de poker et de mobs, et une telle combinaison inhabituelle de genres a vraiment attiré mon attention. Elle est devenue mon jeu préféré, j'ai participé à des tournois et événements en ligne, et tout irait bien, mais ...

À l'été 2019, les serveurs étaient fermés, car en raison de la faible popularité du jeu, leur support n'était pas rentable pour l'éditeur. Le jeu a été construit sur le modèle du jeu en tant que service , donc l'arrêt des serveurs a transformé les clients en morceaux de code cassés.

En raison de la combinaison unique de genres de jeux similaires, il n'y avait tout simplement pas de jeux sur le marché, mais je voulais vraiment jouer quelque chose comme ça.

Peu de temps après la fermeture, je suis tombé sur un post sur le reddit qu'un groupe d'enthousiastes parmi les anciens joueurs a décidé de créer un serveur privé et d'ajuster le code client pour travailler avec, mais je n'avais pas une envie particulière de les rejoindre. Je n'ai jamais été intéressé par l'ingénierie inverse, et d'ailleurs, une telle activité n'a pas l'air trop légale.

En fin de compte, j'ai décidé de rassembler des gens et d'organiser le travail sur «l'héritier spirituel» - pour faire un jeu avec des mécanismes similaires, mais de nouveaux personnages, capacités, cartes et ENT. Nous avons commencé à travailler sur le pur enthousiasme le soir et le week-end. Nous voulions apporter quelque chose de propre au genre, ajouter de la variabilité et de la profondeur, et corriger les défauts de l'original. Dès le début, il était clair que le jeu ne deviendrait pas super populaire, mais j'étais sûr qu'au moins plusieurs milliers de personnes pourraient s'y intéresser. Au moins - les fans du réacteur Atlas fermé.

Gameplay


En général, l'action rappelle le XCOM multijoueur, mais avec 100% de chances de frapper et de héros (comme dans le genre MOBA).

L'idée du jeu est que tous les résultats sont complètement déterminés par les actions fixes des joueurs. Le caractère aléatoire est complètement absent et la variation est suffisamment faible pour que les joueurs expérimentés puissent calculer tous les résultats probables et agir sur la base de ces informations. Pour réduire la variabilité, un terrain de jeu plat divisé en cellules est utilisé, c'est-à-dire que les héros ne peuvent se tenir qu'au centre des cellules (bien que les capacités soient généralement appliquées n'importe où, sans référence à la grille).

Habituellement, 8 personnes participent à un match. Ils sont divisés en 2 équipes de 4 personnes, et chacune contrôle un personnage. Cependant, en raison de l'étape par étape, rien n'empêche de créer un mode dans lequel seulement 2 personnes participent, et chacun contrôle les quatre personnages de son équipe.

Le jeu est divisé en mouvements. Chaque mouvement est divisé en deux étapes - décision (prise de décision) et résolution(affichage des résultats des actions sélectionnées). La dynamique est ajoutée par le fait que les deux étapes se produisent pour les deux équipes en même temps (ce type d'étape par étape s'appelle We-Go) - il n'y a rien de tel qu'une équipe pense, et la seconde attend simplement, en regardant l'écran où rien ne se passe. Au lieu de cela, les deux équipes prennent des décisions et observent les résultats en même temps. La simultanéité des mouvements et la présence du champ de vision des personnages conduit au fait que le jeu ne peut pas être attribué à des jeux avec des informations complètes .

Décision- une phase dans laquelle les joueurs ont la possibilité immédiate de choisir les actions souhaitées. Le terrain de jeu «se fige» pendant plusieurs dizaines de secondes, et les joueurs doivent choisir les actions spécifiques que les personnages contrôlés par eux devront effectuer à la fin de «l'arrêt du temps». Chaque joueur voit quelles actions ses alliés vont entreprendre, mais les actions choisies par les adversaires sont cachées.

Résolution - la phase dans laquelle les actions sélectionnées sont exécutées. Avec le temps, cela prend à peu près la même chose que Décision, ou un peu moins. Pendant cela, les joueurs n'ont aucun contrôle - ils observent simplement ce qui se passe et réfléchissent aux actions pour le prochain mouvement.

La résolution est divisée en phases. Les phases se déroulent séquentiellement et pour chaque capacité, les joueurs savent à l'avance dans quelle phase elle fonctionnera. Dans ce cas, l'ordre de fonctionnement des capacités dans une phase n'a pas d'importance.



En règle générale, les dégâts sont infligés dans la phase de souffle, de sorte que les capacités défensives se déclenchent plus tôt - vous pouvez soit appliquer des boucliers dans la phase de préparation ou vous déplacer (esquiver) dans la phase de tiret. Mais en esquivant, l'ennemi peut subir des dégâts s'il passe à travers un piège (fixé par le joueur dans la phase de préparation). De plus, si l'ennemi n'esquive pas, mais reste simplement en place, le piège ne fonctionnera pas et ne lui causera aucun dommage. Même chose avec les boucliers: si vous n'attaquez pas un personnage protégé, il brûlera simplement à la fin du tour, sans aucun effet. L'intérêt ici est que, comme mentionné ci-dessus, les actions des opposants au moment de la prise de décision sont cachées et doivent donc être prévues.

La valeur typique des dégâts ne dépasse pas 35 pour permettre aux joueurs de compter plus facilement dans leur esprit. Les effets de statut et les capacités sont assez peu nombreux pour la même raison. Le mode de jeu principal est le match à mort habituel, qui va jusqu'à 5 éliminations ou 20 coups.

la mise en oeuvre


J'ai décidé d'écrire en C # (puisque c'est mon langage principal), et j'ai choisi Unity comme moteur (puisque je n'avais aucune expérience dans la création de jeux, mais il supporte nativement C # et est assez convivial pour les débutants).

Les jeux au tour par tour ne nécessitent pas une grande quantité de ressources, donc dès le début, j'ai considéré les options les plus économiques pour l'hébergement du serveur principal. Il y avait une idée d'héberger sur heroku (car il est généralement gratuit), mais redémarrer l'application à un moment aléatoire au moins une fois par jour est extrêmement gênant. Il s'est arrêté à VPS avec Linux pour 45 roubles par mois.

Étant donné les ressources limitées sur l'hébergement (en particulier - très peu d'espace sur le disque dur) et le fait que toute la logique du jeu fonctionne en 2D, j'ai décidé de le faire sur .NET Core (en écrivant mon moteur 2d léger pour calculer les zones affectées par les capacités), et d'utiliser Unity uniquement pour la visualisation côté client. Cela a permis de contrôler simplement le champ de vision des caractères côté serveur et d'envoyer aux clients uniquement les informations qu'ils devraient connaître. Les actions sélectionnées des joueurs avant traitement sur le serveur sont validées, ce qui exclut complètement la possibilité de tricher.

Pour la commodité des joueurs, un simple lanceur pour WinForms l'a scié, qui, si nécessaire, télécharge la version mise à jour du client à partir de Dropbox.

Je vais maintenant m'attarder plus en détail sur les moments les plus intéressants (ou provoquant des difficultés) que nous avons rencontrés lors du développement du jeu.

La procédure d'utilisation des capacités


Comme mentionné ci-dessus, le résultat obtenu ne devrait pas dépendre de l'ordre d'application des capacités dans la phase. Sur le serveur, les capacités sont utilisées dans l'ordre croissant de l'id du héros, mais le joueur ne doit pas y penser. Cette condition impose des restrictions sur ce qui et dans quelles phases peuvent se produire (les concepteurs de jeux sont obligés d'en tenir compte lors de la création des capacités).

Par exemple, une fois dans la phase de préparation, vous pouvez appliquer des boucliers, vous ne pouvez pas y faire de dégâts (sinon cela dépendra de l'id, les dégâts passeront aux boucliers ou directement à la santé avant qu'ils ne soient appliqués). De même, comme les dégâts peuvent être infligés dans la phase Explosion, il n'est pas permis d'appliquer l'effet de statut "Puissant", ce qui augmente les dégâts infligés par le héros. Des règles similaires sont apparues pour tous les éléments du jeu.

Logique de jeu simple


L'idée même du jeu est que les joueurs sont capables de calculer dans leur tête tout le développement des événements dans le cours, et donc la logique de tous les éléments du jeu doit être simple et intuitive. Mais avec la mise en œuvre du plan, de nombreuses difficultés sont apparues.

Tout d'abord, le mouvement . Les héros peuvent se déplacer pendant les phases Dash et Move, et ce mouvement doit se produire simultanément. Deux héros sont autorisés à être sur la même cellule pendant qu'ils se déplacent, mais à la fin des phases de mouvement, il ne doit pas rester plus d'un personnage sur chaque cellule.

La règle la plus simple pour résoudre les conflits à la fin du mouvement est «celui qui s'est levé le premier, cela et les pantoufles». Si le héros a parcouru un long chemin avant d'être dans une cellule «controversée», alors il doit être repoussé et celui qui a parcouru un chemin plus petit doit être laissé en place. Si les héros ont atteint leur destination, ayant parcouru la même distance, alors poussez-les tous les deux. En raison de son déterminisme, il a été décidé de repousser les héros le long des trajectoires de leur mouvement. La répulsion est répétée jusqu'à ce que tous les conflits soient résolus.

Le deuxième problème est l' équivalence . Supposons que nous voulons créer un profil qui, lorsqu'il frappe un ennemi, lui inflige des dégâts et ricoche automatiquement au plus prochepour lui un personnage. Le problème ici est une combinaison de «automatiquement» et de «plus proche». Les héros sont situés sur un champ en damier, ce qui signifie qu'à côté de la cible principale à la même distance minimale, il peut y avoir plusieurs personnages.

Que faire?
  • Ricochet dans les deux
  • Trier les hĂ©ros par ordre alphabĂ©tique
  • Trier par identifiant du joueur
En fait, la solution est que nous avons plus de données - la position à partir de laquelle le coup de feu a été tiré. Si vous ne pouvez pas choisir une cible la plus proche pour le rebond, vous pouvez trier les héros dans le sens horaire, où l'objectif principal est le centre, et le héros qui a appliqué la capacité est la position de départ.

Géométrie honnête


Il s'est avéré que jouer avec une géométrie honnête n'est pas intéressant. Les murs sont imperméables à la plupart des tirs et bloquent le champ de vision des adversaires, et servent en outre d'abris (et si les dommages proviennent du côté de l'abri, ils sont réduits de 50%). Cependant, les murs limitent extrêmement la zone affectée pour le héros lui-même, et pour cette raison, les joueurs n'ont vu aucune raison de les utiliser.

Temps de peinture

— , — . ( ) — . , 90 .

Il a été décidé de vérifier la plage de visibilité non seulement entre les centres, mais entre certains points spécifiques.

Peindre deux


Ainsi, l'utilisation de quatre points supplémentaires le long des bords du héros a réduit la "zone morte" à 60 degrés (ce qui est déjà devenu assez jouable).

En conséquence, les murs sont devenus vraiment utiles.

Peindre trois

, 50% ( , ).

Des points supplémentaires sont situés exactement au milieu entre le centre de la cellule et ses bords, et ce choix n'est pas accidentel. Pour les attaques rectangulaires, il est très important d'où le tir est tiré (puisque ce point est utilisé pour calculer les rebonds des murs, les explosions et certains autres modificateurs d'attaque). Un algorithme simple a permis de décaler automatiquement le point de départ à l'intérieur du héros, en fonction de la position de la souris. Dans ce cas, le point est situé de manière à maximiser la portée du tir près du mur.

Voici un lien vers le gif (20 Mo) , où vous pouvez voir comment fonctionne le décalage automatique du point de tir.

Localisation


Pour ajouter la localisation, j'ai utilisé string.Format (afin que les changements équilibrés dans les nombres de dommages ne créent pas de nouvelles lignes) et une plaque Google. Toutes les chaînes utilisées pour s'afficher à l'écran sont enveloppées dans une méthode qui interpole et vérifie le dictionnaire à partir de traductions connues. Au début de la première version du jeu, les traductions de la plaque Google sont chargées dans un fichier texte au format json, et les lignes du code source sans traduction sont automatiquement téléchargées sur la même plaque Google lors de la première tentative d'affichage à l'écran. Dans la version finale du jeu, pour des raisons évidentes, il n'y a pas de fonctionnalité en ligne pour travailler avec la table, et les traductions sont simplement chargées à partir du fichier texte existant au démarrage.

Pour les traducteurs, la solution n'était pas très pratique (parce que changer un mot conduit à la création d'une nouvelle ligne dans le tableau), mais elle était très simple à implémenter et n'imposait pas aux programmeurs de travailler avec des ressources. Ce serait idéal si les lignes ne changeaient pas pendant le processus de développement, mais ce n'était pas le cas avec nous.

Râteau


Maintenant - le plus intéressant. Quelques conseils évidents qui peuvent aider les programmeurs novices qui décident de couper leur projet favori dans une équipe de personnes partageant les mêmes idées.

  • Si possible, ne rĂ©inventez pas la roue.
    Si le problème est répandu, il devrait y avoir de nombreuses solutions toutes faites. Trouver une solution prête à l'emploi appropriée peut prendre moins de temps que de l'écrire vous-même à partir de zéro (surtout si le problème est complexe).
    . , . , , . NetworkStream-, TcpClient- Json. , , . 30 ( MagicOnion, )

  • — , . — . . — . — .

  • — , . - summary readme. .
  • ,
    . . — , — , , .
  • Git — ,
    , . .
  • ,
    . .
  • ,
    . , , , , . , , , .


Il s'est avéré créer un prototype jouable en moins d'un an. Une partie de la communauté active a rejoint le développement ou les tests, donc maintenant au moins quelques matchs ont lieu presque tous les jours. C'est vraiment cool de voir à quel point les gens apprécient le jeu, à la création duquel je participe activement.

Personnellement, pendant le travail sur le projet, j'ai pompé l'anglais parlé (car la communauté est internationale), j'ai acquis de l'expérience en travaillant avec Linux, en programmant et en utilisant Unity. Les projets pour animaux de compagnie sont cool.

Au fil du temps, nous ajouterons certainement une formation pour les nouveaux joueurs, des modèles et animations normaux, un menu adéquat et une interface utilisateur plus pratique dans son ensemble. Comme les bons modèles 3D sont très chers, ils ont créé un compte sur Patreon, et la communauté a commencé à nous soutenir financièrement.

Liens pour ceux qui sont intéressés par l'idée
— Discord-. .

, , - — , Atlas Reactor

All Articles