Crea un gioco di difesa della torre in AS3 nemici e IA di base

Ehi, sviluppatori Flash, benvenuto alla seconda parte del mio tutorial sul gioco Tower Defense. Nella prima parte, abbiamo sviluppato il meccanismo di base per creare torrette e farle scattare verso il punto di click del mouse. Ma non è quello che sono le torrette! In questa parte estenderemo il gioco includendo i nemici, l'intelligenza artificiale di base (AI) nelle torrette e altri elementi di gioco. Siete pronti?


Anteprima del risultato finale

Questo è il gioco che stiamo per creare in questo tutorial:

Fai clic sui cerchi arancioni per posizionare le torrette. Le cerchie rosse sono nemiche e il numero su ciascuna rappresenta i punti ferita.


Passaggio 1: riassociare

Nel tutorial precedente abbiamo sviluppato un gioco con segnaposto per le torrette. Potremmo distribuire le torrette facendo clic su quei segnaposti e le torrette puntano al puntatore del mouse e sparano proiettili verso il punto in cui l'utente ha fatto clic.

Abbiamo finito con a Principale classe che aveva il ciclo di gioco e la logica di gioco. A parte questo, abbiamo avuto il Torretta classe che non aveva nulla se non il aggiornare funzione che ha fatto ruotare la torretta.


Passaggio 2: una classe di proiettili separata

In precedenza abbiamo creato i proiettili Principale classe e allegato un ENTER_FRAME ascoltatore per spostarlo. Il proiettile non aveva abbastanza proprietà in precedenza da considerarlo come una classe separata. Ma in un gioco del genere i proiettili possono avere molte varietà come velocità, danni e così via, quindi è una buona idea estrarre il codice del proiettile e incapsularlo in un altro proiettile classe. Facciamolo.

Crea una nuova classe chiamata proiettile, estendendo il folletto classe. Il codice di base per questa classe dovrebbe essere:

 pacchetto import flash.display.Sprite; Bullet classe pubblica estende Sprite Bullet funzione pubblica () 

Quindi inseriamo il codice per disegnare la grafica del proiettile, presa da Principale, nel proiettile. Come abbiamo fatto con Torretta classe, creiamo una funzione chiamata disegnare nel proiettile classe:

 private function draw (): void var g: Graphics = this.graphics; g.beginFill (0xEEEEEE); g.drawCircle (0, 0, 5); g.endFill (); 

E chiamiamo questa funzione dal proiettile costruttore:

 funzione pubblica Bullet () draw (); 

Ora aggiungiamo alcune proprietà al proiettile. Aggiungi quattro variabili: velocità, speed_x, speed_y e danno, prima di proiettile costruttore:

 velocità var privata: numero; private var speed_x: Number; private var speed_y: Number; danno pubblico var: int;

A cosa servono queste variabili?

  • velocità: Questa variabile memorizza la velocità del proiettile.
  • speed_x e speed_y: Memorizzano le componenti x e y della velocità, rispettivamente, in modo che il calcolo della rottura della velocità nei suoi componenti non debba essere ripetuto più volte.
  • danno: Questa è la quantità di danno che il proiettile può infliggere a un nemico. Manteniamo questa variabile pubblica come sarà necessario nel nostro ciclo di gioco nel Principale classe.

Inizializziamo queste variabili nel costruttore. Aggiorna il tuo proiettile costruttore:

 funzione pubblica Bullet (angle: Number) speed = 5; danno = 1; speed_x = Math.cos (angle * Math.PI / 180) * velocità; speed_y = Math.sin (angle * Math.PI / 180) * velocità; disegnare(); 

Notare il angolo variabile che riceviamo nel costruttore. Questa è la direzione (in gradi) in cui si muoverà il proiettile. Abbiamo appena rotto il velocità nei suoi componenti xey e li memorizza nella cache per un uso futuro.

L'ultima cosa che rimane nel proiettile la classe è avere un aggiornare funzione che verrà chiamata dal loop di gioco per aggiornare (spostare) il proiettile. Aggiungi la seguente funzione alla fine del proiettile classe:

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

Bingo! Abbiamo finito con il nostro proiettile classe.


Passaggio 3: Aggiornamento della classe principale

Abbiamo spostato un sacco di codice bullet da Principale classe a sé stante proiettile classe, quindi un sacco di codice rimane inutilizzato in Principale e molto deve essere aggiornato.

