In de vorige zelfstudie van deze serie zijn we begonnen met het implementeren van de gameplay van de game en al in geslaagd om het vliegtuig op het scherm te laten bewegen. In deze zelfstudie gaan we door met het implementeren van de gameplay. Laten we gelijk duiken met de startTimers
functie.
startTimers
Zoals de naam al aangeeft, is de startTimers
functie start de timers. Voeg de volgende code toe aan gamelevel.lua.
functie startTimers () einde
Roep deze functie aan in de enterScene
methode zoals hieronder getoond.
function scene: enterScene (event) local planeSound = audio.loadStream ("planeesound.mp3") planeSoundChannel = audio.play (planeSound, loops = -1) Runtime: addEventListener ("enterFrame", gameLoop) startTimers () einde
firePlayerBullet
De firePlayerBullet
functie creëert een kogel voor de speler.
function firePlayerBullet () local tempBullet = display.newImage ("bullet.png", (player.x + playerWidth / 2) - bulletWidth, player.y-bulletHeight) table.insert (playerBullets, tempBullet); planeGroup: insert (tempBullet) end
Hier gebruiken we het object Weergeven nieuw beeld
methode om de kogel te creëren. We plaatsen het zodanig dat het zich in het midden van het vlak op de x-as en helemaal boven aan het vlak op de y-as bevindt. De kogel wordt vervolgens ingevoegd in de playerBullets
tabel voor latere referentie en ook in de planeGroup
.
firePlayerBullet
We moeten de firePlayerBullet
functioneer periodiek om ervoor te zorgen dat het vliegtuig van de speler automatisch kogels afvuurt. Voeg het volgende codefragment toe in de startTimers
functie.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) einde
Zoals de naam al aangeeft, de Timer's performWithDelay
methode roept een gespecificeerde functie aan nadat een periode is verstreken. De tijd is in milliseconden, dus hier noemen we de firePlayerBullet
functioneer elke twee seconden. Door te passeren -1
als het derde argument, zal de timer voor altijd herhalen.
Als je het spel nu test, zou je moeten zien dat elke twee seconden een bolletje verschijnt. Ze zijn echter nog niet in beweging. Daar zullen we in de volgende paar stappen voor zorgen.
movePlayerBullets
In movePlayerBullets
, we lopen door de playerBullets
tabel en verander de Y
coördinaat van elke kogel. We controleren eerst om zeker te zijn van de playerBullets
tafel heeft kogels erin. De #
voor playerBullets
wordt het lengte operator en retourneert de lengte van het object waarop deze wordt aangeroepen. Het is handig om te weten dat de #
operator werkt ook op strings.
function movePlayerBullets () if (#playerBullets> 0) en dan voor i = 1, # playerBullets doen playerBullets [i]. y = playerBullets [i] .y - 7 end end end
We moeten aanroepen movePlayerBullets
in de gameLoop
functie zoals hieronder getoond.
function gameLoop () --SNIP-- numberOfTicks = numberOfTicks + 1 movePlayer () movePlayerBullets () end
checkPlayerBulletsOutOfBounds
Wanneer een kogel van het scherm gaat, is deze niet langer relevant voor het spel. Ze maken echter nog steeds deel uit van de playerBullets
tafel en blijven bewegen zoals elke andere opsommingsteken in de tabel. Dit is verspilling van middelen en als het spel heel lang door zou gaan, zou dit resulteren in honderden of duizenden ongebruikte objecten.
Om dit te verhelpen, monitoren we de kogels en, als ze eenmaal van het scherm zijn verwijderd, verwijderen we ze uit de playerBullets
zowel als vanuit het display. Bekijk de implementatie van checkPlayerBulletsOutOfBounds
.
function checkPlayerBulletsOutOfBounds () if (#playerBullets> 0) en dan voor i = # playerBullets, 1, -1 do if (playerBullets [i] .y < -18) then playerBullets[i]:removeSelf() playerBullets[i] = nil table.remove(playerBullets,i) end end end end
Het is belangrijk op te merken dat we door de playerBullets
tafel naar achteren. Als we de tabel naar voren doorlopen, zullen we bij het verwijderen van een van de kogels de lusindex weggooien en een fout veroorzaken. Door de tabel in omgekeerde volgorde in een lus te plaatsen, is het laatste opsommingsteken al verwerkt. De removeSelf
methode verwijdert het weergaveobject en maakt het geheugen vrij. Als een best practice, moet u alle objecten instellen nul
na het bellen removeSelf
.
We roepen deze functie aan in de gameLoop
functie.
function gameLoop () --SNIP-- movePlayer () movePlayerBullets () checkPlayerBulletsOutOfBounds () einde
Als u wilt zien of deze functie goed werkt, kunt u tijdelijk een print ("Bullet verwijderen")
direct nadat het weergaveobject is ingesteld nul
.
generateIsland
Om het spel interessanter te maken, genereren we af en toe een eiland en verplaatsen het het scherm om het uiterlijk te geven van het vliegtuig dat over de eilanden vliegt. Voeg het volgende codefragment toe voor de generateIsland
functie.
function generateIsland () local tempIsland = display.newImage ("island1.png", math.random (0, display.contentWidth - islandWidth), - islandHeight) table.insert (islands, tempIsland) islandGroup: insert (tempIsland) end
We maken gebruik van de nieuw beeld
methode nogmaals en positioneer het eiland door een negatieve waarde in te stellen voor de islandHeight
. Voor de X
positie gebruiken we de Math.random
methode om een getal tussen te genereren 0
en de tonen
's contentWidth
minus de islandWidth
. De reden dat we de breedte van het eiland aftrekken, is ervoor te zorgen dat het eiland volledig op het scherm staat. Als we de breedte van het eiland niet zouden aftrekken, zou er een kans zijn dat een deel van het eiland niet op het scherm zou verschijnen.
We moeten een timer starten om zo nu en dan een eiland te genereren. Voeg het volgende fragment toe aan de startTimers
functie die we eerder hebben gecreëerd. Zoals je ziet, genereren we elk een eilandvijf seconden. In de volgende stap zullen we de eilanden laten bewegen.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) end
moveIslands
De implementatie van moveIslands
is bijna identiek aan de movePlayerBullets
functie. We controleren of het eilanden
tafel bevat alle eilanden en als dat zo is, gaan we er doorheen en verplaatsen we elk eiland een beetje.
function moveIslands () if (#islands> 0) en dan voor i = 1, #islands do eilanden [i] .y = eilanden [i] .y + 3 end end end
checkIslandsOutOfBounds
Net zoals we controleren of de kogels van de speler van het scherm zijn verplaatst, controleren we of een van de eilanden buiten beeld is verplaatst. De implementatie van checkIslandsOutOfBounds
moet je daarom bekend voorkomen. We controleren of de eilanden Y
positie is groter dan display.contentHeight
en als dat zo is, weten we dat het eiland buiten beeld is verplaatst en daarom kan worden verwijderd.
functie checkIslandsOutOfBounds () if (#islands> 0) en dan voor i = # eilanden, 1, -1 do if (eilanden [i] .y> display.contentHeight) en vervolgens eilanden [i]: removeSelf () eilanden [i] = nul table.remove (eilanden, i) einde end end end
generateFreeLife
Om de zoveel tijd heeft de speler een kans om een gratis leven te krijgen. We genereren eerst een gratis afbeelding van het leven en als de speler in botsing komt met de afbeelding, krijgen ze een extra leven. De speler kan maximaal zes levens hebben.
function generateFreeLife () if (numberOfLives> = 6) en dan end end local freeLife = display.newImage ("newlife.png", math.random (0, display.contentWidth - 40), 0); table.insert (freeLifes, freeLife) planeGroup: insert (freeLife) end
Als de speler al zes levens heeft, doen we niets door vroeg uit de functie terug te komen. Als dat niet het geval is, maken we een nieuwe levensafbeelding en voegen deze toe aan het scherm. Vergelijkbaar met hoe we de eilanden eerder hebben gepositioneerd, stellen we het beeld op een negatief Y
positie en genereer een willekeurige waarde voor de afbeeldingen X
positie. We plaatsen het vervolgens in de freeLifes
tabel om er later naar te kunnen verwijzen.
We moeten deze functie af en toe bellen. Voeg het volgende fragment toe aan de startTimers
functie.
function startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) generateFreeLifeTimer = timer.performWithDelay (7000, generateFreeLife, - 1) end
moveFreeLives
De implementatie van moveFreeLifes
moet bekend voorkomen. We lopen door de freeLifes
tabel en verplaats elke afbeelding erin.
functie moveFreeLifes () if (#freeLifes> 0) en dan voor i = 1, # freeLifes doen freeLifes [i] .y = freeLifes [i] .y +5 end end end
We hoeven alleen maar te bellen moveFreeLifes
in de gameLoop
functie.
function gameLoop () --SNIP-- checkIslandsOutOfBounds () moveFreeLifes () einde
checkFreeLifesOutOfBounds
Het volgende codefragment hoort er nu ook bekend uit te zien. We controleren of een van de afbeeldingen in de freeLifes
tafel zijn verplaatst van het scherm en verwijder degenen die hebben.
functie checkFreeLifesOutOfBounds () if (#freeLifes> 0) en dan voor i = # freeLifes, 1, -1 do if (freeLifes [i] .y> display.contentHeight) then freeLifes [i]: removeSelf () freeLifes [i] = nul table.remove (freeLifes, i) einde end end end
We noemen deze functie in de gameLoop
functie.
function gameLoop () --SNIP-- checkIslandsOutOfBounds () moveFreeLifes () checkFreeLifesOutOfBounds () einde
hasCollided
We moeten kunnen zien wanneer game-objecten met elkaar botsen, zoals het vliegtuig van de speler en de gratis afbeeldingen van het leven, de kogels en de vliegtuigen, enz. Hoewel Corona een zeer robuuste physics-engine biedt die botsingen tussen weergave-objecten gemakkelijk kan verwerken voor ons voegt dit een beetje overhead toe met de berekeningen die de motor in elk frame moet uitvoeren.
Voor de doeleinden van deze game zullen we een eenvoudig begrenzingsdetectiesysteem voor grensboxen gebruiken. Wat deze functie doet, is ervoor zorgen dat de rechthoeken of begrenzende vakken rond twee objecten elkaar niet overlappen. Als ze dat doen, botsen de objecten. Deze logica is geïmplementeerd in de hasCollided
functie.
function hasCollided (obj1, obj2) if (obj1 == nil) retourneer dan false end if (obj2 == nil) en vals einde local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin local right = obj1.contentBounds.xMin> = obj2.contentBounds.xMin en obj1.contentBounds.xMin <= obj2.contentBounds.xMax local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin local down = obj1.contentBounds.yMin> = obj2.contentBounds.yMin en obj1.contentBounds.yMin <= obj2.contentBounds.yMax return (left or right) and (up or down) end
Ik heb dit codefragment gevonden op de CoronaLabs-website. Het werkt heel goed, omdat de spelobjecten in onze game rechthoekig zijn. Als u met een object werkt dat niet rechthoekig is, kunt u beter gebruikmaken van de physics-engine van Corona, omdat de botsingsdetectie hiervoor zeer goed is geoptimaliseerd..
checkPlayerCollidesWithFreeLife
We willen controleren of het vliegtuig van de speler in botsing is gekomen met een gratis levensobject. Als dat zo is, kennen we de speler een gratis leven toe.
function checkPlayerCollidesWithFreeLife () if (#freeLifes> 0) en dan voor i = # freeLifes, 1, -1 do if (hasCollided (freeLifes [i], player)) then freeLifes [i]: removeSelf () freeLifes [i] = nihil table.remove (freeLifes, i) numberOfLives = numberOfLives + 1 hideLives () showLives () end end end end
In de checkPlayerCollidesWithFreeLife
functie doorlopen we de freeLives
tafel achteruit om dezelfde reden die ik eerder heb beschreven. We noemen het hasCollided
functie en geef de huidige afbeelding en het vliegtuig van de speler door. Als de twee objecten botsen, verwijderen we de gratis afbeelding van het leven, verhogen de numberOfLives
variabele, en bel de hideLives
en showLives
functie.
We roepen deze functie aan in de gameLoop
functie.
function gameLoop () --SNIP-- moveFreeLifes () checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () einde
hideLives
De hideLives
functie loopt door de livesImages
tabel en stelt de is zichtbaar
eigendom van elk levensbeeld aan vals
.
function hideLives () voor i = 1, 6 do livesImages [i] .isVisible = false end end
showLives
De showLives
functie loopt door de livesImages
tabel en stelt elke afbeelding in is zichtbaar
eigendom aan waar
.
function showLives () voor i = 1, numberOfLives do livesImages [i] .isVisible = true; einde
Dit brengt het derde deel van deze serie tot een einde. In het volgende en laatste deel van deze serie zullen we vijandelijke vliegtuigen maken en de gameplay van de game voltooien. Bedankt voor het lezen en zie je daar.