Gör ett Tower Defense Game i AS3 Fiender och Basic AI

Hej Flash Developers, välkommen till den andra delen av min Tower Defense Game tutorial. I den första delen utvecklade vi den grundläggande mekanismen för att skapa torn och göra dem skjutna mot musklickets punkt. Men det är inte vad torn är för! I denna del kommer vi att förlänga spelet för att inkludera fiender, grundläggande artificiell intelligens (AI) i torn och några fler spelelement. Är du redo?


Slutresultatförhandsvisning

Detta är spelet vi ska skapa i denna handledning:

Klicka på orange cirklarna för att placera torn. De röda cirklarna är fiender, och siffran på var och en representerar sina träffpunkter.


Steg 1: Recap

I den tidigare handledningen utvecklade vi ett spel som hade platshållare för tornen. Vi kunde distribuera torn genom att klicka på dessa platshållare, och tornen riktade mot muspekaren och skottkulor mot den punkt där användaren klickade.

Vi slutade med a Huvudsaklig klass som hade spelet loop och spel logik. Bortsett från det hade vi Turret klass som inte hade något annat än uppdatering funktion som gjorde tornet roterande.


Steg 2: En separat kulklass

Vi skapade tidigare kulorna i Huvudsaklig klass och fäst en ENTER_FRAME lyssnare att flytta den. Kulan hade inte tillräckligt många egenskaper tidigare för att överväga att göra en separat klass. Men i ett sådant spel kan kulor ha många sorter som hastighet, skada osv. Så det är en bra idé att dra ut kollkoden och inkapslera den i en separat Kula klass. Vi gör det.

Skapa en ny klass som heter Kula, förlängning av Sprite klass. Grundkoden för den här klassen ska vara:

 paket import flash.display.Sprite; offentlig klass Bullet utökar Sprite public function Bullet () 

Därefter sätter vi koden för att rita kolumnbilden, taget från Huvudsaklig, i Kula. Som vi gjorde med Turret klass skapar vi en funktion som heter dra i Kula klass:

 privat funktionstegning (): void var g: Graphics = this.graphics; g.beginFill (0xEEEEEE); g.drawCircle (0, 0, 5); g.endFill (); 

Och vi kallar den här funktionen från Kula konstruktör:

 allmän funktion Bullet () draw (); 

Nu lägger vi till några egenskaper i kulan. Lägg till fyra variabler: fart, speed_x, speed_y och skada, Innan Kula konstruktör:

 privat varhastighet: nummer; privat var speed_x: Number; privat var speed_y: Number; allmänhet var skada: int;

Vad är dessa variabler för?

  • fart: Denna variabel lagrar kulsens hastighet.
  • speed_x och speed_y: Dessa lagrar hastighetens x respektive y-komponenter, så att beräkningen av att hastigheten bryts in i dess komponenter behöver inte göras igen och igen.
  • skada: Det här är mängden skada som kulan kan göra mot en fiende. Vi håller denna variabel offentlig eftersom vi kommer att kräva detta i vår spelslinga i Huvudsaklig klass.

Vi initierar dessa variabler i konstruktören. Uppdatera din Kula konstruktör:

 allmän funktion Bullet (vinkel: Number) speed = 5; skada = 1; speed_x = Math.cos (vinkel * Math.PI / 180) * hastighet; speed_y = Math.sin (vinkel * Math.PI / 180) * hastighet; dra(); 

Lägg märke till vinkel variabel vi får i konstruktören. Detta är riktningen (i grader) där kula kommer att röra sig. Vi bryter bara fart in i dess x och y komponenter och cache dem för framtida användning.

Det sista som finns kvar i Kula klassen är att ha en uppdatering funktion som kommer att ringas från spelslingan för att uppdatera (flytta) kullen. Lägg till följande funktion i slutet av Kula klass:

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

Bingo! Vi är färdiga med vårt Kula klass.


Steg 3: Uppdatering av huvudklassen

Vi flyttade mycket kolumnkod från Huvudsaklig klass till sin egen Kula klass, så mycket kod kvarstår oanvänd i Huvudsaklig och mycket behöver uppdateras.

