Maak een Tower Defense Game in AS3 Vijanden en Basic AI

Hallo Flash-ontwikkelaars, welkom bij het tweede deel van mijn Tower Defense Game-zelfstudie. In het eerste deel ontwikkelden we het basismechanisme om torentjes te maken en ze te laten schieten met het oog op muisklikken. Maar dat is niet waar torentjes voor zijn! In dit deel breiden we het spel uit met vijanden, basale kunstmatige intelligentie (AI) in torentjes en nog wat spelelementen. Ben je klaar?


Eindresultaat voorbeeld

Dit is de game die we gaan maken in deze tutorial:

Klik op de oranje cirkels om torentjes te plaatsen. De rode cirkels zijn vijanden en het aantal op elk vertegenwoordigt de hitpunten.


Stap 1: samenvatten

In de vorige tutorial hebben we een game ontwikkeld met plaatshouders voor de torentjes. We zouden torentjes kunnen inzetten door op die tijdelijke aanduidingen te klikken, en de torentjes gericht op de muisaanwijzer en geschoten kogels naar het punt waar de gebruiker klikte.

We zijn klaar met een Hoofd klasse die de spelloop en spellogica had. Afgezien daarvan hadden we de Torentje klas die niets anders had dan de bijwerken functie waardoor de toren draaide.


Stap 2: Een afzonderlijke Bullet-klasse

We hebben eerder de kogels gemaakt Hoofd klasse en bijgevoegd een ENTER_FRAME luisteraar om het te verplaatsen. De kogel had niet genoeg eigenschappen eerder om het als een afzonderlijke klasse te beschouwen. Maar in een dergelijk spel kunnen kogels allerlei soorten hebben, zoals snelheid, schade, enzovoort. Het is dus een goed idee om de opsommingscode op te halen en deze in een afzonderlijke kapsule te plaatsen. Kogel klasse. Laten we het doen.

Maak een nieuwe klasse genaamd Kogel, uitbreiding van de sprite klasse. De basiscode voor deze klasse moet zijn:

 pakket import flash.display.Sprite; public class Bullet breidt uit public function Bullet () 

Vervolgens plaatsen we de code om de bullet-afbeelding te tekenen Hoofd, in Kogel. Zoals we deden met de Torentje klasse maken we een functie genaamd trek in de Kogel klasse:

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

En we noemen deze functie van de Kogel constructor:

 public function Bullet () draw (); 

Nu voegen we wat eigenschappen toe aan de kogel. Voeg vier variabelen toe: snelheid, speed_x, speed_y en schade, voor de Kogel constructor:

 privé var snelheid: Number; privé var speed_x: Number; private var speed_y: Number; public var damage: int;

Waar zijn deze variabelen voor?

  • snelheid: Deze variabele slaat de snelheid van de kogel op.
  • speed_x en speed_y: Deze slaan de x- en y-componenten van de snelheid op, zodat de berekening van het breken van de snelheid in de componenten niet steeds opnieuw hoeft te worden uitgevoerd.
  • schade: Dit is de hoeveelheid schade die de kogel kan aanrichten aan een vijand. We houden deze variabele openbaar omdat we dit nodig hebben in onze gamelus in de Hoofd klasse.

We initialiseren deze variabelen in de constructor. Update uw Kogel constructor:

 publieke functie Bullet (hoek: Number) speed = 5; schade = 1; speed_x = Math.cos (hoek * Math.PI / 180) * snelheid; speed_y = Math.sin (angle * Math.PI / 180) * snelheid; trek(); 

Let op de hoek variabele die we ontvangen in de constructor. Dit is de richting (in graden) waarin de kogel zal bewegen. We breken gewoon de snelheid in de x- en y-componenten en cacheer ze voor toekomstig gebruik.

Het laatste wat overblijft in de Kogel klasse is om een ​​te hebben bijwerken functie die wordt aangeroepen vanuit de spellus om de opsomming bij te werken (te verplaatsen). Voeg de volgende functie toe aan het einde van de Kogel klasse:

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

Bingo! We zijn klaar met onze Kogel klasse.


Stap 3: De hoofdklasse bijwerken

We hebben veel bullet code verplaatst Hoofd klasse op zichzelf Kogel klasse, dus veel code blijft ongebruikt in Hoofd en er moet nog veel worden bijgewerkt.

