Faire de Tower Defense un jeu d'unité - Partie 1

image

Les jeux de tower defense gagnent en popularité, et cela n'est pas surprenant - peu de choses peuvent être comparées au plaisir d'observer vos propres lignes de défense qui détruisent les ennemis maléfiques! Dans ce tutoriel en deux parties, nous allons créer un jeu de tower defense sur le moteur Unity !

Vous apprendrez comment procéder comme suit:

  • Créez des vagues d'ennemis
  • Faites-leur suivre les points de l'itinéraire
  • Construisez et améliorez des tours et apprenez-leur également à briser les ennemis en petits pixels

En fin de compte, nous obtenons le cadre du jeu, qui peut être développé davantage!

Remarque : vous avez besoin des connaissances de base d'Unity (par exemple, vous devez savoir comment les actifs et les composants sont ajoutés, quels préfabriqués sont) et les bases de C # . Pour tout savoir, je vous recommande de parcourir les tutoriels sur Unity de Sean Duffy ou la série Beginning C # with Unity de Brian Mockley.

Je travaillerai dans Unity pour OS X, mais ce tutoriel convient également pour Windows.

À travers les fenêtres de la tour d'ivoire


Dans ce tutoriel, nous allons créer un jeu de tower defense dans lequel les ennemis (petits bugs) rampent vers un cookie vous appartenant et vos serviteurs (bien sûr, ce sont des monstres!). Le joueur peut placer des monstres à des points stratégiques et les améliorer en or.

Le joueur doit tuer tous les bugs jusqu'à ce qu'ils arrivent au cookie. Chaque nouvelle vague d'ennemis devient de plus en plus difficile à vaincre. Le jeu se termine lorsque vous survivez à toutes les vagues (victoire!) Ou lorsque cinq ennemis rampent vers les cookies (perte!).

Voici une capture d'écran du jeu terminé:


Monstres, unissez-vous! Protégez le cookie!

Se rendre au travail


Téléchargez ce projet vierge , décompressez-le et ouvrez le projet TowerDefense-Part1-Starter dans Unity.

Le projet de projet a des atouts graphiques et sonores, des animations toutes faites et plusieurs scripts utiles. Les scripts ne sont pas directement liés aux jeux de tower defense, donc je n'en parlerai pas ici. Cependant, si vous souhaitez en savoir plus sur la création d'animations 2D dans Unity, consultez ce didacticiel Unity 2D .

Le projet contient également des préfabriqués, que nous ajouterons plus tard pour créer des personnages. Enfin, il y a une scène dans le projet avec un arrière-plan et une interface utilisateur personnalisée.

Ouvrez le GameScene situé dans le dossier Scenes et réglez le mode Game sur un rapport d'aspect de 4: 3 afin que toutes les étiquettes correspondent correctement à l'arrière-plan. En mode Jeu, vous verrez ce qui suit:


Paternité:

  • Les graphismes du projet sont tirés du pack Wiki Wenderlich gratuit! D'autres œuvres graphiques peuvent être trouvées sur son site Web gameartguppy .
  • Super musique tirée de BenSound , qui a d'autres bandes sonores impressionnantes!
  • Je remercie également Michael Jesper pour la fonction très utile de bougé de l'appareil photo.
.

L'endroit est marqué d'une croix: l'emplacement des monstres


Les monstres ne peuvent être placés que sur des points marqués d'un x .

Pour les ajouter à la scène, faites glisser Images \ Objects \ Openspot de Project Browser vers la fenêtre Scene . Alors que la position n'est pas importante pour nous.

Une fois que vous avez sélectionné Openspot dans la hiérarchie, cliquez sur Ajouter un composant dans l' inspecteur et sélectionnez Box Collider 2D . Dans la fenêtre Scène, Unity affichera un collisionneur rectangulaire avec une ligne verte. Nous utiliserons ce collisionneur pour reconnaître les clics de souris à cet endroit.


Ajoutez le composant Audio \ Source audio à Openspot de la même manière. Pour le paramètre AudioClip du composant Audio Source, sélectionnez le fichier tower_place situé dans le dossier Audio et désactivez Play On Awake .