Först, radera createBullet () och moveBullet () funktioner. Ta även bort bullet_speed variabel.

Gå sedan till skjuta funktionen och uppdatera den med följande kod:

 privat funktionskott (e: MouseEvent): tomrum för varje (var turret: Turret i torn) 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); 

Vi använder inte längre createBullet funktion för att skapa kula använder snarare Kula konstruktör och passera tornet rotation till det som är riktningen för kullens rörelse och så behöver vi inte lagra den i kula rotation egendom som vi gjorde tidigare. Vi bifogar inte heller någon lyssnare till kullen eftersom kullen kommer att uppdateras från inuti spelet slingan nästa.


Steg 4: Spara Bullet References

Nu när vi behöver uppdatera kulorna från spelslingan behöver vi en hänvisning av dem att lagras någonstans. Lösningen är densamma som för tornen: skapa en ny Array som heter kulor och skjut kulorna på det som de är skapade.

Förklara först en matris strax under torn arraydeklaration:

 privat var ghost_turret: Turret; privata vartorn: Array = []; privata var kulor: Array = [];

Nu för att fylla i denna array. Vi gör det när vi skapar en ny kula - så i skjuta fungera. Lägg till följande innan du lägger till kullen på scenen:

 var new_bullet: Bullet = ny 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);

Steg 5: Uppdatera kulorna

Precis som hur vi uppdaterar tornen spelet loop, vi kommer att uppdatera kulorna också. Men den här gången, istället för att använda a för varje loop, vi använder en grundläggande för slinga. Innan detta måste vi lägga till två variabler överst i spelslingan så att vi vet vilka variabler som används inom spelslingan och kan sätta dem fritt för sopkollektion.

 var turret: Turret; var bullet: Bullet;

Fortsätt och lägg till följande kod i slutet av spelet loop:

 för (var i: int = kulor. längd - 1; i> = 0; i--) bullet = kulor [i]; om (! bullet) fortsätt; bullet.update (); 

Här kryssar vi över alla kulor på scenen varje ram och kallar deras uppdatering funktion som får dem att röra sig. Notera här att vi iterera kulor array i omvänd. Varför? Vi ser detta framåt.

Nu när vi har en torn variabel deklarerad utanför redan behöver vi inte deklarera det igen inuti för varje loop av torn. Ändra det till:

 för varje (turret i torn) turret.update (); 

