Créez un jeu de défense de tour dans AS3 Ennemis et IA de base

Salut les développeurs Flash, bienvenue dans la deuxième partie de mon tutoriel sur le jeu Tower Defense. Dans la première partie, nous avons développé le mécanisme de base pour créer des tourelles et les faire tirer vers le point de clic de souris. Mais ce n'est pas pour ça que les tourelles! Dans cette partie, nous étendrons le jeu aux ennemis, à l'intelligence artificielle de base dans les tourelles et à d'autres éléments du jeu. Es-tu prêt?


Aperçu du résultat final

C'est le jeu que nous allons créer dans ce tutoriel:

Cliquez sur les cercles orange pour placer des tourelles. Les cercles rouges sont des ennemis et le nombre sur chacun représente ses points de vie..


Étape 1: récapitulation

Dans le précédent tutoriel, nous avons développé un jeu comportant des espaces réservés pour les tourelles. Nous pourrions déployer des tourelles en cliquant sur ces espaces réservés et les tourelles visant le pointeur de la souris et tirant des balles vers le point où l'utilisateur a cliqué.

Nous avons fini avec un Principale classe qui avait la boucle de jeu et la logique de jeu. En dehors de cela, nous avons eu la Tourelle classe qui n'avait rien beaucoup sauf la mettre à jour fonction qui fait tourner la tourelle.


Étape 2: Une classe de balle séparée

Nous avons précédemment créé les balles dans Principale classe et attaché un ENTER_FRAME auditeur pour le déplacer. La puce n'avait pas suffisamment de propriétés auparavant pour pouvoir en faire une classe séparée. Mais dans un tel jeu, les puces peuvent avoir de nombreuses variantes telles que la vitesse, les dégâts, etc. Il est donc judicieux d'extraire le code de balle et de l'encapsuler dans un fichier séparé. Balle classe. Faisons le.

Créez une nouvelle classe appelée Balle, étendre la Lutin classe. Le code de base pour cette classe devrait être:

 package import flash.display.Sprite; Classe publique Bullet étend Sprite fonction publique Bullet () 

Ensuite, nous mettons le code pour dessiner le graphique de balle, tiré de Principale, dans Balle. Comme nous l'avons fait avec le Tourelle classe, nous créons une fonction appelée dessiner dans le Balle classe:

 fonction privée draw (): void var g: Graphics = this.graphics; g.beginFill (0xEEEEEE); g.drawCircle (0, 0, 5); g.endFill (); 

Et nous appelons cette fonction du Balle constructeur:

 fonction publique Bullet () draw (); 

Maintenant, nous ajoutons quelques propriétés à la puce. Ajoutez quatre variables: la vitesse, speed_x, rapide et dommage, avant le Balle constructeur:

 private var speed: Number; private var speed_x: Nombre; private var speed_y: Number; dommages au public: int;

Quelles sont ces variables pour?

  • la vitesse: Cette variable stocke la vitesse de la balle.
  • speed_x et rapide: Ceux-ci stockent les composantes x et y de la vitesse, respectivement, de sorte que le calcul de la décomposition de la vitesse en ses composantes ne doit pas être fait encore et encore.
  • dommage: C'est la quantité de dégâts que la balle peut faire à un ennemi. Nous gardons cette variable publique car nous en aurons besoin dans notre boucle de jeu dans le Principale classe.

Nous initialisons ces variables dans le constructeur. Mettez à jour votre Balle constructeur:

 fonction publique Bullet (angle: Number) speed = 5; dommage = 1; speed_x = Math.cos (angle * Math.PI / 180) * vitesse; speed_y = Math.sin (angle * Math.PI / 180) * vitesse; dessiner(); 

Remarquez le angle variable que nous recevons dans le constructeur. C'est la direction (en degrés) dans laquelle la balle va se déplacer. Nous venons de casser le la vitesse dans ses composants x et y et les mettre en cache pour une utilisation future.

La dernière chose qui reste dans le Balle la classe est d'avoir un mettre à jour fonction qui sera appelée à partir de la boucle du jeu pour mettre à jour (déplacer) la balle. Ajoutez la fonction suivante à la fin du Balle classe:

 public function update (): void x + = speed_x; y + = speed_y; 

