Comment fonctionnent les capacités dans War Robots





salut! Je m'appelle Vladimir Popov et je suis développeur client sur le projet War Robots.



War Robots existe depuis plusieurs années: pendant ce temps, des dizaines de nouveaux mechs sont apparus dans le jeu. Et, bien sûr, aucun d'entre eux ne serait unique sans ses propres capacités.



Je vais vous expliquer dans cet article comment fonctionne le système de capacités de notre jeu et comment il a évolué, simplement et sans aucun détail technique.



Tout d'abord, plongeons dans l'histoire et examinons l'ancienne implémentation - maintenant elle n'est plus utilisée sur le projet.



Les anciennes capacités étaient très triviales: elles avaient un composant qui était accroché au robot. C'était une construction monolithique dans laquelle le programmeur décrivait en détail comment la capacité fonctionne: son flux, comment et avec quoi elle interagit. Toute la logique est décrite à l'intérieur d'un composant, que le concepteur de jeu pourrait simplement accrocher au robot et ajuster les paramètres. Il n'y avait aucun moyen de changer les capacités de flux - les concepteurs de jeux ne pouvaient changer que les paramètres et les horaires.



L'ancienne capacité ne pouvait exister que dans deux états: actif et inactif. Chaque état peut se voir attribuer sa propre action.







Prenons l'exemple de la capacité Jammer. Elle était à un moment donné, par exemple, le robot Stalker. Elle a travaillé comme suit:



  1. Si la capacité est active, l'animation est lue et le robot entre dans l'état brouilleur. Dans cet état, le robot ne peut pas être ciblé.
  2. Si la capacité est inactive, rien ne se passe.
  3. Lorsque vous essayez d'activer la capacité, il est vérifié si plus de n secondes se sont écoulées depuis la dernière activation.
  4. La désactivation se produit automatiquement après m secondes.


Pendant longtemps, cette fonctionnalité nous suffisait. Mais au fil du temps, tout a changé: les concepteurs de jeux et les programmeurs n'étaient plus satisfaits de cette approche. Il était difficile pour les programmeurs de maintenir de telles capacités, car le code devenait monstrueux - avec une très longue chaîne d'héritage, où chaque situation devait être décrite. Les concepteurs de jeux manquaient de flexibilité du système. Autrement dit, pour tout changement dans une capacité, ils devaient commander la révision des programmeurs, même si exactement la même fonctionnalité existait dans la capacité voisine.



Puis nous avons réalisé que nous devions changer quelque chose. Et ils ont développé un nouveau système. Dans celui-ci, chaque capacité a commencé à être représentée comme un ensemble de plusieurs objets liés. La fonction était divisée en états, capacités et composants d'états.



Comment ça fonctionne?



Toute capacité a un maître . C'est son objet central. Il connecte le reste des objets de capacité au monde extérieur et vice versa. Et il prend également toutes les décisions principales.



Il peut y avoir n'importe quel nombre d' états . En substance, l'état ici n'est pas très différent de l'état "actif" / "inactif" dans l'ancienne version. Mais maintenant, il peut y en avoir un certain nombre, et leur objectif est devenu plus abstrait. Un seul état peut être actif à la fois.



La principale innovation par rapport à l'ancien système était les composants . Le composant décrit une sorte d'action. Chaque état peut avoir n'importe quel nombre de composants.







Comment fonctionnent les nouvelles capacités?



La capacité ne peut être que dans l'un des états à la fois. Le maître est en train de les changer. Les composants qui se lient à l'état réagissent à l'activation / désactivation de l'état et, en fonction de cela, peuvent soit commencer à exécuter une action, soit arrêter de l'exécuter.



Tous les objets sont désormais personnalisables. Le concepteur de jeu peut mélanger les états et les composants de n'importe quelle manière et ainsi obtenir une nouvelle capacité à partir des blocs préinstallés. Les programmeurs sont désormais nécessaires uniquement pour créer un nouveau composant ou état, ce qui facilite grandement l'écriture de code. Maintenant, ils travaillent avec de petites entités, décrivent des éléments simples et n'assemblent pas eux-mêmes la capacité - les concepteurs de jeux ont commencé à le faire.



Flow est devenu comme ça:



  1. Le maître active le premier état;
  2. ;
  3. ;
  4. ;
  5. ;
  6. ;
  7. .


Par la suite, cette procédure est répétée encore et encore. Pour faciliter l'utilisation, un état n'est pas seulement un conteneur pour les composants, mais il détermine également quand passer à un autre état et demande au maître de basculer.



Au fil du temps, cela ne nous suffisait pas, et le schéma de la capacité a été transformé sous la forme suivante: le







maître, l'état et les composants sont restés à leur place, mais de nouveaux éléments leur ont été ajoutés.



La première chose qui attire votre attention est que nous avons ajouté des conditions à chaque état et composant. Pour les États, ils définissent des exigences supplémentaires pour quitter l'État. Pour les composants, ils déterminent si le composant peut effectuer son action.



Un conteneur de charges (charges) contient des charges, les recharge, arrête de se recharger si nécessaire et fournit des charges aux États pour utilisation.



Le temporisateur est utilisé lorsque plusieurs états doivent avoir un temps d'exécution commun, mais que leur propre temps d'exécution n'est pas défini.