Nous devons créer 11 points supplémentaires. Bien qu'il y ait une tentation de répéter toutes ces étapes, Unity a une meilleure solution: Prefab !

Faites glisser Openspot de la hiérarchie vers le dossier Prefabs dans l' arborescence du projet . Son nom deviendra bleu dans la Hiérarchie, ce qui signifie qu'il est attaché au préfabriqué. Quelque chose comme ça:


Maintenant que nous avons le blanc préfabriqué, nous pouvons créer autant de copies que nous voulons. Faites simplement glisser et déposez Openspot depuis le dossier Prefabs dans le Navigateur du projet dans la fenêtre Scène . Répétez cette opération 11 fois et 12 objets Openspot apparaîtront dans la scène.

Utilisez maintenant l' inspecteur pour définir ces 12 objets Openspot avec les coordonnées suivantes:

  • (X: -5,2, Y: 3,5, Z: 0)
  • (X: -2,2, Y: 3,5, Z: 0)
  • (X: 0,8, Y: 3,5, Z: 0)
  • (X: 3,8, Y: 3,5, Z: 0)
  • (X: -3,8, Y: 0,4, Z: 0)
  • (X: -0,8, Y: 0,4, Z: 0)
  • (X: 2,2, Y: 0,4, Z: 0)
  • (X: 5,2, Y: 0,4, Z: 0)
  • (X: -5,2, Y: -3,0, Z: 0)
  • (X: -2,2, Y: -3,0, Z: 0)
  • (X: 0,8, Y: -3,0, Z: 0)
  • (X: 3,8, Y: -3,0, Z: 0)

Lorsque vous faites cela, la scène ressemblera à ceci:


Nous plaçons des monstres


Pour simplifier le placement, il existe un préfabriqué Monster dans le dossier Prefab du projet.


Monster Prefab prêt à l'emploi

À l'heure actuelle, il se compose d'un objet de jeu vide avec trois sprites différents et des animations de tir en tant qu'enfants.

Chaque sprite est un monstre avec différents niveaux de puissance. Le préfabriqué contient également le composant Source audio , qui sera lancé pour jouer du son lorsqu'un monstre tire un laser.

Nous allons maintenant créer un script qui hébergera Monster sur Openspot .

Dans l' arborescence du projet, sélectionnez l'objet Openspot dans le dossier Prefabs . Dans l' inspecteur, cliquez sur Ajouter un composant , puis sélectionnez Nouveau script et nommez le script PlaceMonster . Sélectionnez C Sharp comme langue et cliquez sur Créer et ajouter . Depuis que nous avons ajouté le script au préfabriqué Openspot , tous les objets Openspot de la scène auront désormais ce script. Super!

Double-cliquez sur le script pour l'ouvrir dans l'EDI. Ajoutez ensuite deux variables:

public GameObject monsterPrefab; private GameObject monster; 

Nous allons créer une instance de l'objet stocké dans monsterPrefab pour créer le monstre, et le stocker dans monster afin qu'il puisse être manipulé pendant le jeu.

Un monstre par point


Pour qu'un seul monstre puisse être placé sur un point, ajoutez la méthode suivante:

 private bool CanPlaceMonster() { return monster == null; } 

Dans CanPlaceMonster() nous pouvons vérifier si la variable monster est toujours null . Si c'est le cas, alors il n'y a pas de monstre à ce point, et nous pouvons le placer.

Ajoutez maintenant le code suivant pour placer le monstre lorsque le joueur clique sur ce GameObject:

 //1 void OnMouseUp() { //2 if (CanPlaceMonster()) { //3 monster = (GameObject) Instantiate(monsterPrefab, transform.position, Quaternion.identity); //4 AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); // TODO:   } } 

