Maak een Space Defender Game - Game Logic

In deze tutorialserie leren we hoe je een space shooter-game kunt maken, net als het klassieke spel Space Defender. Lees verder!


Series Overzicht

In deze versie van Space Defender moet de speler zijn ruimte verdedigen door vijanden neer te schieten. Elke keer dat de speler met succes een vijand vernietigt, zullen ze punten verdienen en wanneer de speler 20 of 40 punten heeft bereikt, zal hun wapen een upgrade krijgen. Om dingen te mixen, verstuurt dit spel bonuspakketten die 5 punten waard zijn. Bekijk de korte video hierboven om het spel in actie te zien.


Waar we zijn gebleven ...

In deel 1 van deze serie hebben we geleerd hoe we onze app kunnen instellen, hoe we aangepaste lettertypen kunnen gebruiken, hoe we een storyboard kunnen gebruiken en hoe we ons hoofdmenu kunnen instellen. In deel 2 van deze serie zullen we leren hoe we de gameplay van onze app kunnen creëren. Dus laten we beginnen!

Onze eerste stap is het maken van een nieuw bestand met de naam game.lua. Zodra het is gemaakt, opent u het bestand in uw favoriete editor.


1. Bibliotheken toevoegen

Aangezien we een nieuwe scène beginnen, moeten we een aantal bibliotheken hebben. We zullen de physics-engine gebruiken die is ingebouwd in Corona SDK voor botsingsdetectie.

 lokaal storyboard = require ("storyboard") local scene = storyboard.newScene () local physics = require ("physics")

2. Fysica configureren

Nadat we onze bibliotheken hebben ingesteld, zullen we de physics-engine configureren. De onderstaande instellingen stellen de zwaartekracht in op 0 (net als in de ruimte) en stellen de iteraties in op 16. De setPositionIterations betekent dat de motor 16 posities per frame zal doorlopen. Alles dat hoger is dan 16 kan de spelprestaties negatief beïnvloeden.

 physics.start () physics.setGravity (0, 0) physics.setPositionIterations (16)

3. Een willekeurige generator gebruiken

Hoewel deze stap niet nodig is voor deze zelfstudie, is het een goede gewoonte om de generator voor willekeurige getallen te "seeden". Ik gebruik graag de huidige tijd om de generator te zaaien.

 math.randomseed (os.time ())

4. Spelvariabelen instellen

Nu zullen we enkele variabelen definiëren voor onze game. Elke variabele heeft een opmerking ernaast waarin het doel van de variabele wordt uitgelegd.

 lokaal schermW, schermH, halfW, halfY = display.contentWidth, display.contentHeight, display.contentWidth * 0.5, display.contentHeight * 0.5 local gameover_returntomenu - forward declard our game over button - Spelinstellingen instellen motionx = 0; - Variabele gebruikt om het karakter langs de y-assnelheid te verplaatsen = 10; - Regelt de schip snelheid playerScore = 0; - Stelt de speler score playerLives = 20 in; - Stelt het aantal levens in voor de speler slowEnemySpeed ​​= 2375; - Stelt in hoe snel de witte schepen over het scherm bewegen slowEnemySpawn = 2400; - Stelt witschip spawn rate fastEnemySpeed ​​= 1875 in; - Bepaalt hoe snel de groene schepen snel over het scherm bewegenEnemySpawn = 1800; - Stelt groen schip spawn rate in bulletSpeed ​​= 325; - Bepaalt hoe snel de kogel door het scherm gaat bulletSpawn = 250; - Stelt spawn rate in

5. Maak de hoofdscènescène

Nadat we de variabelen hebben gemaakt, gaan we de scène in de functie instellen scène: createScene. Als je je herinnert uit Deel 1, wordt deze functie gebruikt om visuele elementen en spellogica te maken. In een latere functie zullen we een beroep doen op deze functies om het spel uit te voeren.