Il est important de noter que tous les objets de capacité sont facultatifs. Techniquement, seuls le maître et un état suffisent pour pouvoir fonctionner.



Il n'y a pas tellement de capacités qui sont complètement assemblées sans l'implication des programmeurs, mais le développement en général est devenu nettement moins cher, car les programmeurs écrivent maintenant de très petites choses: par exemple, un nouvel état ou deux composants, le reste est réutilisé.



Résumons les éléments constitutifs des capacités dont nous disposons et ce qu'ils sont:



  • Le maître agit comme une machine à états. Il fournit aux états et aux composants des informations sur le monde et le monde - des informations sur une capacité. Le maître sert de lien entre les états, les composants et les parties de service d'une capacité: charges et temporisations externes.
  • L'état écoute les commandes d'activation et de désactivation du maître et, en conséquence, active et désactive les composants, et demande également au maître de passer à un autre état. L'État décide du moment où il doit passer au suivant. Pour ce faire, il utilise sa condition interne: si le joueur a cliqué sur le bouton de capacité, si un certain temps s'est écoulé depuis l'activation de l'état, etc. - et les conditions externes liées à l'état.
  • : . : , , .
  • , , . . , . , . — , .
  • Un conteneur de charges contient des charges, les recharge, arrête de se recharger en cas de besoin et accorde des charges aux États. Il est utilisé dans les capacités multi-charges lorsque vous devez donner au joueur la possibilité de l'utiliser plusieurs fois, mais pas plus de n fois de suite.
  • Le temporisateur est utilisé lorsque plusieurs états ont une durée commune, mais on ne sait pas combien de temps chacun d'eux est valide. Tout état peut démarrer une minuterie pendant n secondes. Tous les États intéressés s'abonnent à l'événement sur la fin du minuteur et font quelque chose quand il se termine.


Revenons maintenant au diagramme des capacités. Comment a-t-elle commencé à agir?



  1. Au début du jeu, le maître choisit le premier état et l'active;
  2. L'État active tous ses composants;
  3. ;
  4. ;
  5. , ;
  6. ;
  7. .


Les États peuvent utiliser des redevances comme condition de transition supplémentaire. Si une telle transition a lieu, le nombre de charges diminue. En outre, les États peuvent utiliser une minuterie commune. Dans ce cas, la durée totale de leur exécution sera déterminée par la minuterie, et chaque état individuellement peut durer à tout moment.



Nous n’avons pas proposé quelque chose de complètement nouveau pour l’interface utilisateur. C'est arrangé comme ça avec nous.



Le maître a sa propre interface utilisateur. Il définit certains éléments qui doivent toujours figurer dans l'interface utilisateur et ne dépendent pas de l'état actuellement actif.



Chaque étatil y en a deux dans l'interface utilisateur. L'interface utilisateur d'état est affichée uniquement lorsque son état est actif. Il reçoit des données sur son état et peut les afficher d'une manière ou d'une autre. Par exemple, les états de durée ont généralement une barre et un texte dans leur interface utilisateur qui représentent le temps restant.



Dans le cas où l'état attend une commande externe pour continuer la capacité, son interface utilisateur affiche un bouton. Et appuyer dessus envoie la commande à l'état.







Regardons maintenant le travail des capacités à l'aide d'exemples spécifiques. Commençons par un robot appelé Inquisitor.



Nous avons quatre états qui changent les uns après les autres. Au-dessus des états, vous pouvez voir leur affichage dans l'interface utilisateur. Dans deux d'entre eux, vous pouvez voir les composants qui y font référence. Les deux autres états n'ont tout simplement aucun composant.



Flux de travail des capacités:



  1. WaitForClick. .
  2. , . WaitForGrounded.
  3. . , . , , Jammer, .
  4. .
  5. : Sound Jammer, Shake, n.
  6. Duration, n , .
  7. Duration, : .
  8. À la fin, la capacité revient au premier état.






Un autre exemple est Phantom. Beaucoup de choses se passent ici de la même manière que Inquisitor, mais il y a encore quelques nuances:



  1. Nous commençons par WaitForClick.
  2. Ensuite, la durée, dans laquelle la téléportation est définie, les statistiques du mech sont modifiées, le son et l'animation sont joués.
  3. Après cela - DurationOrClick, dans lequel les statistiques de fourrure sont modifiées, l'animation et les effets sont lus.
  4. Si un clic a été fait, nous passons à une autre Durée, dans laquelle la fourrure est téléportée, les statistiques sont modifiées, l'animation, les effets et les sons sont joués.
  5. Après cet état ou après la fin du temps DurationOrClick, nous passons à Duration.


La principale différence est que les états de branchement apparaissent ici. DurationOrClick passe à l' état a si le temps spécifié est écoulé, ou à l' état b , si le joueur a eu le temps de cliquer sur le bouton de capacité auparavant.







Ainsi, il semblerait que notre système soit passé du simple au complexe, mais simplifie ainsi la vie des programmeurs et des concepteurs de jeux. L'aide des premiers est désormais principalement nécessaire lors de l'ajout de petits composants, tandis que les seconds ont reçu une plus grande autonomie et peuvent désormais assembler indépendamment de nouvelles capacités à partir d'états et de composants existants. Dans le même temps, les joueurs ont également reçu un bénéfice sous la forme de capacités plus diverses et complexes des mechs.



All Articles