Wis eerst de createBullet () en moveBullet () functies. Verwijder ook de kogel snelheid veranderlijk.

Ga vervolgens naar de schieten functie en update deze met de volgende code:

 privéfunctie shoot (e: MouseEvent): void voor elk (var torentje: torentje in torentjes) var new_bullet: Bullet = new Bullet (revolver.rotatie); 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); 

We gebruiken niet langer het createBullet functie om kogel te maken, gebruik liever de Kogel bouwer en passeer de toren omwenteling erop, wat de richting is van de beweging van de kogel en dus hoeven we hem niet op te slaan in de kogels omwenteling eigendom zoals we eerder deden. We voegen ook geen luisteraars aan de opsomming toe, omdat de opsommingsteken wordt bijgewerkt vanuit de volgende lus van de game.


Stap 4: De Bullet-referenties opslaan

Nu we de opsommingstekens van de gamelus moeten bijwerken, moeten we ergens een referentie van deze opsommen. De oplossing is hetzelfde als voor de torentjes: maak een nieuwe reeks genaamd bullets en duw de kogels erop zoals ze zijn gemaakt.

Verklaar eerst een array net onder de torentjes matrixverklaring:

 private var ghost_turret: Turret; private var torentjes: Array = []; private var bullets: Array = [];

Nu om deze array te vullen. We doen dit telkens wanneer we een nieuwe bullet creëren - dus in de schieten functie. Voeg het volgende toe vlak voor het toevoegen van de kogel aan het podium:

 var new_bullet: Bullet = new Bullet (revolver.rotatie); 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);

Stap 5: werk de bulletins bij

Net als hoe we de loop van de turrets bijwerken, zullen we ook de kogels bijwerken. Maar deze keer in plaats van een voor ... elk loop, we zullen een basis gebruiken voor lus. Hiervoor moeten we twee variabelen aan de bovenkant van de gamelus toevoegen, zodat we weten welke variabelen binnen de gamelus worden gebruikt en deze gratis kunnen instellen voor garbagecollection.

 var torentje: torentje; var bullet: Bullet;

Ga je gang en voeg de volgende code toe aan het einde van de game-lus:

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

Hier doorkruisen we alle frames op het podium elk beeld en noemen ze hun bijwerken functie waardoor ze bewegen. Merk op dat we de bullets array in omgekeerde volgorde. Waarom? We zullen dit vooruit zien.

Nu dat we een hebben torentje variabele die al buiten is gedeclareerd, hoeven we niet opnieuw te declareren binnen de voor ... elk loop van torentjes. Pas het aan:

 voor elk (torentje in torentjes) torentje.update (); 