Slutligen lägger vi till gränskontrolltillståndet; Detta var tidigare i kulan ENTER_FRAME men nu kontrollerar vi det i spelslingan:

 om (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (jag, 1); bullet.parent.removeChild (bullet); Fortsätta; 

Vi kontrollerar om kula är borta från scenens gräns, och i så fall tar vi bort sin referens från kulor array med hjälp av splitsa funktionen och ta bort kula från scenen och fortsätt med nästa iteration. Så här ser spelet på dig:

 privat funktion gameLoop (e: Event): void var turret: Turret; var bullet: Bullet; för varje (turret i torn) turret.update ();  för (var i: int = kulor. längd - 1; i> = 0; i--) bullet = kulor [i]; om (! bullet) fortsätt; bullet.update (); 

Om du nu kör spelet, ska du ha samma funktion som i Del 1, med kod som är mycket renare och organiserad.


Steg 6: Presentation av fienden

Nu lägger vi till en av de viktigaste elementen i spelet: fienden. Första är att skapa en ny klass som heter Fiende förlängning av Sprite klass:

 paket import flash.display.Sprite; offentlig klass Enemy utökar Sprite public function Enemy () 

Nu lägger vi till några egenskaper i klassen. Lägg till dem före din Fiende konstruktör:

 privat var speed_x: Number; privat var speed_y: Number;

Vi initierar dessa variabler i Fiende konstruktör:

 Officiell funktion Enemy () speed_x = -1.5; speed_y = 0; 

Nästa skapar vi dra och uppdatering funktioner för Fiende klass. Dessa liknar dem mycket från Kula. Lägg till följande kod:

 privat funktionstegning (): 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; 

Steg 7: Timing av spelhändelserna

I vårt spel behöver vi ha många händelser som äger rum vid vissa tillfällen eller upprepade gånger med vissa intervall. Sådan tidpunkt kan uppnås med användning av en tidräknare. Räknaren är bara en variabel som ökar när tiden går i spelet. Det viktiga här är när och av hur mycket belopp som ska öka räknaren. Det finns två sätt på vilka timing i allmänhet görs i spel: tidsbaserad och rambaserad.

Skillnaden är att enheten för steg i tidsbaserat spel är baserat på realtid (dvs antal millisekunder passerade), men i ett rambaserat spel är stegetheten baserat på ramenheter (dvs antalet ramar som passerat).

För vårt spel ska vi använda en rambaserad räknare. Vi har en räknare som vi ökar med en i spelet slingan, som kör varje ram, och så kommer det i grunden att ge oss antalet ramar som har gått sedan spelet startade. Gå vidare och förklara en variabel efter de andra variabla deklarationerna i Huvudsaklig klass:

 privat var ghost_turret: Turret; privata vartorn: Array = []; privata var kulor: Array = []; privat var global_time: Number = 0;

Vi ökar denna variabel i spelet slingan överst:

 global_time ++;

Nu bygger vi på denna räknare vi kan göra saker som att skapa fiender, vilket vi gör nästa.


Steg 8: Låt oss skapa några fiender

Vad vi vill göra nu är att skapa fiender på fältet efter vartannat sekund. Men vi har att göra med ramar här, kom ihåg? Så efter hur många ramar ska vi skapa fiender? Tja, vårt spel körs vid 30 FPS, vilket ökar global_time motverka 30 gånger varje sekund. En enkel beräkning berättar att 3 sekunder = 90 ramar.

I slutet av spelet slinga lägg till följande om blockera:

 om (global_time% 90 == 0) 

Vad handlar det om? Vi använder modulo (%) operatören, vilket ger resten av en division - så global_time% 90 ger oss resten när global_time är uppdelad med 90. Vi kontrollerar om resten är 0, eftersom detta bara kommer att vara fallet när global_time är en multipel av 90 - det vill säga villkoret återkommer Sann när global_time är lika med 0, 90, 180 och så vidare ... På så sätt uppnår vi en utlösare vid var 90: e eller 3 sekunder.

Innan vi skapar fienden, förklara en annan grupp som heter fiender strax under torn och kulor array. Detta kommer att användas för att lagra referenser till fiender på scenen.

 privat var ghost_turret: Turret; privata vartorn: Array = []; privata var kulor: Array = []; privata var fiender: Array = []; privat var global_time: Number = 0;

Angiv också en fiende variabel högst upp i spelslingan:

 global_time ++; var turret: Turret; var bullet: Bullet; var fiende: fiende

Slutligen lägg till följande kod inuti om blockera vi skapade tidigare:

 fiende = ny fiend (); fiende.x = 410; enemy.y = 30 + Math.random () * 370; enemies.push (fiende); addChild (fiende);

Här skapar vi en ny fiende, placera den slumpmässigt till höger om scenen, tryck den i fiender array och lägg till den till scenen.


Steg 9: Uppdatering av fienden

Precis som vi uppdaterar kulorna i spelsling uppdaterar vi fienderna. Sätt följande kod under tornet för varje slinga:

 för (var j: int = enemies.length - 1; j> = 0; j--) fiende = fiender [j]; enemy.update (); om (fiende.x < 0)  enemies.splice(j, 1); enemy.parent.removeChild(enemy); continue;  

Precis som vi gjorde en gränskontroll för kulor, kontrollerar vi också för fiender. Men för fiender kontrollerar vi bara om de gick ut från scenens vänstra sida, eftersom de bara rör sig åt höger mot vänster. Du borde se fiender från höger om du kör spelet nu.


Steg 10: Ge fienderna lite hälsa

Varje fiende har lite liv / hälsa och det kommer också vårt. Vi kommer också att visa den återstående hälsan på fienderna. Låt oss förklara några variabler i Fiende klass för hälsa saker:

 privat var health_txt: TextField; privat var hälsa: int; privat var speed_x: Number; privat var speed_y: Number;

Vi initierar hälsa variabel i konstruktorn nästa. Lägg till följande i Fiende konstruktör:

 hälsa = 2;

Nu initierar vi hälsotextvariabeln för att visa i centrum av fienden. Vi gör det i dra fungera:

 health_txt = nytt TextField (); health_txt.height = 20; health_txt.width = 15; health_txt.textColor = 0xffffff; health_txt.x = -5; health_txt.y = -8; health_txt.text = hälsa + ""; addChild (health_txt);

Allt vi gör är att skapa en ny Textfält, Ställ in sin färg, placera den och sätt dess text till det aktuella värdet av hälsa Slutligen lägger vi till en funktion för att uppdatera fiendens hälsa:

 public function updateHealth (mängd: int): int hälsa + = mängd; health_txt.text = hälsa + ""; återvända hälsa; 

Funktionen accepterar ett heltal för att lägga till hälsan, uppdaterar hälsoteksten och returnerar den slutliga hälsan. Vi ringer den här funktionen från vår spelslinga för att uppdatera varje fiendens hälsa och upptäcka om den fortfarande lever.


Steg 11: Skydda fienden.

Först kan vi ändra vår skjuta fungera lite. Ersätt det befintliga skjuta funktion med följande:

 privata funktionsskott (turret: Turret, fiende: Enemy): void varvinkel: Number = Math.atan2 (enemy.y - turret.y, enemy.x - turret.x) / Math.PI * 180; turret.rotation = vinkel; var new_bullet: Bullet = ny Bullet (vinkel); 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 skjuta funktionen accepterar nu två parametrar. Den första är en hänvisning till ett torn som kommer att göra skjutningen; den andra är en hänvisning till en fiende mot vilken den kommer att skjuta.

Den nya koden här liknar den som finns i Turret klassens uppdatering funktion, men istället för musens position använder vi nu fiendens cordinates. Så nu kan du ta bort all kod från uppdatering funktion av Turret klass.

Nu hur man gör tornen skjuta på fiender? Jo logiken är enkel för vårt spel. Vi gör alla tornen skjuta den första fienden i fiender array. Vad? Låt oss ange en kod och försöka förstå. Lägg till följande rader i slutet av för varje slinga som används för att uppdatera tornen:

 för varje (turret i torn) turret.update (); för varje (fiende i fiender) skott (torn, fiende); ha sönder; 

För varje torn uppdaterar vi nu den, sedan iterera fiender array, skjuta den första fienden i array och bryta från loopen. Så väsentligen skottar varje torn den tidigast skapade fienden som den alltid är i början av matrisen. Försök att köra spelet och du bör se torn som skjuter fienderna.

Men vänta, vad stämmer den här kulan? Ser ut att de skjuter för fort. Låt oss se varför.


Steg 12: Tornen skjuter för fort

Som vi vet går spelslingan varje ram, det vill säga 30 gånger i sekundet i vårt fall, så den skytteförklaring vi lade till i föregående steg kallas i takt med vår spelslinga och därmed ser vi en ström av kulor som flyter. Det verkar som om vi behöver en tidsmekanism inuti tornen också. Byt till Turret klass och lägg till följande kod:

 privat var local_time: Number = 0; privat var reload_time: int;
  1. lokal tid: Vår räknare heter lokal tid i motsats till global_time i Huvudsaklig klass. Detta är av två anledningar: För det första, eftersom denna variabel är lokal för Turret klass; För det andra, för det går inte alltid fram som vår global_time variabel - det kommer att återställas många gånger under spelet.
  2. omladdningstid: Det här är den tid som tornet behöver för att ladda om efter att ha tagit en kula. I grund och botten är det tidsskillnaden mellan två kula skott av en torn. Kom ihåg att alla tidsenheter i vårt spel gäller ramar.

Öka lokal tid variabel i uppdatering funktion och initiera omladdningstid i konstruktören:

 public function update (): void local_time ++; 
 allmän funktion Turret () reload_time = 30; dra(); 

Lägg sedan till följande två funktioner i slutet av Turret klass:

 allmän funktion isReady (): Boolean return local_time> reload_time;  återställning av allmän funktion (): void local_time = 0; 

är redo returnerar sant endast när det aktuella lokal tid är större än omladdningstid, d.v.s. när tornet har laddats om. Och den återställa funktionen återställer helt enkelt lokal tid variabel, för att starta omladdning igen.

Nu tillbaka i Huvudsaklig klass, ändra skjutkoden i spelet slingan vi lade till i föregående steg till följande:

 för varje (turret i torn) turret.update (); om (! turret.isReady ()) fortsätter; för varje (fiende i fiender) skott (torn, fiende); turret.reset (); ha sönder; 

Så om nu är tornet inte klart (är redo() avkastning falsk) fortsätter vi med nästa iteration av turret slingan. Du kommer att se att tornen eldar med ett intervall på 30 bilder eller 1 sekund nu. Häftigt!


Steg 13: Begränsa Turret Range

Fortfarande något inte rätt. Tornen skjuter på fiender oavsett avståndet mellan dem. Vad som saknas här är räckvidd av ett torn. Varje torn bör ha sitt eget sortiment inom vilket det kan skjuta en fiende. Lägg till en annan variabel till Turret klass kallas räckvidd och ställ den till 120 inuti konstruktören:

 privat var reload_time: int; privat var local_time: Number = 0; privat varavstånd: int;
 allmän funktion Turret () reload_time = 30; intervall = 120; dra(); 

Lägg också till en funktion som heter canShoot i slutet av klassen:

 Officiell funktion canShoot (fiende: Enemy): Boolean var dx: Number = enemy.x - x; var dy: Nummer = fiende.y - y; om (Math.sqrt (dx * dx + dy * dy) <= range) return true; else return false; 

Varje torn kan bara skjuta en fiende när den uppfyller vissa kriterier. Du kan till exempel låta tornet bara skjuta röda fiender med mindre än hälften av livet och inte mer än 30 px borta. All sådan logik för att avgöra om tornet kan skjuta en fiende eller inte kommer att gå in i canShoot funktion som returnerar Sann eller falsk enligt logiken.

Vår logik är enkel. Om fienden är inom räckvidden Sann; returnera annars falskt. Så när avståndet mellan tornet och fienden (Math.sqrt (dx * dx + dy * dy)) är mindre än eller lika med räckvidd, det återvänder Sann. Lite mer modifiering i spelsegmentet i spelet loop:

 för varje (turret i torn) turret.update (); om (! turret.isReady ()) fortsätter; för varje (fiende i fiender) if (turret.canShoot (fiende)) shoot (turret, fiende); turret.reset (); ha sönder; 

Nu är det bara om fienden är inom tornet, kommer tornet att skjuta.


Steg 14: Kollisionsdetektion

En mycket viktig del av varje spel är kollisionsdetektering. I vårt spel kollisionskontroll görs mellan kulor och fiender. Vi lägger till kollisionsdetekteringskoden inuti för varje slinga som uppdaterar kulorna i spelslingan.

Logiken är enkel. För varje kula korsar vi fiender array och kontrollera om det finns en kollision mellan dem. Om så är fallet tar vi bort kula, uppdaterar fiendens hälsa och bryter ut ur slingan för att kontrollera andra fiender. Låt oss lägga till en kod:

 för (i = kullar. längd - 1; i> = 0; i--) kula = kula [i]; // om kulan inte är definierad fortsätter du med nästa iteration om (! bullet) fortsätter; bullet.update (); om (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) bullets.splice (jag, 1); bullet.parent.removeChild (bullet); Fortsätta;  för (var k: int = enemies.length - 1; k> = 0; k--) fiende = fiender [k]; om (bullet.hitTestObject (fiende)) bullets.splice (jag, 1); bullet.parent.removeChild (bullet); om (fiende.updateHealth (-1) == 0) enemies.splice (k, 1); enemy.parent.removeChild (fiende);  ha sönder; 

Vi använder ActionScript hitTestObject funktion för att kontrollera kollision mellan kula och fiende. Om kollisionen uppträder, avlägsnas kulan på samma sätt som när den lämnar scenen. Fiendens hälsa uppdateras sedan med hjälp av updateHealth metod, till vilken kula's skada egendom är godkänd. Om updateHealth funktionen returnerar ett heltal mindre än eller lika med 0, det betyder att fienden är död och så tar vi bort det på samma sätt som kulan.

Och vår kollisionsdetektering är klar!


Steg 15: Varför omvandla "för" looparna?

Kom ihåg att vi korsar fienderna och kulorna i omvänd ordning i vår spelslinga. Låt oss förstå varför. Låt oss anta att vi använde en stigande för slinga. Vi är på index i = 3 och vi tar bort en kula från matrisen. Vid borttagning av föremålet vid position 3, dess utrymme fylls av objektet då i position 4. Så nu föremålet tidigare i position 4 är på 3. Efter iterationen jag steg med 1 och blir 4 och så objekt på plats 4 är kontrollerad.

Oj, ser du vad som hände just nu? Vi missade bara objektet nu på plats 3 som skiftes tillbaka som ett resultat av splicing. Och så använder vi en omvänd för slinga som tar bort detta problem. Du kan se varför.


Steg 16: Visar tornets räckvidd

Låt oss lägga till några extra saker för att få spelet att se bra ut. Vi lägger till funktionalitet för att visa ett tornets intervall när musen svävar på den. Byt till Turret klass och lägg till några variabler till den:

 privat varavstånd: int; privat var reload_time: int; privat var local_time: Number = 0; privat var kropp: Sprite; privat var range_circle: Sprite;

Nästa uppdatering av dra funktion till följande:

 privatfunktionsdrag (): void range_circle = new Sprite (); g = range_circle.graphics; g.beginFill (0x00D700); g.drawCircle (0, 0, intervall); 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 (kropp); 

Vi bryter tornets grafik i två delar: kroppen och intervallet grafik. Vi gör detta för att ge beställning till de olika delarna av tornet. Här behöver vi range_circle att ligga bakom tornets kropp, och så lägger vi först till scenen. Slutligen lägger vi till två muslyttare för att växla intervallet grafik:

 privat funktion onMouseOver (e: MouseEvent): void range_circle.visible = true;  privat funktion onMouseOut (e: MouseEvent): void range_circle.visible = false; 

Fäst nu lyssnarna på respektive händelser i slutet av konstruktören:

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

Om du kör spelet och försöker distribuera ett torn, kommer du att se en flimmer när du svävar på platshållarna. Varför är det så?


Se flimmer?

Steg 17: Ta bort flimmer

Kom ihåg att vi ställer in mouseEnabled ghosttornets egenskap till falsk? Vi gjorde det för att spöketornet fångade mushändelser genom att komma in mellan musen och platshållaren. Samma situation har kommit igen eftersom tornet själv har två barn nu - dess kropp och spridningsintervallet - som fångar mushändelserna emellan.

Lösningen är densamma. Vi kan ställa in deras individer mouseEnabled egenskaper till falsk. Men en bättre lösning är att ställa in spök tornet mousechildren egendom till falsk. Vad det här gör är att begränsa alla spökdräktens barn från att ta emot mushändelser. Snyggt va? Gå vidare och sätt på det falsk i Huvudsaklig konstruktör:

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

Problemet löst.

Steg 18: Vad Nästa?

Vi kan förlänga denna demo för att inkludera mycket mer avancerade funktioner och göra det till ett spelbart spel. Vissa av dessa kan vara:

  1. Bättre AI-logik för att välja och skjuta fiender.
  2. Olika typer av torn, kulor och fiender i spelet.
  3. Komplexa fiendens vägar istället för raka linjer.

Låt oss se vad du kan komma med från denna grundläggande demo. Jag kommer vara glad att höra om dig tornförsvarsspel, och dina kommentarer eller förslag till serien.