Bingo! Nous avons fini avec notre Balle classe.


Étape 3: Mise à jour de la classe principale

Nous avons déplacé beaucoup de code de balle de Principale classe à sa propre Balle classe, donc beaucoup de code reste inutilisé dans Principale et beaucoup doit être mis à jour.

Tout d'abord, supprimez le createBullet () et moveBullet () les fonctions. Enlevez aussi le bullet_speed variable.

Ensuite, allez au tirer fonction et le mettre à jour avec le code suivant:

 séance de fonction privée (e: MouseEvent): void pour chaque (tourelle var: tourelle dans des tourelles) var new_bullet: Bullet = new Bullet (tourelle.rotation); new_bullet.x = tourelle.x + Math.cos (tourelle.rotation * Math.PI / 180) * 25; new_bullet.y = tourelle.y + Math.sin (tourelle.rotation * Math.PI / 180) * 25; addChild (new_bullet); 

Nous n'utilisons plus le createBullet fonction pour créer une balle plutôt utiliser la Balle constructeur et passer la tourelle rotation à ce qui est la direction du mouvement de la balle et donc on n'a pas besoin de le stocker dans la balle rotation propriété comme nous l'avons fait plus tôt. De plus, nous n'attachons aucun auditeur à la puce car celle-ci sera mise à jour à partir de la boucle de jeu..


Étape 4: Sauvegarde des références de balle

Maintenant que nous avons besoin de mettre à jour les puces à partir de la boucle de jeu, nous avons besoin d’une référence pour les stocker quelque part. La solution est la même que pour les tourelles: créer un nouveau Tableau nommé balles et poussez les balles dessus comme ils sont créés.

Commencez par déclarer un tableau juste en dessous de tourelles déclaration de tableau:

 private var ghost_turret: Tourelle; Tourelles de var privées: Array = []; puces de variables privées: Array = [];

Maintenant pour peupler ce tableau. Nous le faisons chaque fois que nous créons une nouvelle puce - alors, dans le tirer une fonction. Ajoutez ce qui suit juste avant d'ajouter la puce à la scène:

 var new_bullet: Bullet = new Bullet (turret.rotation); new_bullet.x = tourelle.x + Math.cos (tourelle.rotation * Math.PI / 180) * 25; new_bullet.y = tourelle.y + Math.sin (tourelle.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet);

Étape 5: Mettre à jour les puces

Tout comme pour la mise à jour des tourelles dans la boucle du jeu, nous allons également mettre à jour les puces. Mais cette fois, au lieu d’utiliser un pour chaque boucle, nous allons utiliser une base pour boucle. Avant cela, nous devons ajouter deux variables en haut de la boucle de jeu afin de savoir quelles variables sont utilisées dans la boucle de jeu et de les libérer pour le ramassage des ordures..

 tourelle var: tourelle; var bullet: balle;

Allez-y et ajoutez le code suivant à la fin de la boucle de jeu:

 pour (var i: int = bullets.length - 1; i> = 0; i--) bullet = balles [i]; si (! bullet) continue; bullet.update (); 

Nous parcourons ici toutes les balles de la scène et appelons leur mettre à jour fonction qui les fait bouger. Notez ici que nous itérons la balles tableau en sens inverse. Pourquoi? Nous verrons cela à venir.

Maintenant que nous avons un tourelle déjà déclarée variable à l'extérieur, nous n'avons pas besoin de la déclarer à nouveau à l'intérieur du pour chaque boucle de tourelles. Modifiez-le pour:

 pour chacun (tourelle dans des tourelles) tourelle.update (); 