Als laatste voegen we de grenscontrole toe; dit was eerder in de kogel ENTER_FRAME maar nu controleren we het in de game-loop:

 if (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (kogel); doorgaan met; 

We controleren of de kogel buiten de grens van het toneel is en als dat zo is, verwijderen we eerst zijn referentie van de bullets array met behulp van de verbinding functie en verwijder vervolgens de kogel uit het werkgebied en ga verder met de volgende iteratie. Dit is hoe je game-loop eruit zou moeten zien:

 privéfunctiegameLoop (e: Event): void var torentje: torentje; var bullet: Bullet; voor elk (torentje in torentjes) torentje.update ();  for (var i: int = bullets.length - 1; i> = 0; i--) bullet = bullets [i]; als (! opsommingsteken) doorgaan; bullet.update (); 

Als je nu het spel uitvoert, zou je dezelfde functionaliteit moeten hebben als in deel 1, met code die veel zuiverder en overzichtelijker is.


Stap 6: De vijand voorstellen

Nu voegen we een van de belangrijkste elementen van het spel toe: de vijand. Het eerste is om een ​​nieuwe klasse genaamd te maken Vijand uitbreiding van de sprite klasse:

 pakket import flash.display.Sprite; openbare klasse Enemy breidt Sprite uit openbare functie Enemy () 

Nu voegen we wat eigenschappen toe aan de klas. Voeg ze toe vóór je Vijand constructor:

 privé var speed_x: Number; private var speed_y: Number;

We initialiseren deze variabelen in de Vijand constructor:

 openbare functie Enemy () speed_x = -1.5; speed_y = 0; 

Vervolgens maken we de trek en bijwerken functies voor de Vijand klasse. Deze lijken erg op die uit Kogel. Voeg de volgende code toe:

 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; 

Stap 7: Timing van de game-evenementen

In ons spel moeten we veel evenementen hebben die plaatsvinden op bepaalde tijden of herhaaldelijk met bepaalde tussenpozen. Een dergelijke timing kan worden bereikt met behulp van een tijdteller. De teller is slechts een variabele die wordt opgehoogd naarmate de tijd verstrijkt in het spel. Het belangrijkste is hier wanneer en met hoeveel bedrag de teller moet worden verhoogd. Er zijn twee manieren waarop timing in het algemeen in games wordt gedaan: tijdgebaseerd en frame-gebaseerd.

Het verschil is dat de eenheid van stap in tijdgebaseerd spel gebaseerd is op realtime (dat wil zeggen, het aantal milliseconden gepasseerd), maar in een op frames gebaseerd spel is de eenheid van stap gebaseerd op frame-eenheden (dat wil zeggen het aantal frames dat is gepasseerd).

Voor onze game gaan we een frame-gebaseerde teller gebruiken. We hebben een teller die we met één verhogen in de spellus, die elk frame uitvoert, en dus in feite ons het aantal frames geven dat is verstreken sinds de game is gestart. Ga je gang en declareer een variabele na de andere variabele declaraties in de Hoofd klasse:

 private var ghost_turret: Turret; private var torentjes: Array = []; private var bullets: Array = []; private var global_time: Number = 0;

We verhogen deze variabele in de spellus bovenaan:

 global_time ++;

Nu gebaseerd op deze teller kunnen we dingen doen als vijanden maken, wat we vervolgens zullen doen.


Stap 8: Laten we wat vijanden maken

Wat we nu willen doen is vijanden om de twee seconden op het veld maken. Maar we hebben hier te maken met frames, weet je nog? Dus na hoeveel frames moeten we vijanden maken? Welnu, onze game draait op 30 FPS, waardoor de global_time tel 30 keer per seconde. Een eenvoudige berekening vertelt ons dat 3 seconden = 90 frames.

Voeg aan het einde van de spellus het volgende toe als blok:

 if (global_time% 90 == 0) 

Waar gaat die toestand over? We gebruiken de modulo (%) -operator, die de rest van een divisie geeft - dus global_time% 90 geeft ons de rest wanneer global_time is gedeeld door 90. We controleren of de rest is 0, omdat dit alleen het geval zal zijn wanneer global_time is een veelvoud van 90 - dat wil zeggen dat de toestand terugkeert waar wanneer global_time is gelijk aan 0, 90, 180 en zo verder ... Op deze manier bereiken we een trigger op elke 90 frames of 3 seconden.

Voordat we de vijand creëren, verklaar een andere array genaamd vijanden net onder de torentjes en bullets matrix. Dit wordt gebruikt om verwijzingen naar vijanden op het podium op te slaan.

 private var ghost_turret: Turret; private var torentjes: Array = []; private var bullets: Array = []; privé var vijanden: Array = []; private var global_time: Number = 0;

Verklaar ook een vijand variabele aan de bovenkant van de spellus:

 global_time ++; var torentje: torentje; var bullet: Bullet; var vijand: vijand;

Voeg ten slotte de volgende code toe in de als blok dat we eerder hebben gemaakt:

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

Hier creëren we een nieuwe vijand, positioneer het willekeurig aan de rechterkant van het podium, druk het in de vijanden array en voeg het toe aan het podium.


Stap 9: De vijanden updaten

Net zoals we de kogels in de loop van het spel updaten, werken we de vijanden bij. Plaats de volgende code onder het torentje voor ... elk lus:

 for (var j: int = vijemies.length - 1; j> = 0; j--) enemy = vijanden [j]; enemy.update (); als (vijand.x < 0)  enemies.splice(j, 1); enemy.parent.removeChild(enemy); continue;  

Net zoals we een grenscontrole voor kogels uitvoerden, controleren we ook op vijanden. Maar voor vijanden controleren we gewoon of ze van de linkerkant van het podium zijn gegaan, omdat ze alleen van rechts naar links bewegen. Je zou vijanden van rechts moeten zien komen als je het spel nu uitvoert.


Stap 10: Geef de vijanden wat gezondheid

Elke vijand heeft wat leven / gezondheid en dat geldt ook voor ons. We zullen ook de resterende gezondheid van de vijanden laten zien. Laten we enkele variabelen verklaren in de Vijand klasse voor het gezondheidsmateriaal:

 private var health_txt: TextField; privé var gezondheid: int; privé var speed_x: Number; private var speed_y: Number;

We initialiseren de Gezondheid variabele in de volgende constructor. Voeg het volgende toe aan de Vijand constructor:

 gezondheid = 2;

Nu initialiseren we de gezondheidstekstvariabele die in het midden van de vijand wordt weergegeven. We doen dit in de trek functie:

 health_txt = nieuw 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);

