Bouw een Endless Runner Game from Scratch Obstacles and Enemies

Welkom bij het zesde deel in onze Endless Runner-zelfstudiereeks! Ons spel begint zich vorm te geven en voelt gepolijst aan. Na het voltooien van de stappen van vandaag, zal het veel interactiever en leuker zijn om te spelen. We zullen nog twee evenementen toevoegen die een aantal spannende nieuwe uitdagingen voor de spelers zullen creëren. Ten eerste voegen we de mogelijkheid toe dat onze held een kleine grendel schiet om obstakels die op hem afkomen te verwijderen. Houd in gedachten dat de kleine "blueish" afbeelding wordt afgeschoten van ons monster. De afbeeldingen waarmee we werken, zijn niet bijzonder professioneel, omdat ze gewoon voor de praktijk zijn. Nadat het monster de mogelijkheid heeft om een ​​paar bouten af ​​te vuren, voegen we ook gebeurtenissen toe die obstakels naar hem terugsturen. Daarna zullen we twee soorten obstakels creëren: de eerste is een spijkermuur die op de grond zal zitten en met dezelfde snelheid als de grond zal reizen, en de tweede is een geest die vanaf de rechterbovenkant van de grond naar binnen zal vliegen het scherm naar de speler.

Dus laten we beginnen! Het eerste dat we moeten doen is zorgen dat we alle assets op de juiste plek hebben staan. Download het bestand dat is gekoppeld aan deze tutorial en in de map "new" vindt u drie nieuwe items. U kunt ook met de rechtermuisknop op de afbeeldingen klikken en ze in de juiste map opslaan. De afbeeldingen moeten spikeBlock.png, ghost.png en blast.png heten.

Open uw main.lua-bestand dat te vinden is in de "oude" map van de tutorialdownloads. We beginnen met het geven van vuur aan onze spelers omdat we ze niet weerloos willen laten terwijl ze worden gebombardeerd met nieuwe uitdagingen! We kunnen dit vermogen in drie stappen toevoegen. De eerste zal 5 bouten maken en ze in een groep plaatsen. In plaats van objecten te maken en te vernietigen elke keer dat we ze willen gebruiken, kunnen we efficiënter zijn door ze voor te bereiden en op te slaan buiten het scherm totdat we ze nodig hebben. We gaan eigenlijk hetzelfde doen voor elk van de drie verschillende afbeeldingen, dus ga je gang en voeg deze code toe tussen waar we de variabele snelheid declareren en waar we onze blokken maken.

 --creëer spoken en stel hun positie in als buiten beeld voor a = 1, 3, 1 do ghost = display.newImage ("ghost.png") ghost.name = ("ghost" ... a) ghost.id = een geest. x = 800 ghost.y = 600 ghost.speed = 0 --variable gebruikt om te bepalen of ze in het spel zijn of niet ghost.isAlive = false - maak de geesten transparant en meer ... spookachtig! ghost.alpha = .5 ghosts: insert (ghost) end - maak spikes voor a = 1, 3, 1 do spike = display.newImage ("spikeBlock.png") spike.name = ("spike" ... a) spike .id = a spike.x = 900 spike.y = 500 spike.isAlive = false spikes: insert (spike) end - maak ontploffingen voor a = 1, 5, 1 do blast = display.newImage ("blast.png" ) blast.name = ("blast" ... a) blast.id = a blast.x = 800 blast.y = 500 blast.isAlive = false blasts: insert (blast) end

Merk op dat ze allemaal een aantal gemeenschappelijke kenmerken hebben. We moeten ze doornemen en een naam geven voor het geval we er ooit individueel naar willen verwijzen. We geven ze een ID en geven ons ook meer toekomstige referentie-opties. We geven ze hun offscreen x- en y-coördinaten (we stapelen soortgelijke afbeeldingen op elkaar). Een ander ding dat we ze allemaal geven, is de variabele 'levend'. Dit staat standaard ingesteld op 'onwaar' omdat deze van het scherm zullen afhangen. Door de sprites deze variabele te geven, kunnen we snel controleren of de sprite al dan niet in gebruik is. De enige sprite die iets anders heeft, is de Ghost Sprite. Voor ghost sprites voegen we een snelheidsvariabele toe, zodat we spoken met willekeurige snelheden kunnen laten bewegen. We zullen ook de alpha van de geesten instellen op .5 zodat ze meer lijken, laten we zeggen, spookachtig!