In de volgende code maken we de scène: createScene functie en het toevoegen van de achtergrond en de boven- / onderwanden. Beide muren zijn ingesteld als statische fysica-objecten om te voorkomen dat de speler van het scherm verdwijnt.

 function scene: createScene (event) local group = self.view - Stel visuele elementen en muren lokaal in bg = display.newImageRect ("images / BKG.png", 480, 320) bg.x = halfW bg.y = halfY groep: insert (bg) local topwall = display.newRect (0,0, scherm W, 20) topwall.y = -5 topwand: setFillColor (0,0,0) topwall.alpha = 0.01 physics.addBody (topwall, "static ") groep: insert (topwall) local bottomwall = display.newRect (0,0, screenW, 20) bottomwall.y = 325 bottomwall: setFillColor (0,0,0) bottomwall.alpha = 0.01 physics.addBody (bottomwall," statische ") groep: invoegen (bodemwand) einde

6. HUD-elementen toevoegen

Binnen hetzelfde scène: createScene functie, maar na de bottomwall weergaveobject, we gaan nog vier weergaveobjecten toevoegen. Hier is een uitleg van het doel van elk object.

  • btn_up, btn_down: Deze weergaveobjecten fungeren als knoppen aan de linkerkant van het scherm en elk object verplaatst het schip respectievelijk omhoog of omlaag. Ze kunnen echter pas worden gebruikt als we de verplaatsingsfunctie hebben ingesteld.
  • enemyHitBar: Dit weergaveobject is ingesteld als een sensor en reageert alleen op fysieke botsingen. Wanneer het reageert op botsingen, zal het het vijandelijke object verwijderen en er een van het leven van de speler aftrekken.
 local btn_up = display.newRect (0,0,75,160) btn_up: setReferencePoint (display.TopLeftReferencePoint) btn_up.x, btn_up.y = 0,0; btn_up.alpha = 0.01 groep: invoegen (btn_up) local btn_down = display.newRect (0,0,75,160) btn_down: setReferencePoint (display.BottomLeftReferencePoint) btn_down.x, btn_down.y = 0, screenH; btn_down.alpha = 0.01 group: insert (btn_down) local enemyHitBar = display.newRect (-20,0,20,320) enemyHitBar: setFillColor (0,0,0) enemyHitBar.name = "enemyHitBar" physics.addBody (enemyHitBar, isSensor = true) groep: insert (enemyHitBar)

Direct na het weergaveobject enemyHitBar gaan we enkele GUI-elementen toevoegen om de speler score en spelerlevens weer te geven. We zullen ook tekst weergeven op het scherm met de tekst "Omhoog" en "Omlaag verplaatsen" om de speler op de hoogte te brengen van waar ze moeten worden aangeraakt om het schip omhoog of omlaag te brengen.

 local gui_score = display.newText ("Score:" ... playerScore, 0,0, "Kemco Pixel", 16) gui_score: setReferencePoint (display.TopRightReferencePoint) gui_score.x = screenW group: insert (gui_score) local gui_lives = display.newText ("Lives:" ... playerLives, 0,0, "Kemco Pixel", 16) gui_lives: setReferencePoint (display.BottomRightReferencePoint) gui_lives.x = schermW gui_lives.y = screenH group: insert (gui_lives) local gui_moveup = display.newText ( "Move Up", 0,0,50,100, "Kemco Pixel", 16) groep: insert (gui_moveup) local gui_movedown = display.newText ("Move Down", 0,0,50,23, "Kemco Pixel", 16 ) gui_movedown: setReferencePoint (display.BottomLeftReferencePoint) gui_movedown.y = screenH group: insert (gui_movedown)

7. Het speler-schip toevoegen

Vervolgens zullen we het schip van de speler aan het scherm toevoegen. Het schip zal worden toegevoegd als een dynamisch fysica-object, zodat het kan reageren op botsingen met andere fysica-objecten. We zullen later in deze tutorial dieper ingaan op botsingen.

 local ship = display.newImageRect ("images / spaceShip.png", 29, 19) ship.x, ship.y = 75, 35 ship.name = "schip" physics.addBody (schip, "dynamisch", friction = 0.5, bounce = 0) groep: invoegen (verzenden)