Ce code localise le monstre lorsque vous cliquez sur la souris ou touchez l'écran. Comment fonctionne-t-il?

  1. Unity appelle automatiquement OnMouseUp lorsqu'un joueur touche le collisionneur physique GameObject.
  2. Lorsqu'elle est appelée, cette méthode place un monstre si CanPlaceMonster() renvoie true .
  3. Nous créons un monstre à l'aide de la méthode Instantiate , qui crée une instance du préfabriqué donné avec la position et la rotation spécifiées. Dans ce cas, nous copions monsterPrefab , lui donnons la position actuelle de GameObject et aucune rotation, transférons le résultat vers GameObject et l'enregistrons dans monster
  4. À la fin, nous appelons PlayOneShot pour lire l'effet sonore attaché au composant AudioSource de l'objet.

Maintenant, notre script PlaceMonster peut avoir un nouveau monstre, mais nous devons toujours spécifier un préfabriqué.

Utiliser le bon préfabriqué


Enregistrez le fichier et revenez à Unity.

Pour définir la variable monsterPrefab , sélectionnez d'abord l'objet Openspot dans le dossier Prefabs du navigateur du projet.

Dans l' inspecteur, cliquez sur le cercle à droite du champ Monster Prefab du composant PlaceMonster (Script) et sélectionnez Monster dans la boîte de dialogue qui apparaît.


C’est tout. Lancez la scène et créez des monstres à différents endroits en cliquant sur la souris ou en touchant l'écran.


Super! Maintenant, nous pouvons créer des monstres. Cependant, ils ressemblent à un étrange gâchis, car tous les sprites enfants du monstre sont dessinés. Maintenant, nous allons le réparer.

Augmentez le niveau des monstres


La figure ci-dessous montre qu'avec une augmentation de niveau, les monstres ont de plus en plus peur.


Quelle mignonne! Mais si vous essayez de voler ses cookies, ce monstre se transformera en tueur.

Le script est utilisé comme base pour la mise en œuvre du système de niveaux de monstre. Il suit la puissance du monstre à chaque niveau et, bien sûr, le niveau actuel du monstre.

Ajoutez ce script.

Sélectionnez le préfabriqué Prefab / Monster dans l' arborescence du projet . Ajoutez un nouveau script C # appelé MonsterData . Ouvrez le script dans l'EDI et ajoutez le code suivant au - dessus de la classe MonsterData .

 [System.Serializable] public class MonsterLevel { public int cost; public GameObject visualization; } 

Nous créons donc MonsterLevel . Il regroupe le prix (en or, que nous soutiendrons ci-dessous) et une représentation visuelle du niveau du monstre.

Nous ajoutons au-dessus de [System.Serializable] afin que les instances de classe puissent être modifiées dans l'inspecteur. Cela nous permet de changer rapidement toutes les valeurs de la classe Level, même lorsque le jeu est en cours d'exécution. C'est incroyablement utile pour équilibrer le jeu.

Définition des niveaux de monstre


Dans notre cas, nous stockons le MonsterLevel spécifié dans la List<T> .

Pourquoi ne pas simplement utiliser MonsterLevel[] ? Nous aurons besoin de l'index d'un objet MonsterLevel spécifique plusieurs fois. Bien qu'il soit facile d'écrire du code pour cela, nous devons encore utiliser IndexOf() , qui implémente la fonctionnalité Lists . Cela n'a aucun sens de réinventer la roue.


Réinventer le vélo est généralement une mauvaise idée.

En haut de MonsterData.cs, ajoutez ce qui suit à l' using construction:

 using System.Collections.Generic; 

Il nous donne accès à des structures de données généralisées afin que nous puissions utiliser la classe List<T> dans le script.

Remarque : les généralisations sont un puissant concept C #. Ils vous permettent de spécifier des structures de données de type sécurisé sans avoir à adhérer au type. Ceci est utile pour les classes de conteneurs telles que les listes et les ensembles. Pour en savoir plus sur les structures génériques, lisez le livre Introduction aux génériques C # .

Ajoutez maintenant la variable suivante à MonsterData pour contenir la liste MonsterLevel :

 public List<MonsterLevel> levels; 

Grâce aux généralisations, nous pouvons garantir que la List partir du level ne contiendra que des objets MonsterLevel .