De volgende twee dingen die we moeten doen, zijn het maken van hun respectievelijke weergavegroepen en vervolgens die groepen aan de schermweergave toevoegen, zodat we ze in het spel kunnen zien. Voeg deze toe recht onder de instantiatie van de andere displaygroepen.

 local ghosts = display.newGroup () local spikes = display.newGroup () local blasts = display.newGroup ()

Ga vervolgens naar het gedeelte waar we alles aan het scherm toevoegen. Voeg die displaygroepen toe met de rest. Ik zou de nieuwe bestelling er ongeveer zo uit laten zien:

 --het merendeel van de volgorde hier doet er niet toe zolang de achtergrond - achterin staan ​​en de geesten en monsters op het eindscherm staan: insert (backbackground) scherm: insert (achtergrondfar) scherm: insert (backgroundnear1) scherm: insert (backgroundnear2 ) scherm: insert (blokken) scherm: insert (spikes) scherm: insert (blasts) scherm: insert (ghosts) scherm: insert (monster) scherm: insert (collisionRect)

Als je eenmaal alles daarin hebt, zou het er ongeveer zo uit moeten zien als je het in het perspectief van de iPhone 4 zou uitvoeren:

Nadat je dat op zijn plaats hebt gezet, voegen we de functie toe die alle bouten die ons monster maakt, updatet. Voeg de volgende functie toe onder de functie checkCollisions ().

 functie updateBlasts () - voor elke ontploffing die we hebben geconstateerd om te kijken wat het doet voor a = 1, blasts.numChildren, 1 do - als die ontploffing niet in het spel is, hoeven we niets anders te controleren als ( blasts [a] .isAlive == true) then (blasts [a]): translate (5, 0) --als de explosie van het scherm is afgegaan, dood het dan en breng het terug naar de oorspronkelijke plaats als (blasts [ a] .x> 550) en vervolgens ontploffingen [a] .x = 800 ontploffingen [a] .y = 500 ontploffingen [a] .isAlive = vals einde - check voor botsingen tussen de ontploffingen en de pieken voor b = 1, spikes.numChildren, 1 do if (spikes [b] .isAlive == true) then if (blasts [a] .y - 25> spikes [b] .y - 120 en blasts [a] .y + 25 < spikes[b].y + 120 and spikes[b].x - 40 < blasts[a].x + 25 and spikes[b].x + 40 > ontploffingen [a] .x - 25) en vervolgens ontploffingen [a] .x = 800 ontploffingen [a] .y = 500 ontploffingen [a] .isAlive = verkeerde pieken [b] .x = 900 pieken [b] .y = 500 spikes [b] .isAlive = valse einden - controle van botsingen tussen de ontploffingen en de geesten voor b = 1, geesten.numChildren, 1 do if (ghosts [b] .isAlive == true) then if (blasts [ a] .y - 25> spoken [b] .y - 120 en ontploffingen [a] .y + 25 < ghosts[b].y + 120 and ghosts[b].x - 40 < blasts[a].x + 25 and ghosts[b].x + 40 > ontploffingen [a] .x - 25) en vervolgens ontploffingen [a] .x = 800 ontploffingen [a] .y = 500 ontploffingen [a] .isAlive = verkeerde geesten [b] .x = 800 ghosts [b] .y = 600 spoken [b] .isAlive = false ghosts [b] .speed = 0 end end end end end

De bovenstaande functie zal twee hoofdverantwoordelijkheden hebben. De eerste is om de positie van de explosie bij te werken en te controleren of deze ergens tegenaan botste. Je kunt zien dat wanneer het botst met iets, de manier waarop we het vernietigen is door het simpelweg van het scherm te verplaatsen en isAlive op false in te stellen. Hierdoor wordt de bout klaar om opnieuw te worden gebruikt. Iets anders dat je misschien hebt opgemerkt, is dat we maar vijf bouten maken. Wat gebeurt er als alle vijf de bouten al in het spel zijn en de gebruiker een andere ontploffing probeert te ontbranden? Niets! Met de manier waarop het nu is ingesteld, is de gebruiker beperkt tot slechts vijf actieve ontploffingen in één keer. Dit is gemakkelijk te veranderen door meer te instantiëren aan het begin van het spel, maar het laat je zien hoe je limieten instelt voor spelers. Dit is ook een methode die we zullen gebruiken om ervoor te zorgen dat 50 spoken niet willekeurig alles tegelijk op het scherm genereren, waardoor de speler direct wordt gedood.