8. Het speler-schip verplaatsen

Herinner je je de btn_up en btn_down weergaveobjecten die we hebben toegevoegd? We gaan nu gebeurtenislisteners aan deze objecten toevoegen om het spelersschip te laten bewegen. Wanneer btn_up wordt aangeraakt, zullen we onze snelheid variabel negatief maken en wanneer btn_down wordt aangeraakt, zullen we onze snelheid positief maken. Door deze variabele positief en negatief te maken, vertellen we onze volgende functie om het schip omhoog of omlaag te brengen.

 -- Wanneer de knop omhoog wordt aangeraakt, stelt u onze beweging in om de functie schip omhoog te verplaatsen btn_up: touch () motionx = -snelheid; end btn_up: addEventListener ("touch", btn_up) - Als de knop omlaag wordt ingedrukt, stelt u onze beweging in om de functie schip omlaag te verplaatsen btn_down: touch () motionx = speed; einde btn_down: addEventListener ("touch", btn_down)

9. Beweging inschakelen

Nadat we evenement luisteraars hebben toegevoegd aan onze btn_up en btn_down om objecten weer te geven, gaan we twee runtime-gebeurtenislisteners maken met hun respectieve functies. Deze functies zullen elk frame uitvoeren en de enige met runtime-functies is dat u moet aangeven wanneer u ze wilt stoppen. We zullen dat later behandelen. Voor nu zal de stop-functie de variabele instellen MotionX tot 0 (omdat geen enkele knop wordt aangeraakt) en de moveguy functie voegt de variabele toe MotionX naar ons schip Y positie.

 lokale functie stop (event) als event.phase == "ended" dan motionx = 0; end-end Runtime: addEventListener ("touch", stop) - Deze functie verplaatst het schip feitelijk op basis van de bewegingslocatiefunctie moveguy (event) ship.y = ship.y + motionx; einde Runtime: addEventListener ("enterFrame", moveguy)

10. Kogels afvuren

Inmiddels hebben we ons schip in beweging, maar het vuurt niet! Om het schip klaar te maken om kogels af te vuren, moeten we de fireship () functie. Deze functie maakt nieuwe weergaveobjecten die reageren op natuurbotsingen en deze functie verplaatst het object ook van links naar rechts over het scherm.

Om de game interessanter te maken, zullen we de speler toestaan ​​meer kogels te schieten als deze een bepaalde score bereiken. Wanneer de speler 20 bereikt, schiet het schip twee kogels af en wanneer de speler 40 bereikt, vuurt het schip een derde kogel af die diagonaal naar beneden schiet.

 function fireShip () bullet = display.newImageRect ("images / bullet.png", 13, 8) bullet.x = ship.x + 9 bullet.y = ship.y + 6 bullet: toFront () bullet.name = " bullet "physics.addBody (bullet, isSensor = true) transition.to (bullet, time = bulletSpeed, x = 500, onComplete = function (self) self.parent: remove (self); self = nil; end; ) if (playerScore> = 20) then secondBullet = display.newImageRect ("images / bullet.png", 13, 8) secondBullet.x = ship.x + 9 secondBullet.y = ship.y + 12 secondBullet: toFront ( ) secondBullet.name = "bullet" physics.addBody (secondBullet, isSensor = true) transition.to (secondBullet, time = bulletSpeed, x = 500, onComplete = function (self) self.parent: remove (self); self = nil; end;) end if (playerScore> = 40) then thirdBullet = display.newImageRect ("images / bullet.png", 13, 8) thirdBullet.x = ship.x + 9 thirdBullet.y = verzenden. y + 12 thirdBullet: toFront () thirdBullet.name = "bullet" physics.addBody (thirdBullet, isSensor = true) transition.to (thirdBullet, time = bulletSpeed, x = 500, y = ship.y + 100, onComplete = function (self) self.parent: remove (self); zelf = nul; einde; )

