Lag en Plane slåssespill i Corona: Ferdigstilling Gameplay

Create a Plane slåssespill i Corona: Ferdigstilling Gameplay
36
Del
6
Del

Dette Cyber ​​mandag Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av
Dette innlegget er en del av en serie som heter Opprett en Plane slåssespill i Corona.Create a Plane slåssespill i Corona. Mer GameplayWhat du skal lage
Innledning

I den fjerde og siste del av denne serien, vi fortsetter der vi slapp av i forrige tutorial. Vi vil opprette fiendtlige fly spilleren trenger for å unngå eller skyte, og vi vil også lage et game over skjermen.

1. generateEnemys

generateEnemys funksjonen genererer et tall mellom tre Anmeldelser og sju
, og kaller generateEnemyPlane funksjon hver to sekunder
for imidlertid mange ganger numberOfEnemysToGenerate er lik. Skriv inn følgende kode i gamelevel.lua
.
Funksjons generateEnemys () numberOfEnemysToGenerate = Math.random (3,7) timer.performWithDelay (2000, generateEnemyPlane, numberOfEnemysToGenerate) end

Vi trenger også å påkalle denne funksjonen i enterScene metoden som vist nedenfor
funksjon scene. enterScene (hendelse) --SNIP-- Spilletid: addEventListener ("enterframe", gameLoop) startTimers () generateEnemys () end

La oss se hva gjennomføringen av generateEnemyPlane ser ut.

2. generateEnemyPlane