Alles wat we doen is een nieuw maken TextField, stel de kleur in, positioneer het en stel de tekst in op de huidige waarde van Gezondheid Als laatste voegen we een functie toe om de gezondheid van de vijand bij te werken:

 public function updateHealth (amount: int): int health + = amount; health_txt.text = health + ""; gezondheid teruggeven; 

De functie accepteert een geheel getal om aan de status toe te voegen, werkt de gezondheidstekst bij en retourneert de uiteindelijke status. We zullen deze functie uit onze spellus halen om de gezondheid van elke vijand bij te werken en te detecteren of hij nog leeft.


Stap 11: De vijanden schieten.

Laten we eerst onze wijzigen schieten functioneer een beetje. Vervang de bestaande schieten functie met het volgende:

 privéfunctie shoot (torentje: torentje, vijand: vijand): void var angle: Number = Math.atan2 (vijand.y - torentje.y, vijand.x - torentje.x) / Math.PI * 180; torentje.rotatie = hoek; 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); 

De schieten functie accepteert nu twee parameters. De eerste is een verwijzing naar een torentje dat de schietpartij zal doen; de tweede is een verwijzing naar een vijand waarnaar hij zal schieten.

De nieuwe code is hier vergelijkbaar met de code in de Torentje klasse bijwerken functie, maar in plaats van de positie van de muis gebruiken we nu de cordinaten van de vijand. Dus nu kun je alle code verwijderen uit de bijwerken functie van de Torentje klasse.

Hoe kunnen de torentjes op vijanden schieten? Nou, de logica is eenvoudig voor onze game. We laten alle torens schieten op de eerste vijand in de vijanden matrix. Wat? Laten we wat code zetten en dan proberen te begrijpen. Tel de volgende regels op aan het eind van de voor ... elk lus gebruikt om de torentjes bij te werken:

 voor elk (torentje in torentjes) torentje.update (); voor elke (vijand in vijanden) shoot (torentje, vijand); breken; 

Voor elk torentje werken we het nu bij, en dan herhalen we het vijanden array, schiet de eerste vijand in de array en breek uit de lus. Dus in wezen schiet elk torentje naar de vroegst gecreëerde vijand omdat het altijd aan het begin van de reeks staat. Probeer het spel uit te voeren en je moet zien dat torens de vijanden neerschieten.

Maar wacht, wat is die kogelstroom? Het lijkt erop dat ze te snel schieten. Laten we zien waarom.


Stap 12: Torentjes schieten te snel

Zoals we weten draait de gamelus elk frame, dus 30 keer per seconde in ons geval, dus de schietverklaring die we in de vorige stap hebben toegevoegd, wordt gebeld met de snelheid van onze gamelus en daarom zien we een stroom kogels stroomt. Het lijkt erop dat we ook een timingmechanisme nodig hebben in de torentjes. Schakel over naar de Torentje klasse en voeg de volgende code toe:

 private var local_time: Number = 0; private var reload_time: int;
  1. lokale tijd: Onze teller wordt gebeld lokale tijd in tegenstelling tot global_time in de Hoofd klasse. Dit is om twee redenen: ten eerste omdat deze variabele lokaal is voor de Torentje klasse; ten tweede omdat het niet altijd naar voren komt zoals het onze global_time variabele - deze wordt tijdens de game vaak opnieuw ingesteld.
  2. herlaad tijd: Dit is de tijd die het torentje nodig heeft om te herladen na het maken van een kogel. Eigenlijk is het het tijdsverschil tussen twee kogelspruiten door een torentje. Onthoud dat alle tijdseenheden in onze game qua frames zijn.

Verhoog de lokale tijd variabele in de bijwerken functie en initialiseer de herlaad tijd in de constructor:

 public function update (): void local_time ++; 
 public function Turret () reload_time = 30; trek(); 