11. Vijanden creëren

Nadat we ons schip in brand hebben gestoken, moeten we de speler een aantal vijanden geven om op te schieten! We zullen twee verschillende functies maken - createSlowEnemy () en createFastEnemy (). Beide functies zullen een fysica-weergaveobject creëren dat van rechts naar links beweegt met de snelheid van de vijand en waarbij het beeld het enige verschil is.

 function createSlowEnemy () enemy = display.newImageRect ("images / enemy.png", 32, 26) vijand.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "vijand" physics.addBody (vijand, isSensor = true) transition.to (vijand, time = slowEnemySpeed, x = -20) eindfunctie createFastEnemy () enemy = display.newImageRect ("images / fastEnemy.png" , 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "vijandige" physics.addBody (vijand, isSensor = true) overgang. om (vijand, time = fastEnemySpeed, x = -20) te eindigen

12. Maak bonuspakketten

Vervolgens maken we bonuspakketten voor onze speler om de functie binnen te halen createBonus (). De createBonus () functie maakt een natuurkundig weergaveobject dat van rechts naar links beweegt en elk bonuspakket dat de speler pakt, krijgt 5 punten.

 functie createBonus () bonus = display.newImageRect ("images / bonus.png", 18, 18) bonus.rotation = 180 bonus.x = 500 bonus.y = math.random (10, scherm H-10) bonus.name = "bonus" physics.addBody (bonus, isSensor = true) transition.to (bonus, time = 1475, x = -20, onComplete = function () display.remove (bonus) bonus = nul einde;) einde

13. Updaten van Player Lives

Onze voorlaatste functie is de updateLives () functie. Deze functie wordt elke keer dat een vijand voorbij de speler komt geroepen om de speler het doel te geven om zijn kant van de ruimte te verdedigen. Als het aantal levens langer is dan 0, trekt deze functie een leven af ​​en werkt de tekst op het scherm bij. Anders zal het resulteren in een game over scène.

In de game over-scène annuleren we al onze timers en verwijderen we al onze evenementluisteraars. Met de Corona SDK is het erg belangrijk om te onthouden dat je expliciet aan je app moet vertellen wanneer runtime-listeners en -timers moeten worden verwijderd (alleen als de timer loopt). Nadat deze zijn verwijderd, zullen we een game-over-bericht weergeven en de speler toestaan ​​terug te keren naar het menu.

 function updateLives () if (playerLives> = 0) then playerLives = playerLives - 1 gui_lives.text = "Lives:" ... playerLives gui_lives.x = schermW else timer.cancel (tmr_fireShip) timer.cancel (tmr_sendSlowEnemies) timer.cancel (tmr_sendSlowEnemies2 ) timer.cancel (tmr_sendFastEnemies) timer.cancel (tmr_sendBonus) Runtime: removeEventListener ("collision", onCollision) Runtime: removeEventListener ("enterFrame", moveguy) Runtime: removeEventListener ("touch", stop) - Geef het spel weer op het lokale scherm gameover_message = display.newText ("Game Over!", 0,0, "Kemco Pixel", 32) gameover_message.x = halfW gameover_message.y = halfY - 15 groep: insert (gameover_message) functie returnToMenuTouch (event) if (event. phase == "started") then storyboard.gotoScene ("menu", "slideRight", "1000") end-end gameover_returntomenu = display.newText ("Return To Menu", 0,0, "Kemco Pixel", 28) gameover_returntomenu .x = halfW gameover_returntomenu.y = halfY + 35 gameover_returntomenu: addEventListener ("touch", returnToMenuTouch) group: in sert (gameover_returntomenu) end end

14. Collision Detection

We zijn klaar voor onze laatste functie in onze scene: createScene () -functie! Deze functie behandelt al onze botsingsdetectie door de eigenschap te vergelijken mijn naam van object1 tot die van object 2. Elk object wordt als een parameter doorgegeven aan deze functie onder de naam van de variabele evenement.

Om het u gemakkelijker te maken, heb ik de vijf gevallen van botsing afgebroken.

  • Zaak 1 - Object 1 is een kogel en Object 2 is een vijand
    Wat is er gaande: Verwijder de vijand en update de score
  • Case 2 - Object 1 is een vijand en Object 2 is een kogel
    Wat is er gaande: Verwijder de vijand en update de score
  • Case 3 - Object 1 is een schip en Object 2 is een bonus
    Wat is er gaande: Verwijder bonus en update score
  • Case 4 - Object 1 is een vijand en Object 2 is een vijandelijke HitBar
    Wat is er gaande: Vijand verwijderen en levens bijwerken
  • Case 5 - Object 1 is een vijandHitBar en Object 2 is een vijand
    Wat is er gaande: Vijand verwijderen en levens bijwerken
 function onCollision (event) if (event.object1.name == "bullet" en event.object2.name == "enemy") then display.remove (event.object2) playerScore = playerScore + 1 elseif (event.object1.name == "vijand" en event.object2.name == "bullet") then display.remove (event.object1) playerScore = playerScore + 1 elseif (event.object1.name == "schip" en event.object2.name = = "bonus") en vervolgens display.remove (event.object2) playerScore = playerScore + 5 elseif (event.object1.name == "enemy" en event.object2.name == "enemyHitBar") then display.remove (event. object1) updateLives () elseif (event.object1.name == "enemyHitBar" en event.object2.name == "enemy") then display.remove (event.object2) updateLives () end gui_score.text = "Score:" ... playerScore gui_score.x = screenW end

15. Bewegingsmomenten synchroniseren

Omdat we alles hebben ingesteld voor onze game, moeten we alles laten bewegen! Binnenkant van de functie scène: enterScene () - onthoud dat de enterScene functie bevindt zich buiten de createScene functie - we zullen 5 timers en een runtime luisteraar aanmaken. De timers sturen de kogels, vijanden en bonussen uit terwijl de runtime-luisteraar de botsingsdetectie afhandelt.

 functie scène: enterScene (evenement) lokale groep = self.view tmr_fireShip = timer.performWithDelay (bulletSpawn, fireShip, 0) tmr_sendSlowEnemies = timer.performWithDelay (slowEnemySpawn, createSlowEnemy, 0) tmr_sendSlowEnemies2 = timer.performWithDelay (slowEnemySpawn + (slowEnemySpawn * 0.5), createSlowEnemy, 0) tmr_sendFastEnemies = timer.performWithDelay (fastEnemySpawn, createFastEnemy, 0) tmr_sendBonus = timer.performWithDelay (2500, createBonus, 0) Runtime: addEventListener ("collision", onCollision) end

16. De scène vernietigen

De laatste toevoeging (ik beloof het!) Is de scène: destroyScene () functie en luisteraars van scene-evenementen. De functie voor het vernietigen van scènes zorgt ervoor dat de fysica wordt verwijderd zodra de speler de scène verlaat. De luisteraars van de scene-evenementen zullen de createScene, enterScene, en destroyScene respectievelijk.

 functie scène: destroyScene (event) lokale groep = self.view pakket.loaded [physics] = nul physics = nul einde scène: addEventListener (scène "createScene", scène): addEventListener (scène "enterScene", scène): addEventListener (" destroyScene ", scène) terugkeren scène

Conclusie

Gefeliciteerd! Je hebt veel dingen geleerd, zoals Corona's storyboard-functie, natuurkunde, botsingen en nog veel meer! Dit zijn waardevolle vaardigheden die op bijna elk spel kunnen worden toegepast en als u deze game voor uw apparaat wilt bouwen, raad ik u ten zeerste aan de officiële Corona-documenten te gebruiken voor het bouwen van het apparaat.

Heel erg bedankt voor het lezen! Als u vragen heeft, laat deze dan achter in de opmerkingen hieronder.