Création d'un crochet pour chat dans Unity. 2e partie

image

Remarque : ce didacticiel est destiné aux utilisateurs avancés et expérimentés, et il ne couvre pas des sujets tels que l'ajout de composants, la création de nouveaux scripts GameObject et la syntaxe C #. Si vous avez besoin d'améliorer vos compétences Unity, consultez nos didacticiels Prise en main de Unity et Introduction à Unity Scripting .

Dans la première partie du tutoriel, nous avons appris à créer un crochet pour chat avec la mécanique d'enrouler une corde autour des obstacles. Cependant, nous en voulons plus: la corde peut s'enrouler autour des objets au niveau, mais elle ne se détache pas à votre retour.

Se rendre au travail


Ouvrez le projet terminé à partir de la première partie dans Unity ou téléchargez le brouillon de cette partie du didacticiel, puis ouvrez 2DGrapplingHook-Part2-Starter . Comme dans la première partie, nous utiliserons la version Unity 2017.1 ou supérieure.

Ouvrez la scène du jeu dans l'éditeur à partir du dossier de projet Scènes .


Lancez la scène du jeu et essayez d'accrocher le crochet de chat sur les pierres au-dessus du personnage, puis balancez-vous pour que la corde s'enroule autour d'une paire de bords de pierre.

Lorsque vous revenez en arrière, vous remarquerez que les points de la pierre à travers lesquels la corde utilisée pour se retourner ne se décrochent plus.


Pensez au point auquel la corde doit se dérouler. Pour simplifier la tâche, il est préférable d'utiliser l'étui lorsque la corde s'enroule autour des bords.

Si la limace, accrochée à une pierre au-dessus de sa tête, pivote vers la droite, la corde se pliera après le seuil auquel elle franchit le point d'angle de 180 degrés avec la nervure à laquelle la limace est actuellement attachée. Dans la figure ci-dessous, elle est représentée par un point vert en surbrillance.


Lorsque la limace retourne dans l'autre sens, la corde doit à nouveau se décrocher au même point (surligné en rouge sur la figure ci-dessus):


La logique de détorsion


Pour calculer le moment où vous devez détordre la corde aux points sur lesquels elle a été enroulée plus tôt, nous avons besoin de connaissances en géométrie. En particulier, nous utiliserons une comparaison des angles pour déterminer quand la corde doit se détacher du bord.

Cette tâche peut sembler un peu intimidante. Les mathématiques peuvent inspirer l'horreur et le désespoir même aux plus courageux.

Heureusement, Unity a d'excellentes fonctions d'aide mathématique qui peuvent nous faciliter un peu la vie.

Ouvrez le script RopeSystem dans l'EDI et créez une nouvelle méthode appelée HandleRopeUnwrap() .

 private void HandleRopeUnwrap() { } 

Allez dans Update() et ajoutez à la fin un appel à notre nouvelle méthode.

 HandleRopeUnwrap(); 

Alors que HandleRopeUnwrap() ne fait rien, mais maintenant nous pouvons traiter la logique associée à l'ensemble du processus de détachement des bords.

Comme vous vous en souvenez de la première partie du didacticiel, nous avons stocké les positions d'enroulement de la corde dans une collection appelée ropePositions , qui est une collection List<Vector2> . Chaque fois qu'une corde s'enroule autour d'un bord, nous gardons la position de ce point d'enroulement dans cette collection.

Pour rendre le processus plus efficace, nous n'exécuterons aucune logique dans HandleRopeUnwrap() si le nombre de positions stockées dans la collection est égal ou inférieur à 1.

En d'autres termes, lorsque la limace s'est accrochée au point de départ et que sa corde n'a pas encore enroulé autour des bords, le nombre de ropePositions de ropePositions sera de 1 et nous ne suivrons pas la logique de traitement de détorsion.

Ajoutez cette simple return en haut de HandleRopeUnwrap() pour enregistrer de précieux cycles CPU, car cette méthode est appelée à partir de Update() plusieurs fois par seconde.

 if (ropePositions.Count <= 1) { return; } 

Ajout de nouvelles variables