Voeg vervolgens de volgende twee functies toe aan het einde van de Torentje klasse:

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

is gereed geeft alleen true als de huidige lokale tijd is groter dan de herlaad tijd, d.w.z. wanneer het torentje is herladen. En de reset functie reset gewoon de lokale tijd variabele, om te beginnen met opnieuw laden.

Nu terug in de Hoofd klasse, wijzig de shoot-code in de gamelus die we in de vorige stap hebben toegevoegd aan het volgende:

 voor elk (torentje in torentjes) torentje.update (); als (! turret.isReady ()) doorgaat; voor elke (vijand in vijanden) shoot (torentje, vijand); turret.reset (); breken; 

Dus als nu het torentje niet klaar is (is gereed() komt terug vals), gaan we verder met de volgende iteratie van de revolverlijn. Je zult zien dat de torentjes schieten met een interval van 30 frames of 1 seconde nu. Stoer!


Stap 13: beperk het revolverbereik

Nog steeds iets niet goed. De torentjes schieten op vijanden, ongeacht de afstand tussen hen. Wat hier ontbreekt, is de reeks van een torentje. Elk torentje zou een eigen bereik moeten hebben waarbinnen het een vijand kan neerschieten. Voeg een andere variabele toe aan de Torentje klas genoemd reeks en zet het op 120 in de constructor:

 private var reload_time: int; private var local_time: Number = 0; privé var-bereik: int;
 public function Turret () reload_time = 30; bereik = 120; trek(); 

Voeg ook een functie toe met de naam canShoot aan het einde van de les:

 openbare functie kanShoot (vijand: vijand): 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; 

Elk torentje kan een vijand alleen neerschieten als het aan bepaalde criteria voldoet - je kunt bijvoorbeeld het torentje alleen rode vijanden laten schieten met minder dan de helft van hun leven en niet meer dan 30px weg. Al deze logica om te bepalen of het torentje in staat is om een ​​vijand te verslaan of niet, gaat in de canShoot functie, die terugkeert waar of vals volgens de logica.

Onze logica is eenvoudig. Als de vijand binnen het bereik is, ga terug waar; anders wordt false geretourneerd. Dus wanneer de afstand tussen het torentje en de vijand (Math.sqrt (dx * dx + dy * dy)) is kleiner dan of gelijk aan reeks, het komt terug waar. Een beetje meer modificatie in de shoot-sectie van de game-loop:

 voor elk (torentje in torentjes) torentje.update (); als (! turret.isReady ()) doorgaat; voor elke (vijand in vijanden) if (turret.canShoot (vijand)) shoot (torentje, vijand); turret.reset (); breken; 

Alleen als de vijand zich binnen het bereik van het torentje bevindt, schiet het torentje dan.


Stap 14: Collision Detection

Een zeer belangrijk onderdeel van elke game is de botsingsdetectie. In onze game wordt botsing gecontroleerd tussen kogels en vijanden. We zullen de botsingsdetectiecode toevoegen in de voor ... elk lus die de kogels in de spellus bijwerkt.

De logica is eenvoudig. Voor elke kogel doorkruisen we de vijanden array en controleer of er een botsing tussen is. Als dat zo is, verwijderen we de kogel, werken we de vijandelijke gezondheid bij en breken we uit de kring om andere vijanden te controleren. Laten we wat code toevoegen:

 for (i = bullets.length - 1; i> = 0; i--) bullet = bullets [i]; // als de kogel niet is gedefinieerd, gaat u verder met de volgende iteratie als (! opsommingsteken) wordt voortgezet; bullet.update (); if (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (i, 1); bullet.parent.removeChild (kogel); doorgaan met;  for (var k: int = vijemies.length - 1; k> = 0; k--) enemy = vijanden [k]; if (bullet.hitTestObject (vijand)) bullets.splice (i, 1); bullet.parent.removeChild (kogel); if (enemy.updateHealth (-1) == 0) vijanden.splice (k, 1); enemy.parent.removeChild (vijand);  pauze; 

