Dans toute collision, deux GameObjects sont impliqués, non? Ce que je veux savoir, c'est comment décider quel objet doit contenir mon OnCollision*
?
Par exemple, supposons que j'ai un objet Player et un objet Spike. Ma première pensée est de mettre sur le lecteur un script contenant du code comme celui-ci:
OnCollisionEnter(Collision coll) {
if (coll.gameObject.compareTag("Spike")) {
Destroy(gameObject);
}
}
Bien sûr, la même fonctionnalité peut être obtenue en ayant à la place un script sur l'objet Spike qui contient du code comme celui-ci:
OnCollisionEnter(Collision coll) {
if (coll.gameObject.compareTag("Player")) {
Destroy(coll.gameObject);
}
}
Bien que les deux soient valides, il était plus logique pour moi d'avoir le script sur le lecteur car, dans ce cas, lorsque la collision se produit, une action est exécutée sur le lecteur .
Cependant, ce qui me fait douter, c'est qu'à l'avenir, vous souhaiterez peut-être ajouter plus d'objets qui tueront le joueur en cas de collision, tels qu'un ennemi, une lave, un faisceau laser, etc. Ces objets auront probablement des balises différentes. Alors le script sur le Player deviendrait:
OnCollisionEnter(Collision coll) {
GameObject other = coll.gameObject;
if (other.compareTag("Spike") || other.compareTag("Lava") || other.compareTag("Enemy")) {
Destroy(gameObject);
}
}
Alors que, dans le cas où le script était sur le Spike, tout ce que vous auriez à faire est d'ajouter ce même script à tous les autres objets qui peuvent tuer le joueur et nommer le script quelque chose comme KillPlayerOnContact
.
De plus, si vous avez une collision entre le joueur et un ennemi, vous voudrez probablement effectuer une action sur les deux . Dans ce cas, quel objet devrait gérer la collision? Ou doit-il à la fois gérer la collision et effectuer différentes actions?
Je n'ai jamais construit de jeu d'une taille raisonnable auparavant et je me demande si le code peut devenir compliqué et difficile à maintenir car il se développe si vous vous trompez ce genre de chose au début. Ou peut-être que tous les moyens sont valides et que cela n'a pas vraiment d'importance?
Toute idée est très appréciée! Merci pour votre temps :)
la source
Tag.SPIKE
place?Réponses:
Personnellement, je préfère architecturer des systèmes tels qu'aucun objet impliqué dans une collision ne le gère, et à la place, un proxy de gestion des collisions peut le gérer avec un rappel comme
HandleCollisionBetween(GameObject A, GameObject B)
. De cette façon, je n'ai pas à penser à quelle logique devrait être où.Si ce n'est pas une option, par exemple lorsque vous ne contrôlez pas l'architecture de réponse aux collisions, les choses deviennent un peu floues. Généralement, la réponse est "cela dépend".
Étant donné que les deux objets recevront des rappels de collision, je mettrais du code dans les deux, mais uniquement du code pertinent pour le rôle de cet objet dans le monde du jeu , tout en essayant de maintenir l'extensibilité autant que possible. Cela signifie que si une logique a le même sens dans l'un ou l'autre objet, préférez la mettre dans l'objet, ce qui entraînera probablement moins de modifications de code à long terme à mesure que de nouvelles fonctionnalités sont ajoutées.
Autrement dit, entre deux types d'objets qui peuvent entrer en collision, l'un de ces types d'objet a généralement le même effet sur plus de types d'objet que l'autre. Mettre du code pour cet effet dans le type qui affecte plus d'autres types signifie moins de maintenance à long terme.
Par exemple, un objet joueur et un objet balle. On peut dire que «subir des dégâts en cas de collision avec des balles» est logique en tant que partie intégrante du joueur. "Appliquer des dommages à la chose qu'elle frappe" est logique dans le cadre de la balle. Cependant, une balle est susceptible d’endommager plus de types d’objets différents qu’un joueur (dans la plupart des jeux). Un joueur ne subira pas de dégâts de blocs de terrain ou de PNJ, mais une balle pourrait tout de même leur infliger des dégâts. Je préfère donc mettre la gestion de collision pour infliger des dommages à la balle, dans ce cas.
la source
TL; DR Le meilleur endroit pour placer le détecteur de collision est l'endroit où vous considérez qu'il est l' élément actif dans la collision, au lieu de l' élément passif dans la collision, car vous aurez peut-être besoin d'un comportement supplémentaire pour être exécuté dans une telle logique active (et, suivant de bonnes directives de POO comme SOLID, est la responsabilité de l' élément actif ).
Détaillé :
Voyons voir ...
Mon approche lorsque j'ai le même doute, quel que soit le moteur de jeu , est de déterminer quel comportement est actif et quel comportement est passif par rapport à l'action que je souhaite déclencher lors de la collecte.
Veuillez noter: j'utilise différents comportements comme abstractions des différentes parties de notre logique pour définir les dommages et - comme autre exemple - l'inventaire. Les deux sont des comportements distincts que j'ajouterais plus tard à un joueur, et n'encombrent pas mon lecteur personnalisé avec des responsabilités distinctes qui seraient plus difficiles à maintenir. En utilisant des annotations comme RequireComponent, je m'assurerais que mon comportement de joueur a également les comportements que je définis maintenant.
C'est juste mon avis: évitez d'exécuter la logique en fonction de la balise, mais utilisez plutôt des comportements et des valeurs différents comme des énumérations parmi lesquelles choisir .
Imaginez ces comportements:
Comportement pour spécifier un objet comme étant une pile au sol. Les jeux RPG l'utilisent pour les objets dans le sol pouvant être ramassés.
Comportement pour spécifier un objet capable de produire des dommages. Il peut s'agir de lave, d'acide, de toute substance toxique ou d'un projectile volant.
Dans cette mesure, considérez
DamageType
et enInventoryObject
tant qu'énumérations.Ces comportements ne sont pas actifs , de la sorte qu'en étant au sol ou en volant, ils n'agissent pas d'eux-mêmes. Ils ne font rien. Par exemple, si vous avez une boule de feu volant dans un espace vide, elle n'est pas prête à faire des dégâts (peut-être que d'autres comportements devraient être considérés comme actifs dans une boule de feu, comme la boule de feu consommant sa puissance en voyageant et diminuant sa puissance de dégâts dans le processus, mais même quand un
FuelingOut
comportement potentiel pourrait être développé, c'est toujours un autre comportement, et pas le même comportement DamageProducer), et si vous voyez un objet dans le sol, il ne vérifie pas l'ensemble des utilisateurs et pense qu'ils peuvent me choisir?Nous avons besoin de comportements actifs pour cela. Les homologues seraient:
Un comportement pour subir des dommages (il faudrait un comportement pour gérer la vie et la mort, car réduire la vie et recevoir des dommages sont généralement des comportements distincts, et parce que je veux garder ces exemples aussi simples que possible) et peut-être résister aux dommages.
Ce code n'est pas testé et devrait fonctionner comme un concept . Quel est le but? Les dommages sont quelque chose que quelqu'un ressent et sont relatifs à l'objet (ou à la forme vivante) endommagé, comme vous le pensez. Ceci est un exemple: dans les jeux de RPG réguliers, une épée vous endommage , tandis que dans d'autres RPG qui l'implémentent (par exemple Summon Night Swordcraft Story I et II ), une épée peut également recevoir des dégâts en frappant une partie dure ou en bloquant l'armure, et pourrait avoir besoin réparations . Ainsi, puisque la logique de dommage est relative au récepteur et non à l'expéditeur, nous mettons uniquement les données pertinentes dans l'expéditeur et la logique dans le récepteur.
Il en va de même pour un détenteur d'inventaire (un autre comportement requis serait celui lié à la présence de l'objet au sol et à la possibilité de l'enlever). C'est peut-être le comportement approprié:
la source
Comme vous pouvez le voir dans la variété des réponses ici, il y a un bon degré de style personnel impliqué dans ces décisions et un jugement basé sur les besoins de votre jeu particulier - il n'y a pas de meilleure approche absolue.
Ma recommandation est de réfléchir à la façon dont votre approche évoluera à mesure que vous développez votre jeu , en ajoutant et en itérant sur les fonctionnalités. Le fait de mettre la logique de gestion des collisions au même endroit aboutira-t-il à un code plus complexe plus tard? Ou prend-il en charge un comportement plus modulaire?
Donc, vous avez des pointes qui endommagent le joueur. Demande toi:
De toute évidence, nous ne voulons pas nous laisser emporter par cela et construire un système sur-conçu qui peut gérer un million de cas au lieu du seul cas dont nous avons besoin. Mais si c'est un travail égal dans les deux sens (c.-à-d. Mettre ces 4 lignes dans la classe A contre la classe B) mais que l'une évolue mieux, c'est une bonne règle de base pour prendre la décision.
Pour cet exemple, dans Unity en particulier, je pencherais pour mettre la logique de dégâts dans les pointes , sous la forme de quelque chose comme un
CollisionHazard
composant, qui lors d'une collision appelle une prise de dégâts méthode de dans unDamageable
composant. Voici pourquoi:Damageable
composant dessus (si vous en avez besoin pour être immunisé contre les pointes uniquement, vous pouvez ajouter types de dommages et laissez leDamageable
composant gérer les immunités)la source
Peu importe qui gère la collision, mais soyez cohérent avec le travail dont il s'agit.
Dans mes jeux, j'essaie de choisir celui
GameObject
qui "fait" quelque chose à l'autre objet comme celui qui contrôle la collision. IE Spike endommage le joueur afin qu'il gère la collision. Lorsque les deux objets se "font" quelque chose l'un à l'autre, demandez-leur de gérer chacun leur action sur l'autre objet.Aussi, je suggérerais d'essayer de concevoir vos collisions de sorte que les collisions soient gérées par l'objet qui réagit aux collisions avec moins de choses. Un pic n'aura besoin que de connaître les collisions avec le joueur, ce qui rend le code de collision dans le script Spike agréable et compact. L'objet joueur peut entrer en collision avec beaucoup d'autres choses qui feront un script de collision gonflé.
Enfin, essayez de rendre vos gestionnaires de collisions plus abstraits. Un Spike n'a pas besoin de savoir qu'il est entré en collision avec un joueur, il a juste besoin de savoir qu'il est entré en collision avec quelque chose qu'il doit infliger des dégâts. Disons que vous avez un script:
Vous pouvez sous
DamageController
-classer dans votre lecteur GameObject pour gérer les dégâts, puis dansSpike
le code de collision dela source
J'ai eu le même problème quand j'ai commencé, alors voilà: dans ce cas particulier, utilisez l'ennemi. Vous voulez généralement que la collision soit gérée par l'objet qui exécute l'action (dans ce cas - l'ennemi, mais si votre joueur avait une arme, alors l'arme devrait gérer la collision), parce que si vous voulez modifier les attributs (par exemple les dégâts de l'ennemi) vous voulez certainement le changer dans l'énémyscript et non dans le playerScript (surtout si vous avez plusieurs joueurs). De même, si vous souhaitez modifier les dégâts d'une arme que le joueur utilise, vous ne voulez certainement pas la modifier pour chaque ennemi de votre jeu.
la source
Les deux objets géreraient la collision.
Certains objets seraient immédiatement déformés, cassés ou détruits par la collision. (balles, flèches, ballons d'eau)
D'autres rebondissaient et roulaient sur le côté. (rochers) Ceux-ci pourraient encore subir des dégâts si la chose qu'ils frappaient était suffisamment dure. Les rochers ne subissent pas de dégâts d'un humain qui les frappe, mais ils pourraient le faire avec un marteau.
Certains objets visqueux comme les humains subiraient des dégâts.
D'autres seraient liés les uns aux autres. (aimants)
Les deux collisions seraient placées dans une file d'attente de gestion d'événements pour un acteur agissant sur un objet à un moment et un lieu (et une vitesse) particuliers. N'oubliez pas que le gestionnaire ne doit gérer que ce qui arrive à l'objet. Il est même possible pour un gestionnaire de placer un autre gestionnaire dans la file d'attente pour gérer les effets supplémentaires de la collision en fonction des propriétés de l'acteur ou de l'objet sur lequel on agit. (La bombe a explosé et l'explosion qui en résulte doit maintenant tester les collisions avec d'autres objets.)
Lors de l'écriture de scripts, utilisez des balises avec des propriétés qui seraient traduites en implémentations d'interface par votre code. Référencez ensuite les interfaces dans votre code de gestion.
Par exemple, une épée peut avoir une balise pour Melee qui se traduit par une interface nommée Melee qui peut étendre une interface DamageGiving qui a des propriétés pour le type de dégâts et un montant de dégâts défini en utilisant soit une valeur fixe soit des plages, etc. Et une bombe peut avoir une étiquette Explosive dont l'interface Explosive étend également l'interface DamageGiving qui a une propriété supplémentaire pour le rayon et éventuellement la vitesse à laquelle les dommages se diffusent.
Votre moteur de jeu coderait alors par rapport aux interfaces, et non à des types d'objets spécifiques.
la source