Dans le cadre de ce nouveau test, nous ajouterons quelques dimensions et références aux différents angles nécessaires pour mettre en œuvre la base de la logique de déroulement. Ajoutez le code suivant à HandleRopeUnwrap() :

 // Hinge =       // Anchor =     Hinge // Hinge Angle =   anchor  hinge // Player Angle =   anchor  player // 1 var anchorIndex = ropePositions.Count - 2; // 2 var hingeIndex = ropePositions.Count - 1; // 3 var anchorPosition = ropePositions[anchorIndex]; // 4 var hingePosition = ropePositions[hingeIndex]; // 5 var hingeDir = hingePosition - anchorPosition; // 6 var hingeAngle = Vector2.Angle(anchorPosition, hingeDir); // 7 var playerDir = playerPosition - anchorPosition; // 8 var playerAngle = Vector2.Angle(anchorPosition, playerDir); 

Il y a beaucoup de variables ici, donc je vais expliquer chacune d'entre elles, ainsi que d'ajouter une illustration pratique qui aidera à comprendre leur objectif.

  1. anchorIndex est l'index de la collection ropePositions à deux positions à partir de la fin de la collection. On peut le considérer comme un point en deux positions sur la corde à partir de la position de la limace. Dans la figure ci-dessous, il s'agit du premier point de fixation du crochet à la surface. Lors du remplissage de la collection ropePositions nouveaux points d'emballage, ce point restera toujours le point d'emballage à une distance de deux positions de la limace.
  2. hingeIndex est l'index de la collection qui stocke le point de la charnière actuelle; en d'autres termes, la position dans laquelle la corde s'enroule actuellement autour du point le plus proche de l'extrémité de la corde de la balle. Il est toujours à une distance d'une position de la limace, c'est pourquoi nous utilisons ropePositions.Count - 1 .
  3. anchorPosition calculé en référençant la place anchorIndex dans la collection ropePositions et est la simple valeur Vector2 de cette position.
  4. hingePosition calculé en référençant la place de hingeIndex dans la collection ropePositions et est la simple valeur Vector2 de cette position.
  5. hingeDir est un vecteur dirigé de anchorPosition vers hingePosition . Il est utilisé dans la variable suivante pour obtenir l'angle.
  6. hingeAngle - la fonction d'aide utile Vector2.Angle() est utilisée ici pour calculer l'angle entre anchorPosition et le point d'articulation.
  7. playerDir est un vecteur dirigé de anchorPosition vers la position actuelle du slug (playerPosition)
  8. Ensuite, en utilisant l'angle entre le point d'ancrage et le joueur (limace), playerAngle calculé.


Toutes ces variables sont calculées en utilisant des positions stockées en tant que valeurs Vector2 dans la collection ropePositions et en comparant ces positions avec d'autres positions ou la position actuelle du joueur (slug).

Les deux variables importantes utilisées pour la comparaison sont hingeAngle et hingeAngle .

La valeur stockée dans hingeAngle doit rester statique car il s'agit toujours d'un angle constant entre le point situé aux deux «plis de la corde» par rapport à la limace et le «pli de la corde» actuel le plus proche de la limace qui ne bouge pas tant que la corde n'est pas torsadée ou après pliage. un nouveau point de pliage sera ajouté.

Lorsque la limace se playerAngle change. En comparant cet angle avec hingeAngle , et en vérifiant également si le slug est à gauche ou à droite de ce coin, nous pouvons déterminer si le point de pliage actuel le plus proche du slug doit être détaché.

Dans la première partie de ce didacticiel, nous avons enregistré les positions de pliage dans un dictionnaire appelé wrapPointsLookup . Chaque fois que nous enregistrons le point de pliage, nous l'avons ajouté au dictionnaire avec la position comme clé et avec 0 comme valeur. Cependant, cette valeur de 0 était plutôt mystérieuse, non?

Nous utiliserons cette valeur pour stocker la position du slug par rapport à son angle avec le point d'articulation (le point de pliage actuel le plus proche du slug).

Si vous affectez une valeur de -1 , alors l'angle du slug ( playerAngle ) est inférieur à l'angle de la charnière ( hingeAngle ), et avec une valeur de 1, l' angle de playerAngle supérieur à hingeAngle .