We gebruiken ActionScript's hitTestObject functie om te controleren op een botsing tussen de kogel en de vijand. Als de collie optreedt, wordt de kogel op dezelfde manier verwijderd als wanneer deze de etappe verlaat. De gezondheid van de vijand wordt dan bijgewerkt met behulp van de updateHealth methode, waarnaar kogel's schade eigendom is doorgegeven. Als het updateHealth functie retourneert een geheel getal kleiner dan of gelijk aan 0, dit betekent dat de vijand dood is en dus verwijderen we het op dezelfde manier als de kogel.

En onze botsingsdetectie is voltooid!


Stap 15: Waarom de "For" -lussen omkeren?

Vergeet niet dat we de vijanden en kogels in omgekeerde volgorde doorkruisen in onze gamelus. Laten we begrijpen waarom. Laten we veronderstellen dat we een oplopende lijn gebruikten voor lus. We zijn op index i = 3 en we verwijderen een kogel uit de array. Bij verwijdering van het artikel op positie 3, de ruimte wordt gevuld door het item en vervolgens op positie 4. Dus nu het item eerder op positie 4 is om 3. Na de iteratie ik verhogingen door 1 en wordt 4 en dus item op positie 4 is nagekeken.

Oeps, zie je wat er net is gebeurd? We hebben het item net op positie gemist 3 die terugging als resultaat van de splitsing. En dus gebruiken we een tegengestelde voor lus die dit probleem oplost. Je kunt zien waarom.


Stap 16: Weergave van het bereik van de toren

Laten we wat extra dingen toevoegen om het spel er goed uit te laten zien. We voegen functionaliteit toe om het bereik van een revolver weer te geven wanneer de muis erop zweeft. Schakel over naar de Torentje class en voeg wat variabelen toe:

 privé var-bereik: int; private var reload_time: int; private var local_time: Number = 0; privaat var-lichaam: Sprite; privé var bereik_circle: Sprite;

Volgende update het trek functioneren als volgt:

 private function draw (): void range_circle = new Sprite (); g = range_circle.graphics; g.beginFill (0x00D700); g.drawCircle (0, 0, bereik); g.endFill (); bereik_cirkel.alpha = 0,2; range_circle.visible = false; addChild (range_circle); body = nieuwe 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 (body); 

We breken de grafische weergave van het torentje in twee delen: het lichaam en de reeksafbeelding. We doen dit om een ​​bestelling te plaatsen voor de verschillende delen van het torentje. Hier hebben we de range_circle om achter het lichaam van de toren te zijn, en dus voegen we het eerst aan het podium toe. Ten slotte voegen we twee muisluisteraars toe om de bereikafbeelding in te schakelen:

 private function onMouseOver (e: MouseEvent): void range_circle.visible = true;  private function onMouseOut (e: MouseEvent): void range_circle.visible = false; 

Bevestig nu de luisteraars aan de respectievelijke gebeurtenissen aan het einde van de constructor:

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

Als je het spel uitvoert en een turret probeert te gebruiken, zie je een flikkering wanneer je de plaatsaanduidingen gebruikt. Waarom is dat?


Zie de flikkering?

Stap 17: De flikker verwijderen

Denk eraan dat we de mouseEnabled eigendom van de geestentoren aan vals? We deden dat omdat het ghost torentje muisgebeurtenissen ving door tussen de muis en de tijdelijke aanduiding te komen. Dezelfde situatie is weer aangebroken, aangezien het torentje zelf nu twee kinderen heeft - het lichaam en de range sprite - die de muisgebeurtenissen vastleggen tussenin.

De oplossing is hetzelfde. We kunnen hun persoon instellen mouseEnabled eigenschappen voor vals. Maar een betere oplossing is om de spookrevolver's te plaatsen mouseChildren eigendom aan vals. Wat dit doet is het beperken van alle kinderen van ghost torentje van het ontvangen van muisgebeurtenissen. Nette, huh? Ga je gang en zet het op vals in de Hoofd constructor:

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

Probleem opgelost.

Stap 18: What Next?

We kunnen deze demo uitbreiden met veel geavanceerdere functies en er een speelbare game van maken. Sommige daarvan kunnen zijn:

  1. Betere AI-logica voor het selecteren en fotograferen van vijanden.
  2. Verschillende soorten torentjes, kogels en vijanden in het spel.
  3. Complexe vijandige paden in plaats van rechte lijnen.

Laten we eens kijken wat u kunt bedenken uit deze eenvoudige demo. Ik ben blij om te horen over jullie torenverdedigingsspellen en jullie opmerkingen of suggesties voor de serie.