Innanzitutto, elimina il createBullet () e moveBullet () funzioni. Rimuovere anche il velocità del proiettile variabile.

Quindi, vai al sparare funzione e aggiornarlo con il seguente codice:

 sparatoria di funzioni private (e: MouseEvent): void per ciascuna (torretta var: torretta nelle torrette) var new_bullet: Bullet = new Bullet (turret.rotation); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; addChild (new_bullet); 

Non usiamo più il createBullet funzione per creare proiettili piuttosto usare il proiettile costruttore e passare la torretta rotazione ad esso che è la direzione del movimento del proiettile e quindi non abbiamo bisogno di memorizzarlo nel proiettile rotazione proprietà come abbiamo fatto in precedenza. Inoltre, non assegniamo alcun ascoltatore al proiettile poiché il proiettile verrà aggiornato dal loop di gioco successivo.


Passaggio 4: Salvataggio dei riferimenti di punti elenco

Ora che abbiamo bisogno di aggiornare i proiettili dal ciclo di gioco, abbiamo bisogno di un riferimento di loro per essere memorizzati da qualche parte. La soluzione è la stessa delle torrette: creane una nuova schieramento di nome proiettili e spingere i proiettili su di esso mentre vengono creati.

Prima dichiarare un array appena sotto il torrette dichiarazione dell'array:

 private var ghost_turret: Turret; torrette private var: Array = []; pallottole private var: Array = [];

Ora per popolare questa matrice. Lo facciamo ogni volta che creiamo un nuovo proiettile - quindi, nel sparare funzione. Aggiungi quanto segue prima di aggiungere il proiettile allo stage:

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

Passaggio 5: aggiornare i punti elenco

Proprio come aggiorneremo le torrette nel ciclo di gioco, aggiorneremo anche i proiettili. Ma questa volta, invece di usare a per ciascuno loop, useremo un basic per ciclo continuo. Prima di questo, dobbiamo aggiungere due variabili nella parte superiore del ciclo di gioco, in modo da sapere quali variabili sono utilizzate all'interno del ciclo di gioco e possiamo renderle libere per la garbage collection.

 var torretta: torretta; var bullet: Bullet;

Vai avanti e aggiungi il seguente codice alla fine del ciclo di gioco:

 for (var i: int = bullets.length - 1; i> = 0; i--) bullet = bullet [i]; se (! bullet) continua; bullet.update (); 

Qui attraversiamo tutti i proiettili sul palco ogni frame e chiamiamo i loro aggiornare funzione che li fa muovere. Nota qui che iteriamo il proiettili array al contrario. Perché? Lo vedremo in anticipo.

Ora che abbiamo un torretta variabile dichiarata all'esterno, non è necessario dichiararla di nuovo all'interno di per ciascuno ciclo di torrette. Modificalo per:

 per ogni (torretta nelle torrette) turret.update (); 

Infine aggiungiamo la condizione di controllo dei confini; questo era in precedenza nel proiettile ENTER_FRAME ma ora lo controlliamo nel ciclo di gioco:

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

Controlliamo se il proiettile è fuori dal limite del palcoscenico e, in tal caso, prima rimuoviamo il suo riferimento dal proiettili array usando il giuntura funzione, quindi rimuovere il proiettile dallo stage e continuare con l'iterazione successiva. Ecco come dovrebbe apparire il tuo ciclo di gioco:

 funzione privata gameLoop (e: Event): void var torretta: Turret; var bullet: Bullet; per ogni (torretta nelle torrette) turret.update ();  for (var i: int = bullets.length - 1; i> = 0; i--) bullet = bullet [i]; se (! bullet) continua; bullet.update (); 

Se ora esegui il gioco, dovresti avere le stesse funzionalità della Parte 1, con un codice molto più pulito e organizzato.


Passaggio 6: presentazione del nemico

Ora aggiungiamo uno degli elementi più importanti del gioco: il nemico. La prima cosa è creare una nuova classe chiamata Nemico estendendo il folletto classe:

 pacchetto import flash.display.Sprite; public class Enemy estende Sprite public function Enemy () 

Ora aggiungiamo alcune proprietà alla classe. Aggiungili prima del tuo Nemico costruttore:

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

Inizializziamo queste variabili nel Nemico costruttore:

 funzione pubblica Enemy () speed_x = -1.5; speed_y = 0; 