Étant donné que nous enregistrons les valeurs dans le dictionnaire, chaque fois que nous comparons hingeAngle avec hingeAngle , nous pouvons comprendre si le slug vient de dépasser la limite après laquelle la corde doit se décrocher.

Cela peut s'expliquer différemment: si l'angle de la limace vient d'être vérifié et qu'il est plus petit que l'angle de la charnière, mais la dernière fois qu'il a été enregistré dans le dictionnaire des points de pliage, il a été marqué d'une valeur indiquant qu'il était de l'autre côté de ce coin, alors le point doit être supprimé immédiatement !

Corde de désaccouplement


Jetez un œil à la capture d'écran ci-dessous avec des notes. Notre limace s'accrochait au rocher, se balançait vers le haut, enroulant une corde autour du bord du rocher en montant.


Vous remarquerez peut-être qu'à la position de swing la plus haute, où la limace est opaque, son point de pliage le plus proche actuel (marqué d'un point blanc) sera stocké dans le dictionnaire wrapPointsLookup avec une valeur de 1 .

En descendant, lorsque playerAngle devient plus petit que hingeAngle (deux lignes vertes en pointillés), comme indiqué par la flèche bleue, une vérification est effectuée, et si la dernière valeur (actuelle) du point de pliage était 1 , alors le point de pliage doit être supprimé.

Maintenant, implémentons cette logique dans le code. Mais avant de commencer, créons un blanc de la méthode que nous utiliserons pour nous détendre. Pour cette raison, après avoir créé la logique, cela ne conduira pas à une erreur.

Ajoutez une nouvelle méthode UnwrapRopePosition(anchorIndex, hingeIndex) en insérant les lignes suivantes:

 private void UnwrapRopePosition(int anchorIndex, int hingeIndex) { } 

