Je fais un jeu 2D descendant et je veux avoir beaucoup de types d'attaque différents. Je voudrais rendre les attaques très flexibles et combinables comme le fonctionne The Binding of Isaac. Voici une liste de tous les objets de collection du jeu . Pour trouver un bon exemple, regardons l' élément Spoon Bender .
Spoon Bender donne à Isaac la possibilité de tirer des larmes à tête chercheuse.
Si vous regardez la section "synergies", vous verrez qu'elle peut être combinée avec d'autres objets de collection pour des effets intéressants mais intuitifs. Par exemple, s'il se combine avec The Inner Eye , il "permettra à Isaac de tirer plusieurs coups de repérage à la fois". Cela a du sens, car The Inner Eye
Donne à Isaac un triple coup
Quelle est une bonne architecture pour concevoir des choses comme ça? Voici une solution de force brute:
if not spoon bender and not the inner eye then ...
if spoon bender and not the inner eye then ...
if not spoon bender and the inner eye then ...
if spoon bender and the inner eye then ...
Mais cela deviendra incontrôlable très rapidement. Quelle est la meilleure façon de concevoir un système comme celui-ci?
la source
Réponses:
Vous n'avez absolument pas besoin de coder manuellement les combinaisons. Vous pouvez plutôt vous concentrer sur les propriétés que chaque élément vous donne. Par exemple, l' article A définit
Projectile=Fireball,Targetting=Homing
. Élément B définitFireMode=ArcShot,Count=3
. LaArcShot
logique est responsable de l'envoi duCount
nombre d'Projectile
articles dans un arc.Ces deux éléments peuvent être combinés avec tout autre élément qui modifie librement ces propriétés (ou d'autres). Si vous ajoutez un nouveau type de projectile, il fonctionnera automatiquement avec
ArcShot
, et si vous ajoutez un nouveau mode de tir, il fonctionnera automatiquement avec lesFireball
projectiles. De même,Targetting
est une propriété qui définit le contrôleur pour les projectiles tout enFireMode
créant les projectiles, afin qu'ils puissent être facilement et trivialement combinés dans n'importe quelle combinaison comme il est logique.Vous pouvez également définir des dépendances de propriété, etc. Par exemple,
ArcShot
nécessite que vous ayez un fournisseur deProjectile
(qui pourrait être juste la valeur par défaut). Vous pouvez définir des priorités de sorte que si vous avez deux éléments actifs qui fournissent tous les deuxProjectile
le code, vous savez lequel utiliser. Ou vous pouvez fournir une interface utilisateur pour permettre à l'utilisateur de sélectionner le type de projectile à utiliser, ou simplement demander au joueur d'équiper les éléments de haute priorité dont il ne veut pas, ou d'utiliser l'élément le plus récent, etc. Vous pouvez en outre autoriser un système d'incompatibilités , par exemple de telle sorte que deux éléments qui ne font que les modifierProjectile
ne peuvent pas être équipés simultanément.En général, lorsque cela est possible, préférez tout type d' approche basée sur les données (ou déclarative ) aux approches procédurales (les gros gâchis si-sinon) en ce qui concerne les objets et autres dans votre jeu. Une logique générique de niveau supérieur configurable par des données simples est de loin préférable aux listes codées en dur de règles spéciales.
la source
Si vous utilisez un langage POO, cela semble être un bon endroit pour utiliser le motif de décoration . Lorsque vous souhaitez modifier la façon dont une attaque se produit, décorez-la simplement avec l'augmentation appropriée.
Exemple c ++ brut:
Cette méthode serait préférable si vous avez un très grand nombre d'attaques et que vous devez toutes les faire se comporter plus ou moins de la même manière. Si vous voulez changer substantiellement la façon dont l'attaque se produit avec le modificateur (par exemple nouvelle animation avec modificateur), cette méthode n'est pas pour vous.
la source
Attack
méthode de l'objet qu'il agrège. LaTripleAttack
classe ne devrait pas connaître laTearAttack
classe. Si cela était vrai, cela conduirait à autant de maux de tête que leelse-if
bloc. Cela signifie que toutes les animations de larmes doivent résider à l'intérieur de l'TearAttackBehaviour
objet. Cet objet ne sait pas (et ne devrait pas) savoir qu'il a été décoré par unTripleAttack
objet. Le résultat est que les 3 animations de déchirure se déroulent indépendamment, car elles sont indépendantes.En tant que fan de Binding of Isaac, je me suis aussi demandé comment faire quelque chose comme ça. Le système du jeu est suffisamment robuste pour que les comportements émergents résultent de la combinaison d'effets (celui qui me vient à l'esprit est d'obtenir un miroir, une cuillère à cintrer et certains amplificateurs de gamme entraînent un mur de larmes tourbillonnant et homing autour d'Isaac, style Magneto ). Le nombre considérable d'entre eux rendrait impossible un bloc "si".
Ma conclusion est qu'Isaac et ses larmes sont deux entités au centre d'un cadre de composant-entité massif . Les entités ont des statistiques de base (vitesse de déplacement, durée de vie, portée, dégâts, sprite, etc.) et chaque composant apporterait un modificateur de statistiques et un verbe.
Dans le code, Isaac et ses larmes auraient chacun une liste qui contiendrait des éléments d'une interface. Isaac aurait une liste de choses qui s'abonnent à l'interface IsaacMutator, et ses larmes tearMutator. IsaacMutator aurait des fonctions pour modifier la santé, la vitesse, la portée, l'apparence et certains verbes spéciaux d'Isaac. TearMutator serait similaire. Une fois par boucle de jeu, Isaac parcourrait tous les IsaacMutators qu'il possède, et toutes les larmes vivantes le feraient également. Pour reprendre votre exemple anglais, cela se lirait comme suit:
etc. Parce que les types sont additifs, vous pouvez empiler et ajouter et supprimer le contenu de votre cœur.
la source
Je pense que votre chemin fonctionne le mieux. Ces types d'articles donnent chacun une condition, s'ils sont utilisés ensemble, ils produisent une condition différente, alors vous aurez effectivement besoin des 3 conditions possibles définies.
Vous pouvez également vous y prendre en créant un nouveau type de définition lorsque les deux éléments sont présents, mais cela ajoute en fait à la convolution:
la source