Enregistrez le fichier et passez à Unity pour configurer chaque niveau.

Sélectionnez Prefabs / Monster dans le navigateur de projet . L' inspecteur affiche désormais le champ Niveaux du composant MonsterData (Script) . Définissez la taille sur 3 .


Ensuite, définissez le coût pour chaque niveau:

  • Élément 0 : 200
  • Élément 1 : 110
  • Élément 2 : 120

Maintenant, nous attribuons les valeurs des champs d'affichage visuel.

Développez Prefabs / Monster dans le navigateur du projet pour voir ses enfants. Faites glisser l'enfant Monster0 dans le champ Elément de visualisation 0 .

Ensuite, définissez l' élément 1 sur Monster1 et l' élément 2 sur Monster2 . Le GIF montre ce processus:


Lorsque vous sélectionnez Prefabs / Monster , le préfabriqué devrait ressembler à ceci:


Définir le niveau actuel


Revenez à MonsterData.cs dans l'EDI et ajoutez une autre variable à MonsterData .

 private MonsterLevel currentLevel; 

Dans la variable privée currentLevel nous stockons le niveau actuel du monstre.

currentLevel maintenant currentLevel et rendez-le visible pour les autres scripts. Ajoutez les lignes suivantes à MonsterData avec la déclaration des variables d'instance:

 //1 public MonsterLevel CurrentLevel { //2 get { return currentLevel; } //3 set { currentLevel = value; int currentLevelIndex = levels.IndexOf(currentLevel); GameObject levelVisualization = levels[currentLevelIndex].visualization; for (int i = 0; i < levels.Count; i++) { if (levelVisualization != null) { if (i == currentLevelIndex) { levels[i].visualization.SetActive(true); } else { levels[i].visualization.SetActive(false); } } } } } 

Assez gros morceau de code C #, non? Prenons-le dans l'ordre:

  1. Définissez la propriété de la variable privée currentLevel . En définissant la propriété, nous pouvons l'appeler comme n'importe quelle autre variable: soit comme CurrentLevel (à l'intérieur de la classe), soit comme CurrentLevel (à l'extérieur). Nous pouvons définir n'importe quel comportement dans la méthode getter ou setter d'une propriété, et en créant uniquement un getter, un setter ou les deux, vous pouvez contrôler les propriétés de la propriété: lecture seule, écriture seule et écriture / lecture.
  2. Dans le getter, nous retournons la valeur de currentLevel .
  3. Dans le setter, nous attribuons à currentLevel nouvelle valeur. Ensuite, nous obtenons l'indice du niveau actuel. Enfin, nous parcourons tous les niveaux et activons / désactivons l'affichage visuel en fonction de currentLevelIndex . C'est formidable car lorsque currentLevel change, le sprite se met à jour automatiquement. Les propriétés sont une chose très pratique!

Ajoutez l'implémentation OnEnable suivante:

 void OnEnable() { CurrentLevel = levels[0]; } 

Ici, nous définissons CurrentLevel lors du placement. Cela garantit que seul le sprite souhaité est affiché.

Remarque : il est important d'initialiser la propriété dans OnEnable , et non dans OnStart , car nous appelons les méthodes ordinales lors de la création d'instances préfabriquées.