generateEnemyPlane funksjonen genererer én fiendens fly. Det er tre typer av fiendtlige fly i dette spillet.

  • Regular
    , flytter nedover skjermen i en rett linje

    Waver
    , beveger seg i et bølgemønster på x-aksen

    Chaser
    , jager spillerens flyet
    funksjon generateEnemyPlane () if (Gameover ~ = sant) da lokale randomGridSpace = matematikk. tilfeldig (11) lokal randomEnemyNumber = Math.random (3) lokale tempEnemy if (planeGrid [randomGridSpace] ~ = 0) så generateEnemyPlane () returnerer else if (randomEnemyNumber == 1) deretter tempEnemy = display.newImage ("enemy1.png" (randomGridSpace * 65) -28, -60) tempEnemy.type = "vanlige" ElseIf (randomEnemyNumber == 2) deretter tempEnemy = display.newImage ("enemy2.png", display.contentWidth /2 -playerWidth /2, - 60) tempEnemy.type = "vakle" annet tempEnemy = display.newImage ("enemy3.png", (randomGridSpace * 65) -28, -60) tempEnemy.type = "chaser" end planeGrid [randomGridSpace] = 1 table.insert (enemyPlanes, tempEnemy) planeGroup: sett (tempEnemy) numberOfEnemysGenerated = numberOfEnemysGenerated + 1; end if (numberOfEnemysGenerated == numberOfEnemysToGenerate) så numberOfEnemysGenerated = 0; resetPlaneGrid () timer.performWithDelay (2000, generateEnemys, 1) endendend

    Vi først sjekke at spillet er ikke over ennå. Vi deretter generere en randomGridSpace, et tall mellom 1 Hotell og 11
    , og en tilfeldig randomEnemyNumber, et tall mellom 1 Hotell og 3
    . Den randomGridSpace brukes til å plassere flyet i en av elleve spalter på toppen av skjermen på x-aksen. Hvis du tenker på spillområdet blir delt inn i elleve seksjoner, da vi bare ønsker å plassere nye fly i et spor som ikke har blitt tatt ennå av en annen planet. Den planeGrid Tabellen inneholder elleve 0-er og når vi plassere en ny planet i et av sporene, setter vi tilsvarende posisjon i tabellen til en for å vise at sporet har blitt tatt av et fly.

    sjekke om indeksen for randomGridSpace i tabellen er ikke lik 0. Hvis det ikke er, vi vet at sporet er i dag tatt og vi bør ikke fortsette, så vi kaller generateEnemyPlane og returnere fra funksjonen.

    Deretter sjekker vi hva randomEnemyNumber er lik og sette tempEnemy til en av de tre fiendebilder, vi også gi den en egenskap av enten vanlig, vakle, eller jeger. Fordi Lua er et dynamisk språk, kan vi legge nye egenskaper til et objekt under kjøring. Vi satt hva indeksen er lik randomGridSpace til 1 i planeGrid tabellen.

    Vi setter tempEnemy inn i enemyPlanes bord for senere referanse og øke numberOfEnemysGenerated. Hvis numberOfEnemysGenerated er lik numberOfEnemysToGenerate, vi null numberOfEnemysGenerated til 0, påberope resetPlaneGrid, og sette en timer som vil kalle generateEnemys igjen etter to sekunder. Denne prosessen gjentas så lenge spillet er ikke over.

    3. moveEnemyPlanes

    Som du kanskje har gjettet, er moveEnemyPlanes funksjon ansvarlig for flytting av fiendtlige fly. Avhengig av flyets typen, er den aktuelle funksjonen kalles
    funksjons moveEnemyPlanes () if (#enemyPlanes > 0). Deretter for i = 1, #enemyPlanes gjøre hvis (enemyPlanes [i] .type == "vanlig") deretter moveRegularPlane (enemyPlanes [i]) ElseIf (enemyPlanes [i] .type == "vakle") så moveWaverPlane (enemyPlanes [i]) ellers moveChaserPlane (enemyPlanes [i]) end end Sluttslutt

    Denne funksjonen må påberopes i gameLoop funksjonen.
    funksjon gameLoop () --SNIP-- checkFreeLifesOutOfBounds () checkPlayerCollidesWithFreeLife () moveEnemyPlanes () end
    4. moveRegularPlane

    moveRegularPlane flytter bare flyet nedover skjermen over y-aksen.
    funksjon moveRegularPlane (fly) plane.y = plane.y + 4end
    5. moveWaverPlane

    moveWaverPlane funksjon flytter flyet nedover skjermen over y-aksen, og i en bølge mønster, over x-aksen. Dette oppnås ved hjelp av cos funksjon av Lua matematikk bibliotek.

    Hvis dette konseptet er fremmed for deg, skrev Michael James Williams en flott introduksjon til Sinus Motion. De samme begrepene gjelder, den eneste forskjellen er at vi bruker cosinus
    . Du bør tenke sinus
    når du arbeider med y-aksen og cosinus
    når du arbeider med x-aksen.
    Funksjon moveWaverPlane (fly) plane.y = plane.y + 4 plane.x = (display.contentWidth /2) + 250 * math.cos (numberOfTicks * 0,5 * math.pi /30) end

    I ovennevnte tekstutdrag, bruker vi numberOfTicks variabel. Vi trenger å øke dette hver gang gameLoop funksjonen kalles.
    Funksjon gameLoop legge følgende som den aller første linjen i gameLoop funksjon. () NumberOfTicks = numberOfTicks + 1end
    6. moveChaserPlane

    moveChaserPlane funksjonen har flyet jage
    spilleren. Den beveger seg nedover langs y-aksen med en konstant hastighet, og det beveger seg mot spillerens posisjon på x-aksen. Ta en titt på gjennomføringen av moveChaserPlane for avklaring
    funksjon moveChaserPlane (fly) hvis. (Plane.x < player.x) så plane.x = plane.x 4 end if (plane.x > spiller. x) så plane.x = plane.x - 4 slutten plane.y = plane.y + 4end

    Hvis du teste spillet nå, bør du se flyene beveger seg nedover skjermen

    7.. fireEnemyBullets

    Hver så ofte, vil vi fiendtlige fly å skyte en kule. Vi ønsker ikke alle av dem å skyte på samme tid, men så velger vi bare et par fly til brann
    funksjons fireEnemyBullets () if (#enemyPlanes > = 2). Deretter lokal numberOfEnemyPlanesToFire = math.floor (# enemyPlanes /2) lokale tempEnemyPlanes = table.copy (enemyPlanes) lokal funksjon fireBullet () lokale randIndex = Math.random (#tempEnemyPlanes) lokal tempBullet = display.newImage ("bullet.png", (tempEnemyPlanes [randIndex] .x + playerWidth /2) + bulletWidth, tempEnemyPlanes [randIndex] .Y + playerHeight + bulletHeight) tempBullet.rotation = 180 planeGroup: Stikk (tempBullet) table.insert (enemyBullets, tempBullet); table.remove (tempEnemyPlanes, randIndex) slutten for i = 0, numberOfEnemyPlanesToFire gjøre fireBullet () end Sluttslutt

    Vi først sjekke at den enemyPlanes tabellen har mer enn to plan i det. Hvis den gjør det, får vi numberOfEnemyPlanes å skyte ved å ta lengden på enemyPlanes bordet, dele det med to, og runde det ned. Vi lager også en kopi av enemyPlanes bordet, slik at vi kan manipulere det separat.

    fireBullet funksjonen velger et fly fra tempEnemyPlanes bordet og gjør flyet brann en kule. Vi genererer et tilfeldig tall på grunnlag av lengden av tempEnemyPlanes bordet, skape en kule bilde, og plassere den ved hjelp av hvilken planet er på randIndex i tempEnemyPlanes tabellen. Vi fjerner da at flyet fra den midlertidige tabellen for å sikre at det ikke vil bli valgt på nytt neste gang fireBullet kalles.

    Vi gjentar denne prosessen imidlertid mange ganger numerOfEnemyPlanesToFire er lik og ringe fireBullet funksjonen.

    Vi må starte tidtakeren som kaller denne funksjonen hver så ofte. For å oppnå dette, legger følgende til startTimers funksjonen.
    Funksjons startTimers () firePlayerBulletTimer = timer.performWithDelay (2000, firePlayerBullet, -1) generateIslandTimer = timer.performWithDelay (5000, generateIsland, -1) generateFreeLifeTimer = timer.performWithDelay (7000, generateFreeLife, - 1) fireEnemyBulletsTimer = timer.performWithDelay (2000, fireEnemyBullets, -1) end
    8. moveEnemyBullets

    Vi trenger også å flytte fiendens kuler som står på skjermen. Dette er ganske enkelt ved hjelp av følgende kode
    funksjons moveEnemyBullets () if (#enemyBullets > 0). Deretter for i = 1, # enemyBullets gjøre enemyBullets [i]. y = enemyBullets [i] .Y + 7 slutten Sluttslutt

    Påkall denne funksjonen i gameLoop funksjonen.
    funksjon gameLoop () --SNIP-- checkPlayerCollidesWithFreeLife () moveEnemyPlanes () moveEnemyBullets () end
    9. checkEnemyBulletsOutOfBounds

    I tillegg til å flytte fiendens kuler, må vi sjekke når fiendens kuler har gått av skjermen, og fjerne dem når de gjør. Gjennomføringen av checkEnemyBulletsOutOfBounds skulle være kjent nå
    funksjons checkEnemyBulletsOutOfBounds () if (#enemyBullets > 0). Deretter for i = # enemyBullets, 1, -1 gjøre hvis (enemyBullets [i] .Y > display.contentHeight) så enemyBullets [i]. removeSelf () enemyBullets [i] = null table.remove (enemyBullets, i) end end Sluttslutt

    Påkall denne funksjonen i gameLoop funksjon
    funksjon gameLoop () - SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () end
    10. checkEnemyPlanesOutOfBounds

    Vi bør også sjekke om de fiendtlige fly har flyttet off-screen
    funksjons checkEnemyPlanesOutOfBounds () hvis (# enemyPlanes > 0). deretter for i = # enemyPlanes, 1, -1 gjøre hvis (enemyPlanes [i] .Y > display.contentHeight) så enemyPlanes [i]: removeSelf () enemyPlanes [i] = null table.remove (enemyPlanes, i) end end Sluttslutt

    Påkall denne funksjonen i gameLoop funksjon
    funksjon gameLoop () --SNIP-- moveEnemyBullets () checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () end
    11. checkPlayerBulletsCollideWithEnemyPlanes

    checkPlayerBulletCollidesWithEnemyPlanes funksjonen bruker hasCollided funksjon for å sjekke om noen av spillerens kuler har kollidert med noen av de fiendtlige fly
    funksjons checkPlayerBulletsCollideWithEnemyPlanes () if (#playerBullets >. 0 og #enemyPlanes > 0), så for i = # playerBullets, 1, -1 gjøre for j = # enemyPlanes, 1, -1 gjøre hvis (hasCollided (playerBullets [i], enemyPlanes [J])) så playerBullets [i]: removeSelf () playerBullets [i] = null table.remove (playerBullets, i) generateExplosion (enemyPlanes [j] .x, enemyPlanes [j] .Y) enemyPlanes [j]: removeSelf () enemyPlanes [j] = null table.remove (enemyPlanes, j ) lokal eksplosjon = audio.loadStream ("explosion.mp3") lokale backgroundMusicChannel = audio.play (eksplosjon, {fadein = 1 000}) end end end Sluttslutt

    Denne funksjonen bruker to nestet for løkker for å sjekke om gjenstandene har kollidert . For hver av de playerBullets, kjører vi gjennom alle flyene i enemyPlanes bordet og ringe hasCollided funksjonen. Hvis det er en kollisjon, fjerner vi kula og planet, ring generateExplosion funksjon, og laste inn og spille en eksplosjon lyd

    Påkall denne funksjonen i gameLoop funksjon
    funksjon gameLoop ().. - -SNIP-- checkEnemyBulletsOutOfBounds () checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () end
    12. generateExplosion

    generateExplosion funksjonen bruker Corona SpriteObject klasse. Sprites tillate animerte sekvenser av rammer som ligger på bildet eller Sprite blad. Ved å gruppere bilder til ett enkelt bilde, kan du trekke visse rammer fra det bildet og lage en animasjonssekvens.
    Funksjon generateExplosion (xPosition, yPosition) lokale alternativer = {width = 60, height = 49, numFrames = 6} lokal explosionSheet = graphics.newImageSheet ("explosion.png", opsjoner) lokal sequenceData = {{name = "eksplosjon", start = 1, teller = 6, tid = 400, loopCount = 1}} lokale explosionSprite = display.newSprite (explosionSheet, sequenceData) explosionSprite.x = xPosition explosionSprite.y = yPosition explosionSprite: addEventListener ("sprite", explosionListener) explosionSprite: play () end

    newImageSheet metoden tar som parametre banen til bildet og en tabell med alternativer for Sprite Ark. Alternativene vi stiller er bredden, høyden, og de numFrames, hvor mange enkeltbilder utgjør dette arket. Det er seks separate eksplosjon bilder som vises i bildet nedenfor.

    Deretter setter vi opp en tabell, sequenceData, som er nødvendig av SpriteObject. Vi setter starten eiendom til en, telle til seks, og tid til 400. Starten Eiendommen er rammen som animasjonen vil starte på, er den teller hvor mange rammer animasjonen inneholder, og tiden eiendommen er hvor lenge animasjonen tar å spille gjennom.

    Vi deretter opprette SpriteObject bestått i explosionSheet og sequenceData, angi x- og y-posisjoner, og legge en lytter til sprite. Lytteren skal brukes til å fjerne sprite når den er ferdig sin animasjon.

    13. explosionListener

    Er explosionListener funksjonen brukes til å fjerne sprite. Hvis hendelsens fase eiendom er lik ended, så vet vi spriten er ferdig med sin animasjon og vi kan fjerne det.
    Funksjon explosionListener (hendelse) if (event.phase == "endte") da lokale eksplosjon = hendelse. Målet eksplosjon: removeSelf () eksplosjon = nil Sluttslutt
    14. checkEnemyBulletsCollideWithPlayer

    checkEnemyBulletsCollideWithPlayer sjekker for å se om noen av fiendens kuler har kollidert med spillerens flyet
    funksjon checkEnemyBulletsCollideWithPlayer () hvis. (#enemyBullets > 0) så for i = # enemyBullets, 1, -1 gjøre hvis (hasCollided (enemyBullets [i], spiller)) så enemyBullets [i]: removeSelf () enemyBullets [i] = null table.remove (enemyBullets, i) if (playerIsInvincible == false) da killPlayer () end end end Sluttslutt

    Vi sløyfe gjennom enemyBullets bordet og sjekke om noen av dem har kollidert med spilleren. Hvis sant, fjerner vi det bestemt punkt, og hvis playerIsInvincible er falsk, påberope vi killPlayer.

    Påkall denne funksjonen i gameLoop funksjonen.
    Funksjon gameLoop () --SNIP-- checkEnemyPlanesOutOfBounds () checkPlayerBulletsCollideWithEnemyPlanes () checkEnemyBulletsCollideWithPlayer () end
    15. killPlayer

    killPlayer funksjonen er ansvarlig for å sjekke hvorvidt spillet er over og gyting en ny spiller dersom det ikke er
    funksjon killPlayer () numberOfLives = numberOfLives- en.; if (numberOfLives == 0) deretter GAME = true doGameOver () else spawnNewPlayer () hideLives () showLives () playerIsInvincible = true Sluttslutt

    første redusere trinnvis numberOfLives etter en, og, hvis det er lik 0, kaller vi det Gameover funksjon. Det spilleren har liv igjen, vi kaller spawnNewPlayer, etterfulgt av hideLives, showLives, og satt playerIsInvincible til sann.

    16. doGameOver

    doGameOver funksjonen forteller storyboard for å gå til game
    scene.
    funksjon doGameOver () storyboard.gotoScene ("gameover") end
    17. spawnNewPlayer

    spawnNewPlayer funksjonen er ansvarlig for gyting en ny spiller etter at den er død. Spillerens flyet blinker i noen sekunder for å vise at det er midlertidig uovervinnelig
    funksjon spawnNewPlayer () lokal numberOfTimesToFadePlayer = 5 lokale numberOfTimesPlayerHasFaded = 0 lokal funksjon fadePlayer () player.alpha = 0.; transition.to (spiller, {tid = 200, alfa = 1}) numberOfTimesPlayerHasFaded = numberOfTimesPlayerHasFaded + 1 if (numberOfTimesPlayerHasFaded == numberOfTimesToFadePlayer) så playerIsInvincible = false end end timer.performWithDelay (400, fadePlayer, numberOfTimesToFadePlayer) end

    For å gjøre spillerens flyet blink, vi visne den inn og ut fem ganger. I fadePlayer funksjonen, setter vi flyets alpha eiendom til 0, noe som gjør det gjennomsiktig. Vi bruker overgangen biblioteket å falme alfa tilbake til en over en periode på 200 millisekunder. Den til metoden for overgangen objektet tar en tabell av alternativer. I vårt eksempel alternativ tabellen inkluderer en tid i millisekunder og eiendommen vi ønsker å animere, alfa, og ønsket verdi, 1.

    øke numberOfTimesThePlayerHasFaded og sjekk om det er lik antall ganger vi ønsket spilleren å falme. Vi satt playerIsInvincible til false. Vi bruker en timer for å ringe fadePlayer funksjonen men mange ganger numberOfTimerToFadePlayer er lik.

    Det er en måte å gjøre alt dette uten å bruke timer og det er ved hjelp av overgangen er gjentakelser eiendom i kombinasjon med sin onComplete handler . Les gjennom dokumentasjonen for å lære mer om dette alternativ tilnærming.

    18. checkEnemyPlaneCollidesWithPlayer

    Det er ett mer kollisjon sjekk vi bør gjøre og det er å se om en fiende fly kolliderer med spillerens flyet
    funksjon checkEnemyPlaneCollideWithPlayer () if (#enemyPlanes > 0). deretter for i = # enemyPlanes, 1, -1 gjøre hvis (hasCollided (enemyPlanes [i], spiller)) så enemyPlanes [i]: removeSelf () enemyPlanes [i] = null table.remove (enemyPlanes, i) if (playerIsInvincible == false) deretter killPlayer () end end end Sluttslutt

    Vi sløyfe gjennom fiendens fly og se om noen av dem kolliderer med spillerens flyet. Hvis det stemmer, fjerner vi at fiendens fly og ringe killPlayer. Hvis du tror det gjør spillet mer interessant, kan du også generere en eksplosjon her.

    19. exitScene

    Når spillet er over, vi overgangen til game
    scene. Husker fra tidligere i opplæringen, er exitScene funksjon der du fjerne eventuelle hendelsen lyttere, stopp timere, og stoppe lyd som spilles
    funksjon scene. ExitScene (hendelse) lokal gruppe = self.view rectUp: removeEventListener ("touch" , movePlane) rectDown: removeEventListener ("touch", movePlane) rectLeft: removeEventListener ("touch", movePlane) rectRight: removeEventListener ("touch", movePlane) audio.stop (planeSoundChannel) audio.dispose (planeSoundChannel) Spilletid: removeEventListener (" enterframe ", gameLoop) cancelTimers () endscene: addEventListener (" exitScene ", scene)

    Vi er i utgangspunktet å angre hva vi gjorde i enterScene funksjonen. Vi kaller kast-metoden på audio-objektet for å frigi minnet forbundet med audiokanalen. Ringe stop alene fritar ikke minnet.

    20. cancelTimers

    Som navnet indikerer, den cancelTimers funksjonen ikke det motsatte av startTimers, oppheves alle tidtakerne.
    funksjons cancelTimers () timer.cancel (firePlayerBulletTimer) timer.cancel (generateIslandTimer) timer.cancel ( fireEnemyBulletsTimer) timer.cancel (generateFreeLifeTimer) end
    21. Game Over Scene

    Det er på tide å lage den game
    scene. Begynn med å legge en ny Lua fil til prosjektet navnet gameover.lua
    , og legge til følgende kode i den.
    Lokale dreiebok = require ("storyboard") lokale scenen = storyboard.newScene () lokal gameOverTextlocal newGameButtonreturn scene
    22. createScene

    Legg til følgende i gameover.lua
    ovenfor retur scene. Herfra ut, bør alle koden plasseres over avkastningen scene uttalelse
    funksjon scene. CreateScene (hendelse) lokal gruppe = self.view lokal bakgrunn = display.newRect (0, 0, display.contentWidth, display.contentHeight ) bakgrunn: setFillColor (0, 0,39, 0,75) gruppe: sett (bakgrunn) gameOverText = display.newText ("Game Over", display.contentWidth /2400, native.systemFont, 16) gameOverText: setFillColor (1, 1, 0) gameOverText.anchorX = 0,5 gameOverText.anchorY = 0,5 gruppen: sett (gameOverText) newGameButton = display.newImage ("newgamebutton.png", 264670) gruppe: sett (newGameButton) newGameButton.isVisible = false slutten

    Som vi gjorde i de to foregående scener, vi gir game
    scene en blå bakgrunn. Vi deretter opprette en TextObject eksempel ved å ringe newText utstilt. Den newText metoden tar noen alternativer, teksten for objektet, sin posisjon, og skriften å bruke. Vi gir det en gul farge ved å påberope setFillColor, passerer i RGB-verdier som prosent. Til slutt lager vi en knapp og skjule det for tiden.

    23. enterScene

    Når dreieboken har fullt overført til game
    scene, er enterScene metode som kalles.

    I enterScene, fjerner vi den forrige scene fra dreieboken. Vi bruker det praktiske metoden scaleTo fra Transition Library å skalere gameOverText med en faktor på 4. vi legge til en onComplete lytteren til overgangen som kaller
    showButton funksjon når overgangen er fullført. Til slutt legger vi en kran hendelse lytteren til spillet knappen som påkaller startNewGame funksjon
    funksjon scene. EnterScene (hendelse) lokal gruppe = self.view storyboard.removeScene ("gamelevel") transition.scaleTo (gameOverText {XScale = 4.0, ySkala = 4.0, tid = 2000, onComplete = showButton}) newGameButton: addEventListener ("tap", startNewGame) end
    24. showButton

    showButton funksjonen skjuler gameOverText og viser newGameButton.
    funksjon showButton () gameOverText.isVisible = false newGameButton.isVisible = true end
    25. startNewGame

    startNewGame funksjonen forteller storyboardet til overgangen til gamelevel
    scene.
    funksjon startNewGame () storyboard.gotoScene ("gamelevel") end
    26. exitScene

    Vi må gjøre noen opprydding når vi forlater game
    scene. Vi fjerner springen arrangementet lytteren vi lagt tidligere til newGameButton
    funksjon scene. ExitScene (hendelse) lokal gruppe = self.view newGameButton: removeEventListener ("tap", startNewGame) end
    27. Legg Scene Lyttere

    Den siste brikken i puslespillet er å legge til scene hendelsen lyttere vi snakket om tidligere. For å gjøre dette, addd følgende kodebiten til gameover.lua
    scene. AddEventListener ("createScene", scene) scenen: addEventListener ("enterScene", scene) scenen: addEventListener ("exitScene" , scene)
    Konklusjon

    Vi har kommet til slutten av denne serien, og nå har en fullt funksjonell plan slåssespill. Jeg håper du har funnet disse veiledningene nyttig og har lært noe underveis. Takk for lesing.