Quindi creiamo il disegnare e aggiornare funzioni per il Nemico classe. Questi sono molto simili a quelli di proiettile. Aggiungi il seguente codice:

 private function 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; 

Step 7: cronometrare gli eventi del gioco

Nel nostro gioco abbiamo bisogno di avere molti eventi che si svolgono in determinati momenti o ripetutamente a determinati intervalli. Tale tempistica può essere raggiunta utilizzando un contatore del tempo. Il contatore è solo una variabile che viene incrementata con il passare del tempo nel gioco. La cosa importante qui è quando e da quanto ammontare per incrementare il contatore. Esistono due modi in cui il tempo viene generalmente eseguito nei giochi: basato sul tempo e basato sul frame.

La differenza è che l'unità del gioco basato sul passo nel tempo è basata sul tempo reale (cioè il numero di millisecondi passati), ma in un gioco basato su frame, l'unità di passo è basata su unità di frame (cioè il numero di frame passati).

Per il nostro gioco useremo un contatore basato su frame. Avremo un contatore che incrementeremo di uno nel ciclo di gioco, che esegue ogni fotogramma, quindi ci darà il numero di fotogrammi che sono passati dall'inizio del gioco. Vai avanti e dichiara una variabile dopo le altre dichiarazioni di variabili nel file Principale classe:

 private var ghost_turret: Turret; torrette private var: Array = []; pallottole private var: Array = []; private var global_time: Number = 0;

Aumentiamo questa variabile nel ciclo di gioco in alto:

 global_time ++;

Ora basato su questo segnalino possiamo fare cose come creare nemici, cosa che faremo dopo.


Passaggio 8: Creiamo alcuni nemici

Quello che vogliamo fare ora è creare nemici sul campo dopo ogni due secondi. Ma qui abbiamo a che fare con i frame, ricordi? Quindi dopo quanti fotogrammi dovremmo creare nemici? Bene, il nostro gioco funziona a 30 FPS, incrementando così il global_time contrastare 30 volte al secondo. Un semplice calcolo ci dice che 3 secondi = 90 fotogrammi.

Alla fine del ciclo di gioco aggiungi quanto segue Se bloccare:

 if (global_time% 90 == 0) 

Di cosa tratta questa condizione? Usiamo l'operatore modulo (%), che fornisce il resto di una divisione - così global_time% 90 ci dà il resto quando global_time è diviso da 90. Controlliamo se il resto è 0, come questo sarà solo il caso in cui global_time è un multiplo di 90 - cioè, la condizione ritorna vero quando global_time è uguale a 0, 90, 180 e così via ... In questo modo, otteniamo un trigger ogni 90 frame o 3 secondi.

Prima di creare il nemico, dichiara un altro array chiamato nemici appena sotto il torrette e proiettili array. Questo sarà usato per memorizzare i riferimenti ai nemici sul palco.

 private var ghost_turret: Turret; torrette private var: Array = []; pallottole private var: Array = []; nemici privati ​​var: Array = []; private var global_time: Number = 0;

Dichiara anche un nemico variabile nella parte superiore del ciclo di gioco:

 global_time ++; var torretta: torretta; var bullet: Bullet; var nemico: nemico;

Infine aggiungi il seguente codice all'interno del Se blocco che abbiamo creato in precedenza:

 nemico = nuovo nemico (); enemy.x = 410; enemy.y = 30 + Math.random () * 370; enemies.push (nemico); addChild (nemico);

Qui creiamo un nuovo nemico, posizionalo casualmente alla destra del palco, spingilo dentro nemici array e aggiungilo al palco.


Passaggio 9: Aggiornamento dei nemici

Proprio come aggiorniamo i proiettili nel ciclo di gioco, aggiorniamo i nemici. Inserisci il seguente codice sotto la torretta per ciascuno ciclo continuo:

 per (var j: int = nemici.lunghezza - 1; j> = 0; j--) nemico = nemici [j]; enemy.update (); se (nemico.x < 0)  enemies.splice(j, 1); enemy.parent.removeChild(enemy); continue;  

Proprio come abbiamo fatto un controllo di confine per i proiettili, controlliamo anche i nemici. Ma per i nemici controlliamo solo se sono usciti dal lato sinistro del palco, poiché si muovono solo da destra a sinistra. Dovresti vedere i nemici provenienti da destra se corri il gioco ora.


Passo 10: dai dei nemici un po 'di salute