Vervolgens moeten we naar de aangeraakte functie gaan om de speler de mogelijkheid te geven om te schieten. Wijzig de aangeraakte functie zodat deze overeenkomt met het volgende:

 --het enige verschil in de aangeraakte functie is nu dat als je de --rechtse kant van het scherm aanraakt, het monster zal ontbranden een beetje blauwe boutfunctie aangeraakt (gebeurtenis) als (event.phase == "begon") dan if (event. X < 241) then if(onGround) then monster.accel = monster.accel + 20 end else for a=1, blasts.numChildren, 1 do if(blasts[a].isAlive == false) then blasts[a].isAlive = true blasts[a].x = monster.x + 50 blasts[a].y = monster.y break end end end end end

Zorg er ten slotte voor dat u updateBlasts () aan de hoofdupdate-functie toevoegt en dat u klaar moet zijn om te gaan. Probeer dat eens en zie de geweldige kracht die ons monster nu heeft! Nadat je het hebt uitgevoerd, zou je zoiets als dit moeten zien:

Nu de spelers zichzelf kunnen verdedigen, laten we doorgaan en ze wat uitdagingen bieden. Beide volgende gebeurtenissen worden overwonnen door ze eenvoudig één keer te fotograferen. Nadat ze op hun plaats zijn, kunt u ze echter eenvoudig aanpassen aan uw eigen project. We zullen beginnen met de puntige muur.

De puntige muur wordt gewoon op de huidige grond geplaatst en beweegt met dezelfde constante snelheid als de grond eromheen. Het eerste wat u moet doen is de updateSpikes-functie toevoegen. Direct onder de updateBlasts () functie voeg de volgende code toe:

 --controleer om te zien of de spikes nog in leven zijn of niet, of ze zijn - update ze dan naar behoren function updateSpikes () voor a = 1, spikes.numChildren, 1 do if (spikes [a] .isAlive == true) then (spikes [a]): translate (speed * -1, 0) als (spikes [a] .x < -80) then spikes[a].x = 900 spikes[a].y = 500 spikes[a].isAlive = false end end end end

Dit zal hetzelfde doen als onze updateBlast-functie. Het zal eenvoudig de positie van de spijkermuur bijwerken en controleren of het van het scherm is gegaan (wat het op dit moment niet kan doen omdat het de speler het eerst zou raken). Voor het geval dat we het echter controleren. Het laatste dat we moeten doen, is er een evenement voor maken. Voeg in checkEvent () onder de andere controles dit toe:

 --hoe vaker u wilt dat er gebeurtenissen plaatsvinden - hoe groter u moet controleren of (vink> 72 aan en vink aan < 81) then inEvent = 12 eventRun = 1 end

We gaan onze muur daadwerkelijk toevoegen in de updateBlocks () -functie. Op die manier weten we zeker dat we het huidige grondniveau hebben. Precies voordat u checkEvent () aanroept, voegt u het volgende in:

 --door de spikes op deze manier in te stellen, zijn we gegarandeerd - slechts 3 spikes per keer maximaal. if (inEvent == 12) en dan voor a = 1, spikes.numChildren, 1 do if (spikes [a] .isAlive == true) en vervolgens niets meer spikes [a] .isAlive = true spikes [a]. y = groundLevel - 200 spikes [a] .x = newX break-end-end-end

