I denne opplæringsserien lærer vi hvordan du lager et romskytespill akkurat som det klassiske spillet Space Defender. Les videre!
I denne versjonen av Space Defender må spilleren forsvare sin plass ved å skyte fiender. Hver gang spilleren ødelegger en fiende, vil de tjene poeng og når spilleren har nådd 20 eller 40 poeng, vil pistolen motta en oppgradering. For å blande ting opp, vil dette spillet sende ut bonuspakker som er verdt 5 poeng. For å se spillet i aksjon, se den korte videoen ovenfor.
I del 1 av denne serien lærte vi hvordan du konfigurerer vår app, hvordan du bruker egendefinerte skrifter, hvordan du bruker et storyboard og hvordan du konfigurerer hovedmenyen. I del 2 av denne serien lærer vi hvordan å lage spillingen av appen vår. Så, la oss komme i gang!
Vårt første skritt er å lage en ny fil som heter game.lua. Når den er opprettet, åpner du filen i favorittredigeren din.
Siden vi starter en ny scene, må vi kreve noen biblioteker. Vi bruker fysikkmotoren som er bygd inn i Corona SDK for kollisjonsdeteksjon.
lokal storyboard = krever ("storyboard") lokal scene = storyboard.newScene () lokal fysikk = krever ("fysikk")
Etter at vi har installert bibliotekene våre, konfigurerer vi fysikkmotoren. Innstillingene under vil sette tyngdekraften til 0 (akkurat som i rommet) og sette iterasjonene til 16. SetPositionIterations betyr at motoren går gjennom 16 stillinger per ramme. Alt høyere enn 16 kan påvirke spillytelsen negativt.
physics.start () physics.setGravity (0, 0) physics.setPositionIterations (16)
Selv om dette trinnet ikke er nødvendig for denne opplæringen, er det en god praksis å "frø" den tilfeldige tallgeneratoren. Jeg liker å bruke den nåværende tiden til å frø generatoren.
math.randomseed (os.time ())
Nå skal vi definere noen variabler for spillet vårt. Hver variabel har en kommentar ved siden av den som forklarer formålet med variabelen.
lokal skjermW, skjermH, halvV, halvY = display.contentWidth, display.contentHeight, display.contentWidth * 0.5, display.contentHeight * 0,5 lokal gameover_returntomenu - fremover deklare vårt spill over-knapp - Still inn spillinnstillinger motionx = 0; - Variabel brukes til å flytte tegn langs y-aksens hastighet = 10; - Kontrollerer fartshastighetsspillerenScore = 0; - Setter spillerens score playerLives = 20; - Angir antall liv for spilleren slowEnemySpeed = 2375; - Angir hvor raskt de hvite skipene beveger seg over skjermen slowEnemySpawn = 2400; - Angir hvitt skipspesjonshastighet fastEnemySpeed = 1875; - Angir hvor fort de grønne skipene beveger seg over skjermen fastEnemySpawn = 1800; - Angir grønt skipgodtall bulletSpeed = 325; - Angir hvor fort kulen beveger seg over skjermen bulletSpawn = 250; - Angir kullsats
Etter å ha opprettet variablene, skal vi sette opp scenen inne i funksjonen scene: createScene
. Hvis du husker fra del 1, brukes denne funksjonen til å lage visuelle elementer og spilllogikk. I en senere funksjon vil vi ringe på disse funksjonene for å kjøre spillet.
I den følgende koden, oppretter vi scene: createScene
funksjon og legge til bakgrunns- og topp- / bunnmurer. Begge veggene er satt opp som statiske fysikkobjekter for å forhindre at spilleren går av skjermen.
funksjonsscene: createScene (event) local group = self.view - Konfigurer visuelle elementer og vegger lokale bg = display.newImageRect ("images / BKG.png", 480, 320) bg.x = halvV bg.y = halfY gruppe: sett inn (bg) local topwall = display.newRect (0,0, screenW, 20) topwall.y = -5 topwall: setFillColor (0,0,0) topwall.alpha = 0.01 physics.addBody (topwall, "static ") gruppe: sett inn (topwall) lokal bottomwall = display.newRect (0,0, screenW, 20) bottomwall.y = 325 bottomwall: setFillColor (0,0,0) bottomwall.alpha = 0.01 physics.addBody (bottomwall, statisk ") gruppe: sett inn (bunnmur) ende
Innsiden av det samme scene: createScene
funksjon, men etter bottomwall
Vis objekt, vi skal legge til fire flere skjermobjekter. Her er en forklaring på formålet med hvert objekt.
btn_up
, btn_down
: Disse displayobjektene vil fungere som knapper på venstre side av skjermen, og hver gjenstand vil flytte skipet opp eller ned henholdsvis. De kan imidlertid ikke brukes før vi setter opp flyttingsfunksjonen.enemyHitBar
: Dette skjermobjektet er opprettet som en sensor og vil bare reagere på fysiske kollisjoner. Når det reagerer på kollisjoner, vil det fjerne fiendens gjenstand og trekke en fra spillernes liv.lokal 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 gruppe: sett inn (btn_up) lokal btn_down = display.newRect (0,0,75,160) btn_down: setReferencePoint (display.BottomLeftReferencePoint) btn_down.x, btn_down.y = 0, skjermH; btn_down.alpha = 0.01 gruppe: sett inn (btn_down) lokal enemyHitBar = display.newRect (-20,0,20,320) enemyHitBar: setFillColor (0,0,0) enemyHitBar.name = "enemyHitBar" physics.addBody (enemyHitBar, isSensor = true) gruppe: sett inn (enemyHitBar)
Like etter fiendtligHitBar-visningsobjektet, skal vi legge til noen GUI-elementer for å vise spilleren poengsummen og spilleren lever. Vi vil også vise tekst på skjermen som sier "Flytt opp" og "Flytt ned" for å varsle spilleren der de trenger å røre for å flytte skipet opp eller ned.
lokal gui_score = display.newText ("Score:" ... playerScore, 0,0, "Kemco Pixel", 16) gui_score: setReferencePoint (display.TopRightReferencePoint) gui_score.x = screenW gruppe: sett inn (gui_score) local gui_lives = display.newText ("Lives:" ... playerLives, 0,0, "Kemco Pixel", 16) gui_lives: setReferencePoint (display.BottomRightReferencePoint) gui_lives.x = screenW gui_lives.y = screenH gruppe: sett inn (gui_lives) local gui_moveup = display.newText "Flytt opp", 0,0,50,100, "Kemco Pixel", 16) gruppe: sett inn (gui_moveup) lokal gui_movedown = display.newText ("Flytt ned", 0,0,50,23, "Kemco Pixel", 16 ) gui_movedown: setReferencePoint (display.BottomLeftReferencePoint) gui_movedown.y = screenH gruppe: sett inn (gui_movedown)
Deretter skal vi legge spillerens skip til skjermen. Skipet vil bli lagt til som et dynamisk fysikkobjekt, slik at det kan reagere på kollisjoner med andre fysikkobjekter. Vi vil gå inn i ytterligere dybde på kollisjoner senere i denne opplæringen.
lokalt skip = display.newImageRect ("images / spaceShip.png", 29, 19) ship.x, ship.y = 75, 35 skip.name = "skip" fysikk.addBody (skip, "dynamisk", friksjon = 0,5, sprette = 0) gruppe: sett inn (skip)
Husker du btn_up
og btn_down
vise objekter vi har lagt til? Vi skal nå legge til hendelseslyttere på disse objektene for å bidra til at spilleren går i bevegelse. Når btn_up
er berørt, vi vil gjøre vår hastighet variabel negativ og når btn_down
er berørt vi vil gjøre vår fart positiv. Ved å gjøre denne variabelen positiv og negativ, forteller vi vår neste funksjon for å flytte skipet opp eller ned.
-- Når opp-knappen berøres, setter du vår bevegelse for å flytte opp skipfunksjonen btn_up: touch () motionx = -speed; avslutte btn_up: addEventListener ("touch", btn_up) - Når ned-knappen berøres, still inn bevegelsen for å flytte skipet ned-funksjonen btn_down: touch () motionx = speed; avslutte btn_down: addEventListener ("touch", btn_down)
Etter at vi har lagt hendelseslyttere til vår btn_up
og btn_down
skjermobjekter, skal vi lage to runtime hendelse lyttere med sine respektive funksjoner. Disse funksjonene kjører hver ramme, og den ene fange med kjøretidsfunksjoner er at du må spesifisere når du skal stoppe dem. Vi vil dekke det senere. For nå vil stoppfunksjonen sette variabelen MotionX
til 0 (fordi ingen av knappene berøres) og moveguy
funksjonen vil legge til variabelen MotionX
til vårt skip y
stilling.
Lokal funksjonstopp (hendelse) hvis event.phase == "endte" da motionx = 0; end-end Runtime: addEventListener ("touch", stop) - Denne funksjonen vil faktisk flytte skipet basert på bevegelses lokal funksjonen moveguy (event) ship.y = ship.y + motionx; slutt Runtime: addEventListener ("enterFrame", moveguy)
Nå har vi vårt skip flyttet, men det skyter ikke! For å få skipet klar til ildkuler, må vi lage fireShip ()
funksjon. Denne funksjonen vil skape nye visningsobjekter som reagerer på fysikkkollisjoner, og denne funksjonen vil også flytte objektet over skjermen fra venstre til høyre.
For å gjøre spillet mer interessant, vil vi la spilleren skyte flere kuler når de når en bestemt score. Når spilleren når 20, vil skipet skyte to kuler og når spilleren når 40, vil skipet brenne en tredje kule som skyter nedover diagonalt.
funksjon 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, isSoror = true) transition.to (bullet, time = bulletSpeed, x = 500, onComplete = funksjon (selv) selv.parent: fjern (selv); self = nil; end; ) hvis (playerScore> = 20) så 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 = funksjon (selv) selv.parent: fjern selv = null; slutt;) avslutte hvis (playerScore> = 40) så tredjeBullet = display.newImageRect ("images / bullet.png", 13, 8) thirdBullet.x = ship.x + 9 thirdBullet.y = skip. y + 12 tredjeBullet: toFront () thirdBullet.name = "bullet" physics.addBody (tredjeBullet, isSensor = true) transition.to (thirdBullet, time = bulletSpeed, x = 500, y = ship.y + 100, onComplete = funksjon (selv) selv.parent: fjern (selv); selv = null; slutt; )
Etter at vi har satt opp vårt skip til brann, må vi gi spilleren noen fiender til å skyte på! Vi lager to forskjellige funksjoner - createSlowEnemy ()
og createFastEnemy ()
. Begge funksjonene vil skape et fysikkskjermobjekt som beveger seg fra høyre til venstre med fiendens hastighet og bildet er den eneste forskjellen.
funksjon createSlowEnemy () fiende = display.newImageRect ("images / enemy.png", 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, skjermH-10) enemy.name = "fiende" fysikk.addBody (fiende, isSensor = true) transition.to (fiende, time = slowEnemySpeed, x = -20) sluttfunksjon createFastEnemy () fiende = display.newImageRect ("images / fastEnemy.png" , 32, 26) enemy.rotation = 180 enemy.x = 500 enemy.y = math.random (10, screenH-10) enemy.name = "fiende" physics.addBody (fiende, isSoror = true) overgang. til (fiende, time = fastEnemySpeed, x = -20) ende
Deretter skal vi lage bonuspakker for at spilleren skal kunne gripe inn i funksjonen createBonus ()
. De createBonus ()
funksjonen vil skape et fysikk-skjermobjekt som beveger seg rett til venstre og hver bonuspakke spilleren tar tak i, de vil tjene 5 poeng.
funksjon createBonus () bonus = display.newImageRect ("images / bonus.png", 18, 18) bonus.rotation = 180 bonus.x = 500 bonus.y = math.random (10, skjermH-10) bonus.name = "bonus" physics.addBody (bonus, isSensor = true) transition.to (bonus, time = 1475, x = -20, onComplete = funksjon () display.remove (bonus) bonus = null ende;) slutten
Vår ved siden av sist funksjon er funksjonen updateLives (). Denne funksjonen blir kalt hver gang en fiende kommer forbi spilleren for å gi spilleren målet om å forsvare sin side av rommet. Hvis antall liv er over 0, vil denne funksjonen trekke et liv og oppdatere skjermteksten. Ellers vil det resultere i et spill over scenen.
I spillet over scenen avbryter vi alle våre tidtakere og fjerner alle våre hørere. Med Corona SDK, er det veldig viktig å huske at du må eksplisitt fortelle appen din når du skal fjerne lurtere og tidtakere (bare når timeren kjører). Etter at disse har blitt fjernet, vil vi vise et spill over meldingen og la spilleren komme tilbake til menyen.
funksjon updateLives () hvis (playerLives> = 0) then playerLives = playerLives - 1 gui_lives.text = "Bor:" ... playerLives gui_lives.x = screenW else timer.cancel (tmr_fireShip) timer.cancel (tmr_sendSlowEnemies) timer.cancel (tmr_sendSlowEnemies2 ) timer.cancel (tmr_sendFastEnemies) timer.cancel (tmr_sendBonus) Runtime: removeEventListener ("kollisjon", onCollision) Runtime: removeEventListener ("enterFrame", moveguy) Runtime: removeEventListener gameover_message = display.newText ("Spill over!", 0,0, "Kemco Pixel", 32) gameover_message.x = halfW gameover_message.y = halfY-15 gruppe: sett inn (gameover_message) funksjon returnToMenuTouch (event) hvis fase == "startet") da storyboard.gotoScene ("meny", "slideRight", "1000") slutten av spillet 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) gruppe: i sert (gameover_returntomenu) slutten
Vi er klare til vår endelige funksjon inne i vår scene: createScene () -funksjon! Denne funksjonen håndterer alle våre kollisjonssensorer ved å sammenligne eiendommen mitt navn
av objekt1 til det som holdes av objekt 2. Hvert objekt blir sendt som en parameter til denne funksjonen under variabelnavnet begivenhet
.
For å gjøre det enklere for deg, har jeg brutt ned de fem kollisjonssaker.
funksjon onCollision (event) hvis (event.object1.name == "bullet" og event.object2.name == "fiende") så show.remove (event.object2) playerScore = playerScore + 1 elseif (event.object1.name == "fiende" og event.object2.name == "bullet") så show.remove (event.object1) playerScore = playerScore + 1 elseif (event.object1.name == "skip" og event.object2.name = = "bonus") og deretter show.remove (event.object2) playerScore = playerScore + 5 elseif (event.object1.name == "fiende" og event.object2.name == "enemyHitBar") så show.remove (event. object1) updateLives () elseif (event.object1.name == "enemyHitBar" og event.object2.name == "fiende") så show.remove (event.object2) updateLives () end gui_score.text = "Score:" ... playerScore gui_score.x = screenW slutten
Siden vi har alt satt opp for vårt spill, må vi bare gjøre alt flytte! Inne i funksjonen scene: enterScene ()
- husk at enterScene
funksjonen er utenfor createScene
funksjon - vi vil lage 5 timere og en runtime lytter. Timere vil sende ut kulene, fiender og bonuser mens løpende lytter vil håndtere kollisjonsdeteksjonen.
funksjonsscene: enterScene (event) lokal gruppe = 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 ("kollisjon", onCollision)
Det endelige tillegget (jeg lover!) Er scene: destroyScene ()
funksjon og scene hendelse lyttere. Den ødelagte scenefunksjonen sørger for at fysikken fjernes når spilleren forlater scenen. Lyttene til sceneventyrene vil ringe til createScene
, enterScene
, og destroyScene
henholdsvis.
funksjon scene: destroyScene (event) lokal gruppe = self.view package.loaded [fysikk] = null fysikk = null slutt scene: addEventListener ("createScene" scene) scene: addEventListener ("enterScene" scene) scene: addEventListener destroyScene ", scene) returnere scene
Gratulerer! Du har lært om mange ting som Corona's storyboard-funksjon, fysikk, kollisjoner, og så mye mer! Disse er verdifulle ferdigheter som kan brukes til nesten hvilket som helst spill, og hvis du vil bygge dette spillet for enheten din, anbefaler jeg på det sterkeste de offisielle Corona-dokumentene for bygging av enheten.
Takk så mye for å lese! Hvis du har spørsmål, vennligst legg dem i kommentarene nedenfor.