OnEnable sera appelé immédiatement lorsque le préfabriqué est créé (si le préfabriqué a été enregistré dans l'état activé), mais OnStart appelé que lorsque l'objet commence à s'exécuter dans le cadre de la scène.

Nous devons vérifier ces données avant de placer le monstre, nous l'initialisons donc sur OnEnable .

Enregistrez le fichier et revenez à Unity. Exécutez le projet et placez les monstres; ils affichent maintenant les sprites corrects du niveau le plus bas.


Mise à niveau de monstre


Revenez à l'EDI et ajoutez la méthode suivante à MonsterData :

 public MonsterLevel GetNextLevel() { int currentLevelIndex = levels.IndexOf (currentLevel); int maxLevelIndex = levels.Count - 1; if (currentLevelIndex < maxLevelIndex) { return levels[currentLevelIndex+1]; } else { return null; } } 

Dans GetNextLevel nous obtenons l'index currentLevel et l'index de niveau le plus élevé; si le monstre n'a pas atteint le niveau maximum, le niveau suivant revient. Sinon, null renvoyé.

Vous pouvez utiliser cette méthode pour savoir si une mise à niveau de monstre est possible.

Pour élever le niveau du monstre, ajoutez la méthode suivante:

 public void IncreaseLevel() { int currentLevelIndex = levels.IndexOf(currentLevel); if (currentLevelIndex < levels.Count - 1) { CurrentLevel = levels[currentLevelIndex + 1]; } } 

Ici, nous obtenons l'index du niveau actuel, puis nous nous assurons que ce n'est pas le niveau maximum, en vérifiant qu'il est inférieur aux levels.Count - 1 . Si tel est le cas, CurrentLevel au niveau suivant.

Vérification de la fonctionnalité de mise à niveau


Enregistrez le fichier et revenez à PlaceMonster.cs dans l'IDE. Ajoutez une nouvelle méthode:

 private bool CanUpgradeMonster() { if (monster != null) { MonsterData monsterData = monster.GetComponent<MonsterData>(); MonsterLevel nextLevel = monsterData.GetNextLevel(); if (nextLevel != null) { return true; } } return false; } 

Nous vérifions d'abord s'il existe un monstre qui peut être amélioré en comparant la variable monster avec null . Si cela est vrai, nous obtenons le niveau actuel du monstre à partir de ses MonsterData .

Ensuite, nous vérifions si le niveau suivant est disponible, c'est-à-dire si GetNextLevel() ne retourne pas null . Si une augmentation de niveau est possible, alors nous retournons true ; sinon retournez false .

Nous mettons en œuvre des améliorations pour l'or


Pour activer l'option de mise à niveau, ajoutez la branche else if à OnMouseUp :

 if (CanPlaceMonster()) { //      } else if (CanUpgradeMonster()) { monster.GetComponent<MonsterData>().IncreaseLevel(); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); audioSource.PlayOneShot(audioSource.clip); // TODO:   } 

Nous vérifions la possibilité d'une mise à niveau en utilisant CanUpgradeMonster() . Si possible, nous MonsterData composant MonsterData aide de GetComponent() et appelons IncreaseLevel() , ce qui augmente le niveau du monstre. Enfin, nous lançons Monster AudioSource .

Enregistrez le fichier et revenez à Unity. Exécutez le jeu, placez et améliorez n'importe quel nombre de monstres (mais pour l'instant).


Payer l'or - Game Manager


Bien que nous puissions immédiatement construire et améliorer des monstres, mais sera-t-il intéressant dans le jeu?

Examinons la question de l'or. Le problème avec le suivi est que nous devons transférer des informations entre différents objets de jeu.

La figure ci-dessous montre tous les objets qui devraient y participer.


Tous les objets de jeu sélectionnés doivent savoir combien d'or un joueur possède.

Pour stocker ces données, nous utiliserons un objet commun auquel d'autres objets peuvent accéder.

Cliquez avec le bouton droit sur la hiérarchie et sélectionnez Créer vide . Nommez le nouvel objet GameManager .

Ajoutez un nouveau script C # appelé GameManagerBehavior à GameManager , puis ouvrez-le dans l'EDI. Nous afficherons la quantité totale d'or du joueur dans l'étiquette, donc en haut du fichier, ajoutez la ligne suivante:

 using UnityEngine.UI; 

Cela nous permettra d'accéder à des classes d'interface utilisateur comme Text , qui est utilisé pour les étiquettes. Ajoutez maintenant la variable suivante à la classe:

 public Text goldLabel; 

Il stockera un lien vers le composant Text utilisé pour afficher la quantité d'or d'un joueur.

Maintenant que le GameManager connaît l'étiquette, comment synchroniser la quantité d'or stockée dans la variable et la valeur affichée sur l'étiquette? Nous allons créer une propriété.

Ajoutez le code suivant à GameManagerBehavior :

 private int gold; public int Gold { get { return gold; } set { gold = value; goldLabel.GetComponent<Text>().text = "GOLD: " + gold; } } 

Semble-t-il familier? Le code est similaire à CurrentLevel , que nous avons défini dans Monster . Nous créons d'abord un gold variable privé pour contenir la quantité d'or actuelle. Ensuite, nous définissons la propriété Gold (de manière inattendue, non?) Et implémentons le getter et le setter.

Le getter renvoie simplement la valeur de l' gold . Le passeur est plus intéressant. En plus de définir la valeur de la variable, il définit également le champ de text pour goldLabel pour afficher la nouvelle valeur d'or.

Sommes-nous généreux? Ajoutez la ligne suivante à Start() pour donner au joueur 1000 pièces d' or, ou moins si vous vous sentez désolé pour l'argent:

 Gold = 1000; 

Affectation d'un objet d'étiquette à un script


Enregistrez le fichier et revenez à Unity. Dans la hiérarchie, sélectionnez GameManager . Dans l' inspecteur, cliquez sur le cercle à droite du Gold Label . Dans la boîte de dialogue Sélectionner le texte , sélectionnez l'onglet Scène et sélectionnez GoldLabel .


Exécutez la scène et l'étiquette affichera Gold: 1000 .


Vérification du "portefeuille" du joueur


Ouvrez le script PlaceMonster.cs dans l'EDI et ajoutez la variable d'instance suivante:

 private GameManagerBehavior gameManager; 

Nous utiliserons gameManager pour accéder au composant GameManagerBehavior de l'objet GameManagerBehavior dans la scène. Pour le spécifier, ajoutez ce qui suit à Start() :

 gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>(); 

Nous obtenons un GameObject appelé GameManager à l'aide de la fonction GameObject.Find() , qui renvoie le premier objet de jeu trouvé avec ce nom. Ensuite, nous obtenons son composant GameManagerBehavior et l'enregistrons pour l'avenir.

Remarque : vous pouvez le faire en définissant un champ dans l'éditeur Unity ou en ajoutant à GameManager une méthode statique qui renvoie une instance du singleton à partir de laquelle nous pouvons obtenir GameManagerBehavior .

Cependant, dans le bloc de code ci-dessus, il y a un cheval noir: la méthode Find , qui fonctionne plus lentement pendant l'exécution de l'application; mais il est pratique et peut être utilisé avec modération.

Prends mon argent!


Nous n'avons pas encore soustrait d'or, nous allons donc ajouter cette ligne deux fois à OnMouseUp() , en remplaçant chacun des commentaires // TODO: :

 gameManager.Gold -= monster.GetComponent<MonsterData>().CurrentLevel.cost; 

Enregistrez le fichier et revenez à Unity, mettez à niveau certains monstres et regardez la mise à jour de la valeur Gold. Maintenant, nous déduisons de l'or, mais les joueurs peuvent construire des monstres tant qu'ils ont suffisamment d'espace; ils empruntent juste de l'argent.


Crédit infini? Super! Mais nous ne pouvons pas le permettre. Le joueur doit pouvoir parier des monstres tant qu'il a suffisamment d'or.

Chèque d'or pour les monstres


Basculez l'IDE vers PlaceMonster.cs et remplacez le contenu de CanPlaceMonster() suit:

 int cost = monsterPrefab.GetComponent<MonsterData>().levels[0].cost; return monster == null && gameManager.Gold >= cost; 

Nous MonsterData prix de placement des monstres à partir des levels dans ses MonsterData . Ensuite, nous vérifions que le monster pas null et que gameManager.Gold supérieur à ce prix.

La tâche pour vous: ajouter indépendamment à CanUpgradeMonster() vérification si le joueur a suffisamment d'or.

Solution à l'intérieur
Remplacez la ligne:

 return true; 

à ce sujet:

 return gameManager.Gold >= nextLevel.cost; 

Il vérifiera si le joueur a plus d' or que le prix de mise à niveau.

Enregistrez et exécutez la scène dans Unity. Essayez maintenant de savoir comment ajouter des monstres à volonté!


Maintenant, nous ne pouvons construire qu'un nombre limité de monstres.

Politique de la tour: ennemis, vagues et waypoints


Il est temps de «préparer le terrain» à nos ennemis. Les ennemis apparaissent au premier point de l'itinéraire, passent au suivant et répètent le processus jusqu'à ce qu'ils atteignent le cookie.

Vous pouvez faire bouger les ennemis comme ceci:

  1. Définissez la route que les ennemis suivront
  2. Déplacez l'ennemi le long de la route
  3. Tournez l'ennemi pour qu'il regarde vers l'avant

Création d'une route à partir de waypoints


Cliquez avec le bouton droit sur la hiérarchie et sélectionnez Créer vide pour créer un nouvel objet de jeu vide. Nommez-le Road et placez-le à (0, 0, 0) .

Maintenant, faites un clic droit sur Road dans la hiérarchie et créez un autre objet de jeu vide en tant qu'enfant de Road. Nommez-le Waypoint0 et placez-le au point (-12, 2, 0) - à partir de là, les ennemis commenceront leur mouvement.


De même, créez cinq autres points d'itinéraire avec les noms et positions suivants:

  • Waypoint1: (X: 7, Y: 2, Z: 0)
  • Waypoint2: (X: 7, Y: -1, Z: 0)
  • Waypoint3: (X: -7,3, Y: -1, Z: 0)
  • Waypoint4: (X: -7,3, Y: -4,5, Z: 0)
  • Point de cheminement5: (X: 7, Y: -4,5, Z: 0)

La capture d'écran ci-dessous montre les points de l'itinéraire et le chemin résultant.


Se faire des ennemis


Créez maintenant des ennemis pour qu'ils puissent se déplacer le long de la route. Il y a un préfabriqué ennemi dans le dossier Prefabs . Sa position est (-20, 0, 0) , donc de nouvelles instances seront créées hors écran.

À tous les autres égards, il est configuré presque de la même manière que le préfabriqué Monster, a AudioSource et une filiale Sprite , et nous pouvons faire pivoter ce sprite à l'avenir sans tourner la barre de santé.


Nous déplaçons des ennemis le long de la route


Ajoutez un nouveau script C # appelé MoveEnemy au préfabriqué Prefabs \ Enemy . Ouvrez le script dans l'EDI et ajoutez les variables suivantes:

 [HideInInspector] public GameObject[] waypoints; private int currentWaypoint = 0; private float lastWaypointSwitchTime; public float speed = 1.0f; 

Dans les waypoints , une copie des waypoints est stockée dans le tableau, et la ligne [HideIn inspector ] au-dessus des waypoints garantit que nous ne pouvons pas modifier accidentellement ce champ dans l' inspecteur , mais y aurons toujours accès à partir d'autres scripts.

currentWaypoint conserve la trace de la route de l'ennemi à l'heure actuelle et lastWaypointSwitchTime stocke l'heure à laquelle l'ennemi l'a traversé. De plus, nous stockons la speed ennemi.

Ajoutez cette ligne à Start() :

 lastWaypointSwitchTime = Time.time; 

Nous initialisons donc lastWaypointSwitchTime avec la valeur de l'heure actuelle.

Pour que l'ennemi se déplace le long de l'itinéraire, ajoutez le code suivant à Update() :

 // 1 Vector3 startPosition = waypoints [currentWaypoint].transform.position; Vector3 endPosition = waypoints [currentWaypoint + 1].transform.position; // 2 float pathLength = Vector3.Distance (startPosition, endPosition); float totalTimeForPath = pathLength / speed; float currentTimeOnPath = Time.time - lastWaypointSwitchTime; gameObject.transform.position = Vector2.Lerp (startPosition, endPosition, currentTimeOnPath / totalTimeForPath); // 3 if (gameObject.transform.position.Equals(endPosition)) { if (currentWaypoint < waypoints.Length - 2) { // 3.a currentWaypoint++; lastWaypointSwitchTime = Time.time; // TODO:     } else { // 3.b Destroy(gameObject); AudioSource audioSource = gameObject.GetComponent<AudioSource>(); AudioSource.PlayClipAtPoint(audioSource.clip, transform.position); // TODO:   } } 

Analysons le code étape par étape:

  1. À partir du tableau de points d'itinéraire, nous obtenons les positions de début et de fin du segment d'itinéraire actuel.
  2. Nous calculons le temps nécessaire pour parcourir toute la distance en utilisant la formule temps = distance / vitesse , puis déterminons l'heure actuelle sur l'itinéraire. En utilisant Vector2.Lerp , nous interpolons la position actuelle de l'ennemi entre le segment exact de début et de fin.
  3. Vérifiez si l'ennemi a atteint endPosition . Si oui, alors nous traitons deux scénarios possibles:
    1. L'ennemi n'a pas encore atteint le dernier point de l'itinéraire, alors augmentez la valeur de currentWaypoint et mettez à jour lastWaypointSwitchTime . Plus tard, nous ajouterons un code pour retourner l'ennemi afin qu'il regarde dans la direction de son mouvement.
    2. L'ennemi a atteint le dernier point de la route, puis nous le détruisons et commençons l'effet sonore. Plus tard, nous ajouterons un code qui réduit la health du joueur.

Enregistrez le fichier et revenez à Unity.

Nous informons les ennemis de la direction du mouvement


Dans leur état actuel, les ennemis ne connaissent pas l'ordre des points de route.

Sélectionnez Road dans la hiérarchie et ajoutez un nouveau script C # appelé SpawnEnemy . Ouvrez-le dans l'EDI et ajoutez la variable suivante:

 public GameObject[] waypoints; 

Nous utiliserons des waypoints pour stocker les références au waypoint dans la scène dans l'ordre souhaité.

Enregistrez le fichier et revenez à Unity. Sélectionnez Route dans la hiérarchie et définissez la taille du tableau de waypoints sur 6 .

Faites glisser chacun des enfants Road dans les champs en collant Waypoint0 dans l' élément 0 , Waypoint1 dans l' élément 1, etc.


Nous avons maintenant un tableau contenant les points de route dans le bon ordre - attention, les ennemis ne reculent jamais, ils s'efforcent constamment d'obtenir une douce récompense.

Vérifiez comment tout cela fonctionne


Ouvrez SpawnEnemy dans l'EDI et ajoutez la variable suivante:

 public GameObject testEnemyPrefab; 

Il stockera une référence au testEnemyPrefab Enemy dans testEnemyPrefab .

Pour créer un ennemi lors de l'exécution du script, ajoutez le code suivant à Start() :

 Instantiate(testEnemyPrefab).GetComponent<MoveEnemy>().waypoints = waypoints; 

Nous allons donc créer une nouvelle copie du préfabriqué stocké dans testEnemy et lui affecter un itinéraire.

Enregistrez le fichier et revenez à Unity. Sélectionnez l'objet Route dans la hiérarchie et sélectionnez le préfabriqué ennemi pour le paramètre Test Enemy .

Lancez le projet et voyez comment l'ennemi se déplace le long de la route (en GIF, pour plus de clarté, la vitesse est augmentée de 20 fois).


Vous avez remarqué qu'il ne regarde pas toujours où il va? C'est drôle, mais nous essayons de créer un jeu professionnel. Par conséquent, dans la deuxième partie du didacticiel, nous apprendrons aux ennemis à regarder en avant.

Où aller ensuite?


Nous avons déjà beaucoup fait et nous nous dirigeons rapidement vers la création de notre propre jeu de tower defense.

Les joueurs peuvent créer un nombre limité de monstres, et l'ennemi court le long de la route, se dirigeant vers notre cookie. Les joueurs ont de l'or et ils peuvent améliorer les monstres.

Téléchargez le résultat final d'ici .

Dans la deuxième partie, nous considérerons la création d'énormes vagues d'ennemis et leur destruction. A très bientôt!

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


All Articles