Het laatste wat u moet doen om dit op gang te krijgen, is door de botsingsdetectie toe te voegen om te zien of onze speler er tegenaan liep of niet. Zet dit recht onder de sectie in checkCollisions () waar we de botsingen tussen de blokken en het monster controleren.

 --stop het spel als het monster tegen een spijkermuur botst voor a = 1, spikes.numChildren, 1 do if (spikes [a] .isAlive == true) then if (collisionRect.y - 10> spikes [a] .y - 170 en spikes [a] .x - 40 < collisionRect.x and spikes[a].x + 40 > collisionRect.x) en stop dan de monster snelheid = 0 einde einde

Zorg ervoor dat u updateSpikes () opnieuw toevoegt aan de hoofdupdatefunctie en geef het een run! Je zou nu deze spikes in het pad van je monster moeten hebben.

Geef dat een werveling en oefen ze neer. Vergeet niet om te testen om ervoor te zorgen dat de botsingen correct werken met de ontploffing. Zorg er ook voor dat tegen de muren lopen de speler daadwerkelijk wordt gedood. Zodra je dat hebt getest en klaar bent voor meer, begin je met het plaatsen van de code voor de geest.

Het plaatsen van de geest is vrijwel identiek aan het plaatsen van de muur. Het grootste verschil tussen de twee is dat voor de geest we gaan randomiseren waar het op komt en hoe snel het reist. We zorgen er ook voor dat de spoken op en neer gaan, zodat ze altijd bewegen naar waar de gebruiker zich bevindt. Met speciale details zoals deze zullen de obstakels voor de gebruiker heel anders aanvoelen, ook al werken ze op een vergelijkbare manier.

Laten we opnieuw beginnen door de functie updateGhosts () toe te voegen. Terwijl u bezig bent, kunt u ook updateGhosts () toevoegen aan de hoofdupdatefunctie.

 --werk de geesten bij als ze nog leven functie updateGhosts () voor a = 1, spoken.numChildren, 1 do if (geesten [a] .isAlive == true) then (spoken (a)): translate (speed * -1, 0 ) if (ghosts [a] .y> monster.y) then ghosts [a] .y = ghosts [a] .y - 1 end if (ghosts [a] .y < monster.y) then ghosts[a].y = ghosts[a].y + 1 end if(ghosts[a].x < -80) then ghosts[a].x = 800 ghosts[a].y = 600 ghosts[a].speed = 0 ghosts[a].isAlive = false; end end end end

Ga vervolgens terug naar de functie checkEvent () en voeg deze toe onder de vorige controles:

 --ghost event if (check> 60 en check < 73) then inEvent = 13 eventRun = 1 end

Deze keer in plaats van dat de spoken van de updateBlock-functie worden verplaatst, doen we dit in de runEvent () -functie. Voeg dit toe onder de andere if-statements:

 --dit zal een beetje anders zijn, omdat we willen dat dit echt - het spel nog meer willekeurig maakt. verander waar de spoken - gespawn en hoe snel ze bij het monster komen. if (inEvent == 13) dan voor a = 1, ghosts.numChildren, 1 do if (ghosts [a] .isAlive == false) dan ghosts [a] .isAlive = true ghosts [a] .x = 500 ghosts [ a] .y = math.random (-50, 400) spoken [a] .speed = math.random (2,4) break-end end-end

Als dat er eenmaal is, moeten we controleren op botsingen tussen het monster en de geesten. Voeg dit toe aan checkingCollions () direct nadat we het werk hebben gedaan voor de spijkermuren:

 --zorg ervoor dat de speler niet geraakt werd door een geest! voor a = 1, ghosts.numChildren, 1 do if (ghosts [a] .isAlive == true) then if (((((monster.y-ghosts [a] .y))<70) and ((monster.y - ghosts[a].y) > -70)) en (geesten [a] .x - 40 < collisionRect.x and ghosts[a].x + 40 > collisionRect.x)) - stop dan de monstersnelheid = 0 einduiteinde

Je zou nu alles op zijn plaats moeten hebben, inclusief de geesten om te ontwijken!

De game zou nu veel leuker en uitdagender moeten zijn voor gebruikers. In de volgende tutorial voegen we nog twee dingen toe die ervoor zorgen dat onze game vollediger wordt: (1) een scoresysteem en (2) monstersterfte! We hebben veel behandeld in deze tutorial, dus als je vragen hebt, kun je het beste luisteren in de reacties hieronder. Bedankt voor het lezen!