Cela fait, revenons à HandleRopeUnwrap() . Sous les variables nouvellement ajoutées, ajoutez la logique suivante, qui gérera deux cas: playerAngle moins que hingeAngle et hingeAngle plus que hingeAngle :

 if (playerAngle < hingeAngle) { // 1 if (wrapPointsLookup[hingePosition] == 1) { UnwrapRopePosition(anchorIndex, hingeIndex); return; } // 2 wrapPointsLookup[hingePosition] = -1; } else { // 3 if (wrapPointsLookup[hingePosition] == -1) { UnwrapRopePosition(anchorIndex, hingeIndex); return; } // 4 wrapPointsLookup[hingePosition] = 1; } 

Ce code doit correspondre à l'explication de la logique décrite ci-dessus pour le premier cas (lorsque hingeAngle < hingeAngle ), mais gère également le second cas (lorsque hingeAngle > hingeAngle ).

  1. Si le point de pliage actuel le plus proche du slug a une valeur de 1 au point où hingeAngle < hingeAngle , alors nous hingeAngle ce point et effectuons un retour afin que le reste de la méthode ne s'exécute pas.
  2. Sinon, si le point de pliage n'a pas été marqué pour la dernière fois avec une valeur de 1 , mais que hingeAngle playerAngle inférieur à hingeAngle , alors -1 est affecté.
  3. Si le point de pliage actuel le plus proche du slug est -1 au point où hingeAngle > hingeAngle , supprimez le point et revenez.
  4. Sinon, nous attribuons les entrées dans le dictionnaire des points de pliage à la position charnière à 1 .

Ce code garantit que le dictionnaire wrapPointsLookup toujours mis à jour, garantissant que la valeur du point de pliage actuel (le plus proche du slug) correspond à l'angle de slug actuel par rapport au point de pliage.

N'oubliez pas que la valeur est -1 lorsque l'angle de slug est inférieur à l'angle de charnière (par rapport au point de référence), et 1 lorsque l'angle de slug est supérieur à l'angle de charnière.

Nous allons maintenant UnwrapRopePosition() dans le script RopeSystem avec un code qui va directement s'engager dans le découplage, déplacer la position de référence et attribuer une nouvelle valeur de distance à la valeur de distance de corde DistanceJoint2D. Ajoutez les lignes suivantes au disque de méthode créé précédemment:

  // 1 var newAnchorPosition = ropePositions[anchorIndex]; wrapPointsLookup.Remove(ropePositions[hingeIndex]); ropePositions.RemoveAt(hingeIndex); // 2 ropeHingeAnchorRb.transform.position = newAnchorPosition; distanceSet = false; // Set new rope distance joint distance for anchor position if not yet set. if (distanceSet) { return; } ropeJoint.distance = Vector2.Distance(transform.position, newAnchorPosition); distanceSet = true; 

  1. L'index du point d'ancrage actuel (la deuxième position de la corde par rapport à la limace) devient la nouvelle position de la charnière, et l'ancienne position de la charnière est supprimée (celle qui était auparavant la plus proche de la limace et que nous sommes maintenant en train de «détordre»). La variable newAnchorPosition attribuer la valeur anchorIndex dans la liste des positions de corde. Il sera ensuite utilisé pour positionner la position mise à jour du point d'ancrage.
  2. La corde-joint RigidBody2D (à laquelle la corde DistanceJoint2D est attachée) change sa position à la nouvelle position du point d'ancrage. Cela garantit un mouvement continu et en douceur de la balle sur la corde lorsqu'elle est connectée à DistanceJoint2D, et cette connexion devrait lui permettre de continuer à osciller par rapport à la nouvelle position, qui est devenue la référence - en d'autres termes, par rapport au point suivant vers le bas de la corde à partir de sa position.
  3. Ensuite, vous devez mettre à jour la valeur de distance distanceJoint2D pour prendre en compte un changement brusque de la distance entre la limace et le nouveau point de référence. Si ce n'est pas déjà fait, une vérification rapide de l'indicateur distanceSet est effectuée et la distance est affectée à la valeur de la distance calculée entre le slug et la nouvelle position du point d'ancrage.

Enregistrez le script et revenez à l'éditeur. Redémarrez le jeu et regardez comment la corde se détache des bords lorsque la balle dépasse les valeurs de seuil de chaque point de flexion!


Bien que la logique soit prête, nous ajouterons du code d'assistance à HandleRopeUnwrap() juste avant de comparer hingeAngle avec hingeAngle ( if (playerAngle < hingeAngle) ).

 if (!wrapPointsLookup.ContainsKey(hingePosition)) { Debug.LogError("We were not tracking hingePosition (" + hingePosition + ") in the look up dictionary."); return; } 

En fait, cela ne devrait pas se produire, car nous redéfinissons et déconnectons le crochet de chat lorsqu'il s'enroule deux fois autour d'une côte, mais si cela se produit toujours, nous pouvons facilement quitter cette méthode avec une simple return et un message d'erreur dans la console.

De plus, grâce à cela, nous traiterons plus facilement de tels cas limites; de plus, nous recevons notre propre message d'erreur dans le cas où quelque chose d'inutile se produit.

Où aller ensuite?


Voici un lien vers le projet terminé de cette deuxième et dernière partie du tutoriel.

Félicitations pour avoir terminé cette série de didacticiels! En ce qui concerne la comparaison des angles et des positions, tout est devenu assez compliqué, mais nous y avons survécu et nous avons maintenant un merveilleux système de crochet et de chat qui peut se retrouver sur des objets dans le jeu.


Saviez-vous que notre équipe de développement Unity a écrit un livre? Sinon, consultez Unity Games By Tutorials . Ce jeu vous apprendra à créer quatre jeux prêts à l'emploi à partir de zéro:

  • Tireur à deux bâtons
  • Tireur à la première personne
  • Jeu de tower defense (avec support VR!)
  • Plateforme 2D

Après avoir lu ce livre, vous apprendrez à créer vos propres jeux pour Windows, macOS, iOS et autres plateformes!

Ce livre est destiné aux débutants et à ceux qui souhaitent mettre à niveau leurs compétences Unity vers un niveau professionnel. Pour maîtriser le livre, vous devez avoir une expérience en programmation (dans n'importe quelle langue).

Source: https://habr.com/ru/post/fr415217/


All Articles