Enfin, nous ajoutons la condition de vérification des limites; c'était auparavant dans la balle ENTER_FRAME mais maintenant nous vérifions cela dans la boucle du jeu:

 si (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (bullet); continuer; 

Nous vérifions si la balle est hors des limites de la scène et, dans l’affirmative, retirons d’abord sa référence de la scène. balles tableau en utilisant le épissure fonction, puis retirez la balle de la scène et passez à la prochaine itération. Voici à quoi devrait ressembler votre boucle de jeu:

 fonction privée gameLoop (e: Event): void var tourelle: Tourelle; var bullet: balle; pour chacun (tourelle dans des tourelles) tourelle.update ();  pour (var i: int = bullets.length - 1; i> = 0; i--) bullet = balles [i]; si (! bullet) continue; bullet.update (); 

Si vous exécutez maintenant le jeu, vous devriez avoir les mêmes fonctionnalités que dans la partie 1, avec un code beaucoup plus propre et organisé.


Étape 6: Présentation de l'ennemi

Nous ajoutons maintenant l’un des éléments les plus importants du jeu: l’ennemi. La première chose à faire est de créer une nouvelle classe nommée Ennemi étendre la Lutin classe:

 package import flash.display.Sprite; public class Enemy étend Sprite fonction publique Enemy () 

Nous ajoutons maintenant quelques propriétés à la classe. Ajoutez-les avant votre Ennemi constructeur:

 private var speed_x: Nombre; private var speed_y: Number;

Nous initialisons ces variables dans le Ennemi constructeur:

 fonction publique Enemy () speed_x = -1.5; speed_y = 0; 

Ensuite, nous créons le dessiner et mettre à jour fonctions pour le Ennemi classe. Ce sont très similaires à ceux de Balle. Ajoutez le code suivant:

 fonction privée draw (): void var g: Graphics = this.graphics; g.beginFill (0xff3333); g.drawCircle (0, 0, 15); g.endFill ();  public function update (): void x + = speed_x; y + = speed_y; 

Étape 7: Calendrier des événements de jeu

Dans notre jeu, nous devons avoir de nombreux événements qui se déroulent à certains moments ou de manière répétée à certains intervalles. Un tel chronométrage peut être réalisé à l'aide d'un compteur de temps. Le compteur est juste une variable qui s'incrémente au fil du temps. La chose importante ici est de savoir quand et de combien incrémenter le compteur. Le chronométrage est généralement effectué de deux manières dans les jeux: basé sur le temps et basé sur le cadre.

La différence est que l’unité du jeu basé sur le temps est basée sur le temps réel (c’est-à-dire le nombre de millisecondes écoulées), mais dans un jeu basé sur les images, l’unité de pas est basée sur les unités de jeu (c.-à-d. Le nombre d’images transmises)..

Pour notre jeu, nous allons utiliser un compteur basé sur les images. Nous aurons un compteur que nous incrémenterons d’un point dans la boucle de jeu, qui exécute chaque image, ce qui nous donnera essentiellement le nombre d’images qui se sont écoulées depuis le début du jeu. Allez-y et déclarez une variable après les autres déclarations de variables du Principale classe:

 private var ghost_turret: Tourelle; Tourelles de var privées: Array = []; puces de variables privées: Array = []; private var global_time: Number = 0;

Nous incrémentons cette variable dans la boucle de jeu en haut:

 global_time ++;

Maintenant, sur la base de ce compteur, nous pouvons faire des choses comme créer des ennemis, ce que nous ferons ensuite.


Étape 8: Créons des ennemis

Ce que nous voulons faire maintenant, c'est créer des ennemis sur le terrain toutes les deux secondes. Mais nous avons affaire à des cadres ici, tu te souviens? Donc après combien d'images devrions-nous créer des ennemis? Eh bien, notre jeu tourne à 30 FPS, augmentant ainsi la global_time contrer 30 fois par seconde. Un simple calcul nous dit que 3 secondes = 90 images.

À la fin de la boucle de jeu, ajoutez ce qui suit si bloc:

 if (global_time% 90 == 0) 

De quoi s'agit-il? Nous utilisons l'opérateur modulo (%), qui donne le reste d'une division - donc global_time% 90 nous donne le reste quand global_time est divisé par 90. Nous vérifions si le reste est 0, comme ce ne sera le cas que lorsque global_time est un multiple de 90 - c'est-à-dire que la condition revient vrai quand global_time équivaut à 0, 90, 180 et ainsi de suite… Ainsi, nous obtenons un déclencheur toutes les 90 images ou toutes les 3 secondes.

Avant de créer l'ennemi, déclarez un autre tableau appelé ennemis juste en dessous de la tourelles et balles tableau. Ceci sera utilisé pour stocker des références aux ennemis sur la scène.

 private var ghost_turret: Tourelle; Tourelles de var privées: Array = []; puces de variables privées: Array = []; ennemis var privés: Array = []; private var global_time: Number = 0;

Aussi déclarer un ennemi variable en haut de la boucle de jeu:

 global_time ++; tourelle var: tourelle; var bullet: balle; var ennemi: Enemy;

Enfin, ajoutez le code suivant à l’intérieur du si bloquer nous avons créé plus tôt:

 ennemi = nouvel ennemi (); ennemi.x = 410; ennemi.y = 30 + Math.random () * 370; ennemis.push (ennemi); addChild (ennemi);

Ici, nous créons un nouvel ennemi, le positionnons au hasard à la droite de la scène, le poussons dans le ennemis tableau et l'ajouter à la scène.


Étape 9: Mise à jour des ennemis

Tout comme nous mettons à jour les balles dans la boucle du jeu, nous mettons à jour les ennemis. Placez le code suivant sous la tourelle pour chaque boucle:

 pour (var j: int = ennemis.longueur - 1; j> = 0; j--) ennemi = ennemis [j]; ennemi.update (); si (ennemi.x < 0)  enemies.splice(j, 1); enemy.parent.removeChild(enemy); continue;  

Tout comme nous avons fait une vérification des limites pour les balles, nous vérifions aussi les ennemis. Mais pour les ennemis, nous vérifions simplement s'ils sont sortis du côté gauche de la scène, car ils ne bougent que de droite à gauche. Vous devriez voir les ennemis venant de droite si vous lancez le jeu maintenant.


Étape 10: Donnez de la santé aux ennemis

Chaque ennemi a une vie / santé et la nôtre aussi. Nous allons également montrer la santé restante sur les ennemis. Permet de déclarer des variables dans le Ennemi classe pour le truc de la santé:

 private var health_txt: TextField; santé privée var: int; private var speed_x: Nombre; private var speed_y: Number;

Nous initialisons le santé variable dans le constructeur suivant. Ajouter ce qui suit au Ennemi constructeur:

 santé = 2;

Nous initialisons maintenant la variable de texte de santé à afficher sur le centre de l'ennemi. Nous le faisons dans le dessiner une fonction:

 health_txt = new TextField (); health_txt.height = 20; health_txt.width = 15; health_txt.textColor = 0xffffff; health_txt.x = -5; health_txt.y = -8; health_txt.text = health + ""; addChild (health_txt);

Tout ce que nous faisons est de créer un nouveau Champ de texte, définir sa couleur, le positionner et définir son texte à la valeur actuelle de santé Enfin, nous ajoutons une fonction pour mettre à jour la santé de l'ennemi:

 fonction publique updateHealth (montant: int): int santé + = montant; health_txt.text = health + ""; retourner la santé; 

La fonction accepte un entier à ajouter à la santé, met à jour le texte de santé et renvoie la santé finale. Nous allons appeler cette fonction depuis notre boucle de jeu pour mettre à jour la santé de chaque ennemi et détecter s'il est toujours en vie..


Étape 11: tirer sur les ennemis.

D'abord modifions notre tirer fonctionne un peu. Remplacer l'existant tirer fonctionner avec ce qui suit:

 séance de fonction privée (tourelle: tourelle, ennemi: ennemi): vide angle var: nombre = Math.atan2 (ennemi.y - tourelle.y, ennemi.x - tourelle.x) / Math.PI * 180; tourelle.rotation = angle; var new_bullet: Bullet = new Bullet (angle); new_bullet.x = tourelle.x + Math.cos (tourelle.rotation * Math.PI / 180) * 25; new_bullet.y = tourelle.y + Math.sin (tourelle.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet); 

le tirer fonction accepte maintenant deux paramètres. Le premier est une référence à une tourelle qui fera le tir; le second est une référence à un ennemi vers lequel il va tirer.

Le nouveau code ici est similaire à celui présent dans le Tourelle la classe mettre à jour fonction, mais au lieu de la position de la souris, nous utilisons maintenant les cordinates de l'ennemi. Alors maintenant, vous pouvez supprimer tout le code de la mettre à jour fonction de la Tourelle classe.

Maintenant, comment faire tirer les tourelles sur les ennemis? La logique est simple pour notre jeu. Nous faisons toutes les tourelles tirer le premier ennemi dans le ennemis tableau. Quoi? Mettons du code et essayons de comprendre. Additionnez les lignes suivantes à la fin du pour chaque boucle utilisée pour mettre à jour les tourelles:

 pour chacun (tourelle dans des tourelles) tourelle.update (); pour chaque (ennemi dans les ennemis) tirer (tourelle, ennemi); Pause; 

Pour chaque tourelle, nous la mettons à jour, puis itérons la ennemis tableau, tirez sur le premier ennemi du tableau et sortez de la boucle. En gros, chaque tourelle tire au premier ennemi créé, comme elle se trouve toujours au début du tableau. Essayez de lancer le jeu et vous devriez voir des tourelles tirer sur les ennemis.

Mais attendez, quel est ce flux de balle qui coule? On dirait qu'ils tirent trop vite. Voyons pourquoi.


Étape 12: Les tourelles tirent trop vite

Comme nous le savons, la boucle de jeu exécute chaque image, c'est-à-dire 30 fois par seconde dans notre cas. La déclaration de tir que nous avons ajoutée à l'étape précédente est appelée à la vitesse de notre boucle de jeu et nous voyons donc un flot de balles couler. On dirait que nous avons également besoin d'un mécanisme de synchronisation à l'intérieur des tourelles. Basculer vers le Tourelle class et ajoutez le code suivant:

 private var local_time: Number = 0; var privée reload_time: int;
  1. heure locale: Notre comptoir s'appelle heure locale contrairement à la global_time dans le Principale classe. C’est pour deux raisons: premièrement, parce que cette variable est locale à la Tourelle classe; deuxièmement, parce que cela ne va pas toujours comme notre global_time variable - il sera réinitialisé plusieurs fois au cours de la partie.
  2. Temps de rechargement: C'est le temps requis par la tourelle pour recharger après avoir tiré une balle. En gros, c’est la différence de temps entre deux tirs de balle par une tourelle. N'oubliez pas que toutes les unités de temps dans notre jeu sont exprimées en termes de cadres..

Incrémenter le heure locale variable dans le mettre à jour fonction et initialiser le Temps de rechargement dans le constructeur:

 fonction publique update (): void local_time ++; 
 fonction publique Turret () reload_time = 30; dessiner(); 

Ajoutez ensuite les deux fonctions suivantes à la fin du Tourelle classe:

 fonction publique isReady (): Boolean return local_time> reload_time;  fonction publique reset (): void local_time = 0; 

est prêt renvoie vrai que lorsque le courant heure locale est supérieure à la Temps de rechargement, c'est-à-dire lorsque la tourelle a été rechargée. Et le réinitialiser la fonction réinitialise simplement le heure locale variable, pour relancer le rechargement.

Maintenant dans le Principale classe, modifiez le code de tournage dans la boucle de jeu que nous avons ajouté à l'étape précédente comme suit:

 pour chacun (tourelle dans des tourelles) tourelle.update (); si (! tourelle.isReady ()) continue; pour chaque (ennemi dans les ennemis) tirer (tourelle, ennemi); turret.reset (); Pause; 

Donc si maintenant la tourelle n’est pas prête (est prêt() résultats faux), nous continuons avec la prochaine itération de la boucle de la tourelle. Vous verrez que les tourelles se déclenchent à un intervalle de 30 images ou 1 seconde maintenant. Cool!


Étape 13: Limitez la portée de la tourelle

Encore quelque chose qui ne va pas. Les tourelles tirent sur les ennemis quelle que soit la distance qui les sépare. Ce qui manque ici c'est le intervalle d'une tourelle. Chaque tourelle doit avoir sa propre plage dans laquelle elle peut tirer sur un ennemi. Ajouter une autre variable à la Tourelle classe appelée intervalle et le mettre à 120 à l'intérieur du constructeur:

 var privée reload_time: int; private var local_time: Number = 0; plage privée var: int;
 fonction publique Turret () reload_time = 30; plage = 120; dessiner(); 

Ajoutez également une fonction appelée CanShoot à la fin du cours:

 fonction publique canShoot (ennemi: Enemy): Boolean var dx: Number = ennemi.x - x; var dy: Nombre = ennemi.y - y; if (Math.sqrt (dx * dx + dy * dy) <= range) return true; else return false; 

Chaque tourelle ne peut tirer sur un ennemi que si elle remplit certains critères. Par exemple, vous pouvez laisser la tourelle ne tirer que sur des ennemis rouges dont la vie est réduite à la moitié de leur durée de vie et à une trentaine de pixels au maximum. Toute cette logique pour déterminer si la tourelle est capable de tirer un ennemi ou non ira dans le CanShoot fonction, qui retourne vrai ou faux selon la logique.

Notre logique est simple. Si l'ennemi est dans la plage retour vrai; sinon retourne faux. Alors, quand la distance entre la tourelle et l'ennemi (Math.sqrt (dx * dx + dy * dy)) est inférieur ou égal à intervalle, il revient vrai. Un peu plus de modification dans la section tournage de la boucle de jeu:

 pour chacun (tourelle dans des tourelles) tourelle.update (); si (! tourelle.isReady ()) continue; pour chaque ennemi ennemi if (tourelle.canShoot (ennemi)) tir (tourelle, ennemi); turret.reset (); Pause; 

Maintenant, ce n’est que si l’ennemi est à portée de la tourelle que celle-ci tire.


Étape 14: Détection de collision

La détection des collisions est une partie très importante de chaque match. Dans notre jeu, le contrôle des collisions est effectué entre les balles et les ennemis. Nous ajouterons le code de détection de collision à l'intérieur du pour chaque boucle qui met à jour les balles dans la boucle du jeu.

La logique est simple. Pour chaque balle nous traversons la ennemis tableau et vérifier s'il y a une collision entre eux. Si c'est le cas, nous retirons la balle, mettons à jour la santé de l'ennemi et sortons de la boucle pour contrôler les autres ennemis. Ajoutons du code:

 pour (i = balles.longueur - 1; i> = 0; i--) balle = balles [i]; // si la puce n'est pas définie, continuez avec la prochaine itération if (! bullet) continue; bullet.update (); si (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (bullet); continuer;  pour (var k: int = ennemis.longueur - 1; k> = 0; k--) ennemi = ennemis [k]; if (bullet.hitTestObject (ennemi)) bullets.splice (i, 1); bullet.parent.removeChild (bullet); if (ennemi.updateHealth (-1) == 0) ennemis.splice (k, 1); ennemi.parent.removeChild (ennemi);  Pause; 

Nous utilisons ActionScript hitTestObject fonction pour vérifier la collision entre la balle et l'ennemi. Si la collision se produit, la balle est retirée de la même manière que quand elle quitte la scène. La santé de l'ennemi est ensuite mise à jour à l'aide du updateHealth méthode, à laquelle ballede dommage la propriété est passée. Si la updateHealth la fonction retourne un entier inférieur ou égal à 0, cela signifie que l'ennemi est mort et nous l'enlevons de la même manière que la balle.

Et notre détection de collision est terminée!


Étape 15: Pourquoi inverser les boucles "Pour"?

Rappelez-vous que nous traversons les ennemis et les balles en sens inverse dans notre boucle de jeu. Comprenons pourquoi. Supposons que nous utilisions un ascendant pour boucle. Nous sommes sur l'index i = 3 et nous retirons une balle du tableau. Enlèvement de l'article à la position 3, son espace est rempli par l'élément alors à la position 4. Alors maintenant, l'élément précédemment à la position 4 est à 3. Après l'itération je incréments de 1 et devient 4 et ainsi article à la position 4 est vérifié.

Oops, vous voyez ce qui s'est passé tout à l'heure? Nous avons juste manqué l'article maintenant à la position 3 qui a reculé à la suite de l'épissure. Et si nous utilisons un inverse pour boucle qui supprime ce problème. Vous pouvez voir pourquoi.


Étape 16: Affichage de la portée de la tourelle

Ajoutons des éléments supplémentaires pour que le jeu soit beau. Nous ajouterons des fonctionnalités pour afficher la portée d'une tourelle lorsque la souris est survolée. Basculer vers le Tourelle classe et y ajouter quelques variables:

 plage privée var: int; var privée reload_time: int; private var local_time: Number = 0; corps var privé: Sprite; private var range_circle: Sprite;

Prochaine mise à jour le dessiner fonction à la suivante:

 fonction privée draw (): void range_circle = new Sprite (); g = range_circle.graphics; g.beginFill (0x00D700); g.drawCircle (0, 0, plage); g.endFill (); range_circle.alpha = 0,2; range_circle.visible = false; addChild (range_circle); body = new Sprite (); var g: Graphics = body.graphics; g.beginFill (0xD7D700); g.drawCircle (0, 0, 20); g.beginFill (0x800000); g.drawRect (0, -5, 25, 10); g.endFill (); addChild (corps); 

Nous décomposons les graphiques de la tourelle en deux parties: le corps et le graphique de la plage. Nous faisons cela afin de donner un ordre aux différentes parties de la tourelle. Ici nous avons besoin du range_circle être derrière le corps de la tourelle, et nous l’ajoutons d’abord à la scène. Enfin, nous ajoutons deux écouteurs à la souris pour basculer le graphique de la plage:

 fonction privée onMouseOver (e: MouseEvent): void range_circle.visible = true;  fonction privée onMouseOut (e: MouseEvent): void range_circle.visible = false; 

Attachez maintenant les écouteurs aux événements respectifs à la fin du constructeur:

 body.addEventListener (MouseEvent.MOUSE_OVER, onMouseOver); body.addEventListener (MouseEvent.MOUSE_OUT, onMouseOut);

Si vous lancez le jeu et essayez de déployer une tourelle, vous verrez un scintillement lorsque vous passez la souris sur les espaces réservés. Pourquoi donc?


Voir le scintillement?

Étape 17: Suppression du scintillement

Rappelez-vous que nous avons défini le mouseEnabled propriété de la tourelle fantôme faux? Nous l'avons fait parce que la tourelle fantôme capturait les événements de la souris en entrant entre la souris et l'espace réservé. La même situation est à nouveau arrivée, car la tourelle elle-même a deux enfants maintenant, son corps et le sprite de la plage, qui capturent les événements de souris..

La solution est la même. Nous pouvons définir leur individu mouseEnabled propriétés à faux. Mais une meilleure solution consiste à régler la tourelle fantôme sourisEnfants propriété à faux. Cela empêche tous les enfants de la tourelle fantôme de recevoir des événements de souris. Neat, hein? Allez-y et réglez-le sur faux dans le Principale constructeur:

 ghost_turret = new Turret (); ghost_turret.alpha = 0,5; ghost_turret.mouseEnabled = false; ghost_turret.mouseChildren = false; ghost_turret.visible = false; addChild (ghost_turret);

Problème résolu.

Étape 18: Quelle suite??

Nous pourrions étendre cette démonstration pour y inclure des fonctionnalités beaucoup plus avancées et en faire un jeu jouable. Certains d'entre eux pourraient être:

  1. Meilleure logique de l'IA pour sélectionner et tirer sur les ennemis.
  2. Différents types de tourelles, balles et ennemis dans le jeu.
  3. Chemins ennemis complexes au lieu de lignes droites.

Voyons ce que vous pouvez trouver avec cette démo de base. Je serai heureux d’entendre parler de vos jeux de tower defense, ainsi que de vos commentaires ou suggestions pour la série..