Ogni nemico ha una vita / salute e anche il nostro. Mostreremo anche la salute rimanente sui nemici. Dichiariamo alcune variabili nel file Nemico classe per la salute:

 private var health_txt: TextField; private var health: int; private var speed_x: Number; private var speed_y: Number;

Inizializziamo il Salute variabile nel costruttore successivo. Aggiungi il seguente al Nemico costruttore:

 salute = 2;

Ora inizializziamo la variabile del testo di salute da mostrare al centro del nemico. Lo facciamo nel disegnare funzione:

 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);

Tutto ciò che facciamo è creare un nuovo Campo di testo, imposta il suo colore, posizionalo e imposta il suo testo sul valore corrente di Salute Infine aggiungiamo una funzione per aggiornare la salute del nemico:

 funzione pubblica updateHealth (amount: int): int health + = amount; health_txt.text = health + ""; restituire la salute; 

La funzione accetta un numero intero da aggiungere alla salute, aggiorna il testo di integrità e restituisce lo stato di integrità finale. Chiameremo questa funzione dal nostro ciclo di gioco per aggiornare la salute di ogni nemico e rilevare se è ancora vivo.


Step 11: Sparare ai nemici.

Prima di tutto, modifica il nostro sparare funziona un po '. Sostituisci l'esistente sparare funzione con il seguente:

 tiro a funzioni private (torretta: torretta, nemico: nemico): void var angle: Number = Math.atan2 (enemy.y - torretta.y, enemy.x - torretta.x) / Math.PI * 180; turret.rotation = angle; var new_bullet: Bullet = new Bullet (angle); new_bullet.x = turret.x + Math.cos (turret.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin (turret.rotation * Math.PI / 180) * 25; bullets.push (new_bullet); addChild (new_bullet); 

Il sparare la funzione ora accetta due parametri. Il primo è un riferimento a una torretta che farà le riprese; il secondo è un riferimento a un nemico verso il quale sparerà.

Il nuovo codice qui è simile a quello presente nel Torretta La classe di aggiornare funzione, ma al posto della posizione del mouse ora usiamo le coordinate del nemico. Quindi ora puoi rimuovere tutto il codice dal aggiornare funzione del Torretta classe.

Ora come far sparare le torrette ai nemici? Bene, la logica è semplice per il nostro gioco. Facciamo sparare a tutte le torrette il primo nemico nel nemici array. Che cosa? Mettiamo un po 'di codice e poi proviamo a capire. Aggiungi le seguenti righe alla fine del per ciascuno ciclo utilizzato per aggiornare le torrette:

 per ogni (torretta nelle torrette) turret.update (); per ciascuno (nemico nei nemici) spara (torretta, nemico); rompere; 

Per ogni torretta ora lo aggiorniamo, quindi iteriamo il nemici array, spara al primo nemico dell'array e interrompi il ciclo. Quindi, in sostanza, ogni torretta spara al primo nemico creato poiché è sempre all'inizio dell'array. Prova a correre e dovresti vedere le torrette che sparano ai nemici.

Ma aspetta, cos'è il flusso di proiettili che scorre? Sembra che stiano sparando troppo velocemente. Vediamo perché.


Step 12: Le torrette stanno girando troppo velocemente

Come sappiamo, il ciclo di gioco esegue ogni frame, vale a dire 30 volte al secondo nel nostro caso, quindi la frase di tiro che abbiamo aggiunto nel passaggio precedente viene chiamata alla velocità del nostro ciclo di gioco e quindi vediamo un flusso di proiettili che scorre. Sembra che abbiamo bisogno di un meccanismo di temporizzazione anche all'interno delle torrette. Passa al Torretta classe e aggiungi il seguente codice:

 private var local_time: Number = 0; private var reload_time: int;
  1. ora locale: Il nostro contatore è chiamato ora locale in contrasto con il global_time nel Principale classe. Questo è per due motivi: primo, perché questa variabile è locale al Torretta classe; secondo, perché non va sempre avanti come il nostro global_time variabile - si resetterà molte volte durante il corso del gioco.
  2. reload_time: Questo è il tempo richiesto dalla torretta per ricaricare dopo aver sparato un proiettile. Fondamentalmente è la differenza di tempo tra due colpi di proiettile di una torretta. Ricorda tutte le unità del tempo nel nostro gioco sono in termini di frame.

Incrementa il ora locale variabile nel aggiornare funzione e inizializza il reload_time nel costruttore:

 public function update (): void local_time ++; 
 funzione pubblica Turret () reload_time = 30; disegnare(); 

Quindi aggiungere le seguenti due funzioni alla fine del Torretta classe:

 public function isReady (): Boolean return local_time> reload_time;  public function reset (): void local_time = 0; 

è pronto restituisce true solo quando corrente ora locale è maggiore del reload_time, cioè quando la torretta si è ricaricata. E il reset la funzione semplicemente resetta il ora locale variabile, per avviarlo di nuovo.

Ora di nuovo in Principale classe, modifica il codice di ripresa nel loop di gioco che abbiamo aggiunto nel passaggio precedente al seguente:

 per ogni (torretta nelle torrette) turret.update (); se (! turret.isReady ()) continua; per ciascuno (nemico nei nemici) spara (torretta, nemico); turret.reset (); rompere; 

Quindi se ora la torretta non è pronta (è pronto() ritorna falso), continuiamo con la successiva iterazione del ciclo della torretta. Vedrai che le torrette sparano con un intervallo di 30 fotogrammi o 1 secondo ora. Freddo!


Passaggio 13: Limita l'intervallo della torretta

Ancora qualcosa non va bene. Le torrette sparano ai nemici indipendentemente dalla distanza tra loro. Quello che manca qui è il gamma di una torretta. Ogni torretta dovrebbe avere il proprio raggio all'interno del quale può sparare a un nemico. Aggiungi un'altra variabile al Torretta classe chiamata gamma e impostarlo 120 all'interno del costruttore:

 private var reload_time: int; private var local_time: Number = 0; range var privato: int;
 funzione pubblica Turret () reload_time = 30; intervallo = 120; disegnare(); 

Aggiungi anche una funzione chiamata canShoot alla fine della classe:

 public function canShoot (enemy: Enemy): Boolean var dx: Number = enemy.x - x; var dy: Number = enemy.y - y; if (Math.sqrt (dx * dx + dy * dy) <= range) return true; else return false; 

Ogni torretta può sparare a un nemico solo quando soddisfa determinati criteri - ad esempio, puoi lasciare che la torretta spari solo nemici rossi con meno della metà della loro vita e non più di 30px di distanza. Tutta questa logica per determinare se la torretta è in grado di sparare a un nemico o no andrà nel canShoot funzione, che restituisce vero o falso secondo la logica.

La nostra logica è semplice. Se il nemico è nel raggio di ritorno vero; altrimenti restituisci falso. Quindi quando la distanza tra la torretta e il nemico (Math.sqrt (dx * dx + dy * dy)) è inferiore o uguale a gamma, ritorna vero. Un po 'più di modifiche nella sezione shoot del loop di gioco:

 per ogni (torretta nelle torrette) turret.update (); se (! turret.isReady ()) continua; per ciascuno (nemico nei nemici) if (torretta.canaiotto (nemico)) spara (torretta, nemico); turret.reset (); rompere; 

Ora solo se il nemico si trova entro il raggio della torretta, la torretta scatterà.


Passaggio 14: rilevamento collisione

Una parte molto importante di ogni gioco è il rilevamento delle collisioni. Nel nostro gioco il controllo della collisione avviene tra proiettili e nemici. Aggiungeremo il codice di rilevamento delle collisioni all'interno di per ciascuno loop che aggiorna i proiettili nel loop di gioco.

La logica è semplice Per ogni proiettile attraversiamo il nemici array e controllare se c'è una collisione tra loro. Se è così, rimuoviamo il proiettile, aggiorniamo la salute del nemico e scappiamo dal ciclo per controllare gli altri nemici. Aggiungiamo un po 'di codice:

 for (i = bullets.length - 1; i> = 0; i--) bullet = bullet [i]; // se il punto non è definito, continuare con l'iterazione successiva se (! bullet) continua; bullet.update (); se (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (proiettile); Continua;  per (var k: int = nemici.lunghezza - 1; k> = 0; k--) nemico = nemici [k]; if (bullet.hitTestObject (nemico)) bullets.splice (i, 1); bullet.parent.removeChild (proiettile); if (enemy.updateHealth (-1) == 0) enemy.splice (k, 1); enemy.parent.removeChild (nemico);  rompere; 

Usiamo ActionScript hitTestObject funzione per verificare la collisione tra il proiettile e il nemico. Se si verifica la collisione, il proiettile viene rimosso allo stesso modo di quando lascia il palco. La salute del nemico viene quindi aggiornata usando il updateHealth metodo, a cui proiettile'S danno la proprietà è passata. Se la updateHealth la funzione restituisce un numero intero inferiore o uguale a 0, questo significa che il nemico è morto e quindi lo rimuoviamo allo stesso modo del proiettile.

E il nostro rilevamento delle collisioni è fatto!


Passo 15: Perché invertire i cicli "For"?

Ricorda che attraversiamo i nemici e i proiettili al contrario nel nostro ciclo di gioco. Capiamo perché. Supponiamo di aver usato un ascendente per ciclo continuo. Siamo su indice i = 3 e rimuoviamo un proiettile dall'array. Sulla rimozione dell'articolo in posizione 3, il suo spazio è riempito dall'elemento quindi in posizione 4. Quindi ora l'articolo precedentemente in posizione 4 è a 3. Dopo l'iterazione io aumenta di 1 e diventa 4 e così oggetto in posizione 4 è controllato.

Oops, vedi cosa è successo proprio ora? Abbiamo appena perso l'oggetto ora in posizione 3 che si è spostato indietro come risultato dello splicing. E così usiamo un rovescio per ciclo che rimuove questo problema. Puoi vedere perché.


Passo 16: Visualizzazione della gamma della torretta

Aggiungiamo alcune cose extra per rendere il gioco un bell'aspetto. Aggiungeremo funzionalità per visualizzare l'intervallo di una torretta quando il mouse viene posizionato su di esso. Passa al Torretta classe e aggiungere alcune variabili ad esso:

 range var privato: int; private var reload_time: int; private var local_time: Number = 0; corpo var privato: Sprite; private var range_circle: Sprite;

Prossimo aggiornamento del disegnare funzione al seguente:

 private function draw (): void range_circle = new Sprite (); g = range_circle.graphics; g.beginFill (0x00D700); g.drawCircle (0, 0, range); 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 (corpo); 

Rompiamo la grafica della torretta in due parti: il corpo e la grafica della gamma. Lo facciamo in modo da dare un ordine alle diverse parti della torretta. Qui richiediamo il range_circle essere dietro il corpo della torretta, e così lo aggiungiamo prima al palco. Infine, aggiungiamo due listener del mouse per alternare la grafica dell'intervallo:

 funzione privata onMouseOver (e: MouseEvent): void range_circle.visible = true;  funzione privata onMouseOut (e: MouseEvent): void range_circle.visible = false; 

Ora allega gli ascoltatori ai rispettivi eventi alla fine del costruttore:

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

Se esegui il gioco e provi a schierare una torretta, vedrai uno sfarfallio quando tieni il mouse sui segnaposto. Perché?


Guarda lo sfarfallio?

Passaggio 17: rimozione dello sfarfallio

Ricorda che abbiamo impostato il mouseEnabled proprietà della torretta fantasma a falso? L'abbiamo fatto perché, la torretta fantasma stava catturando gli eventi del mouse entrando tra il mouse e il segnaposto. La stessa situazione è arrivata ancora una volta dato che la torretta in sé ha due figli - il suo corpo e lo sprite della gamma - che stanno catturando gli eventi del mouse tra.

La soluzione è la stessa. Possiamo impostare il loro individuo mouseEnabled proprietà a falso. Ma una soluzione migliore è impostare la torretta fantasma mouseChildren proprietà a falso. Ciò che fa è limitare tutti i bambini della torretta fantasma dalla ricezione di eventi del mouse. Pulito, eh? Vai avanti e impostalo su falso nel Principale costruttore:

 ghost_turret = new Turret (); ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; ghost_turret.mouseChildren = false; ghost_turret.visible = false; addChild (ghost_turret);

Problema risolto.

Passaggio 18: che cosa succederà?

Potremmo estendere questa demo per includere funzionalità molto più avanzate e trasformarla in un gioco giocabile. Alcuni dei quali potrebbero essere:

  1. Migliore logica AI per selezionare e sparare ai nemici.
  2. Diversi tipi di torrette, proiettili e nemici nel gioco.
  3. Percorsi nemici complessi invece di linee rette.

Vediamo cosa puoi inventare da questa demo di base. Sarò felice di conoscere i tuoi giochi di difesa della torre e i tuoi commenti o suggerimenti per la serie.