I denne veiledningen hjelper jeg deg med å lage nivåer for enhver spillgenre og gjøre utformingen av nivåer mye enklere. Du skal lære å lage din første fliser kartmotor for bruk i noen av dine fremtidige prosjekter. Jeg bruker Haxe med OpenFL, men du bør kunne følge med på hvilket som helst språk.
Her er hva vi skal dekke:
Du kan også få en god start på en ny spillidee også!
Naturligvis har Wikipedia en grundig definisjon av hva et fliserbasert spill er, men for å få det grunnleggende kjernen er det bare noen få ting du trenger å vite:
For å bli enda mer grunnleggende, vil jeg si det slik:
Et flisbasert spill legger ut fliser for å skape hvert nivå.
I forbindelse med de vanlige fliser - rektangulære og isometriske - vil vi bruke rektangulære fliser i denne artikkelen for deres enkelhet. Hvis du bestemmer deg for å prøve ut isometriske nivåer en dag, er det mer matematikk involvert for å få det til å fungere. (Når du er ferdig her, sjekk ut denne flotte grunnen til å skape isometriske verdener.)
Det er noen ganske kule fordeler som du får fra å bruke en flisemotor. Den mest synlige fordel er at du ikke trenger å lage massive bilder for hånd for hvert enkelt nivå. Dette vil kutte ned på utviklingstiden og kutte ned på filstørrelser. Å ha 50 1280x768px bilder for et 50-nivå spill vs å ha ett bilde med 100 fliser gjør en stor forskjell.
En annen bivirkning er at å finne ting på kartet ved hjelp av kode blir litt lettere. I stedet for å kontrollere ting som kollisjon basert på en eksakt piksel, kan du bruke en rask og enkel formel for å finne ut hvilken flis du trenger for å få tilgang. (Jeg skal gå over det litt senere.)
Det første du trenger når du bygger flisemotoren er et sett med fliser. Du har to alternativer: Bruk andres fliser, eller lag dine egne.
Hvis du velger å bruke fliser som allerede er laget, kan du finne fritt tilgjengelig kunst over hele nettet. Ulempen med dette er at kunsten ikke ble laget spesielt for spillet ditt. På den annen side, hvis du bare prototyperer eller prøver å lære et nytt konsept, vil gratis fliser gjøre trikset.
Det er ganske mange ressurser der ute for fri og åpen kildekode. Her er noen få steder å starte søket ditt:
Disse tre koblingene skal gi deg mer enn nok plasser for å finne noen anseelige fliser for dine prototyper. Før du blir gal, ta tak i alt du finner, sørg for at du forstår hvilket lisens alt er dekket under, og hvilke restriksjoner de kommer med. Mange lisenser vil tillate deg å bruke kunsten fritt og for kommersiell bruk, men de kan kreve tilskrivning.
For denne opplæringen brukte jeg noen fliser fra en The Open Game Art Bundle for plattformspillere. Du kan laste ned min nedskalerte versjoner eller originalene.
Hvis du ikke har tatt sjansen til å lage kunst for spillene dine, kan det være litt skremmende. Heldigvis er det noen fantastiske og enkle stykker programvare som tar deg inn i det tykke av det, slik at du kan begynne å praktisere.
Mange utviklere begynner med pikselkunst for sine spill, og her er noen få gode verktøy for nettopp det:
Dette er noen av de mest populære programmene for å lage pikselkunst. Hvis du vil ha noe litt mer kraftig, er GIMP et utmerket alternativ. Du kan også gjøre noen vektorkunst med Inkscape og følge noen fantastiske opplæringsprogrammer på 2D Game Art For Programmers.
Når du tar tak i programvaren, kan du begynne å eksperimentere med å lage dine egne fliser. Siden denne opplæringen er ment å vise deg hvordan du oppretter flisekartet ditt motor Jeg vil ikke gå inn i for mye detalj om å lage flisene selv, men det er en ting å alltid huske på:
Pass på at flisene passer sømløst sammen, og legg til litt variasjon for å holde dem interessante.
Hvis flisene dine viser åpenbare linjer mellom dem når de settes sammen, vil spillet ikke se veldig fint ut. Pass på at du tar litt tid til å lage et fint "puslespill" av fliser ved å gjøre dem sømløse og legge til litt variasjon.
Nå har vi alle de slags ting ut av veien, vi kan dykke inn i koden for å sette nyoppkjøpte (eller opprettede) fliser på skjermen.
La oss starte med den helt grunnleggende oppgaven med å vise en enkelt flis på skjermen. Pass på at flisene dine er like store og lagret i separate bildefiler (vi snakker mer om spritark senere).
Når du har flisene i eiendomsmappen din på prosjektet, kan du skrive opp en veldig enkel Tile
klasse. Her er et eksempel i Haxe:
importer flash.display.Sprite; importer flash.display.Bitmap; import openfl.Assets; klassen Tile utvider Sprite private var image: Bitmap; offentlig funksjon ny () super (); image = new Bitmap (Assets.getBitmapData ("assets / grassLeftBlock.png")); addChild (image);
Siden alt vi gjør nå er å sette en enkelt flis på skjermen, er det eneste som klassen gjør er å importere fliser bildet fra eiendeler
mappe og legg det som et barn til objektet. Denne klassen vil trolig variere sterkt basert på programmeringsspråket du bruker, men du bør lett kunne finne en veiledning om hvordan du viser et bilde på skjermen.
Nå som vi har a Tile
klasse, må vi lage en forekomst av a Tile
og legg det til vår hovedklasse:
importer flash.display.Sprite; importere flash.events.Event; importere flash.Lib; Klassen Main utvider Sprite public function new () super (); var flis = ny flis (); addChild (flis); offentlige statiske funksjon hoved () Lib.current.addChild (new Main ());
De Hoved
klassen skaper en ny Tile
når konstruktøren (den ny()
funksjon, kalt når spillet starter) kalles, og legger det til displaylisten.
Når spillet kjøres, vil hoved()
funksjonen vil også bli kalt, og en ny Hoved
objekt blir lagt til scenen. Du burde nå ha flisene dine øverst til venstre på skjermen. Så langt så bra.
Tips: Hvis ingen av det er fornuftig - ikke bekymre deg! Det er bare standard boilerplate Haxe-kode. Den viktigste tingen å forstå er at dette skaper en Tile
objekt og legger den til skjermen.
Det neste trinnet er å finne en måte å fylle hele skjermen med fliser på. En av de enkleste måtene å gjøre dette på er å fylle opp en rekke med heltall som hver representerer en flis ID. Deretter kan du iterere gjennom matrisen for å bestemme hvilken flis som skal vises og hvor du skal vise den.
Du har muligheten til å bruke et typisk system eller bruke et 2-dimensjonalt array. Hvis du ikke er kjent med 2D-arrays, er det i utgangspunktet et enkelt utvalg som er fylt med flere arrays. De fleste språk vil betegne dette som nameOfArray [x] [y]
hvor x
og y
er indekser for tilgang til et enkelt element.
Siden x
og y
brukes også til skjermkoordinater, det kan være fornuftig å bruke x
og y
som koordinater for flisene våre. Trikset med 2D-arrays er å forstå hvordan heltallene er organisert. Du må kanskje reversere y
og x
når iterating gjennom arrays (eksempel nedenfor).
privat var eksempelArr = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]];
Legg merke til at i denne implementeringen er "0" -elementet i arrayet i seg selv et utvalg av fem heltall. Dette vil bety at du får tilgang til hvert element med y
først, deretter x
. Hvis du prøver å få tilgang til exampleArr [1] [0]
, du ville få tilgang til sjette flis.
Hvis du ikke forstår hvordan 2D-arrayene fungerer akkurat nå, ikke bekymre deg. For denne opplæringen vil jeg bruke et normalt utvalg for å holde ting enkelt og for å gjøre det enklere å visualisere:
privat var eksempelArr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0];
Eksemplet ovenfor viser hvordan et vanlig system kan være litt enklere. Vi kan visualisere nøyaktig hvor flisen skal være, og alt vi trenger å gjøre er å bruke en enkel formel (ikke bekymre deg, det kommer!) For å få flisen vi vil ha.
La oss nå skrive noen kode for å lage vårt utvalg og fylle det med dem. Nummeret vil være ID som representerer vår første flis.
Først må vi opprette en variabel inne i hovedklassen for å holde vårt utvalg:
privat var kart: Array;
Dette kan se litt rart ut, så jeg slår det ned for deg.
Det variable navnet er kart, og jeg har gitt det en veldig spesifikk type: Array
. De
delen forteller bare vårt program at arrayet bare vil inneholde heltall. Arrays kan holde omtrent hvilken som helst type du vil ha, så hvis du bruker noe annet for å identifisere fliser, kan du endre parameteren her.
Deretter må vi legge til noen koden til vår Hoved
klassens konstruktør (husk, dette er ny()
funksjon) slik at vi kan lage en forekomst av kartet vårt:
map = nytt Array();
Denne linjen vil opprette et tomt utvalg som vi snart kan fylle ut med våre for å teste det ut. La oss først definere noen verdier som vil hjelpe oss med vår matte:
offentlig statisk var TILE_WIDTH = 60; offentlig statisk var TILE_HEIGHT = 60; offentlig statisk var SCREEN_WIDTH = 600; offentlig statisk var SCREEN_HEIGHT = 360;
Jeg har laget disse verdiene offentlig statisk
fordi dette vil gi oss tilgang til dem fra hvor som helst i vårt program (via Main.Tile_WIDTH
og så videre). Du har kanskje også lagt merke til at det er delt SCREEN_WIDTH
av TILE_WIDTH
gir oss 10
og deling SCREEN_HEIGHT
av TILE_HEIGHT
gir oss 6
. Disse to tallene vil bli brukt til å bestemme hvor mange heltall som skal lagres i vårt utvalg.
var w = Std.int (SCREEN_WIDTH / TILE_WIDTH); var h = Std.int (SCREEN_HEIGHT / TILE_HEIGHT); for (jeg i 0 ... w * h) map [i] = 1
I denne blokk av kode tilordner vi 10
og 6
til w
og h
, som jeg nevnte ovenfor. Da må vi hoppe inn i en til
loop for å lage en matrise som passer 10 * 6
heltall. Dette vil stå for nok fliser for å fylle hele skjermen.
Nå har vi vårt grunnleggende kart bygget, men hvordan skal vi fortelle fliser å komme seg inn i deres rette sted? For det må vi gå tilbake til Tile
klasse og lag en funksjon som gjør at vi kan flytte fliser rundt etter ønske:
offentlig funksjon setLoc (x: Int, y: Int) image.x = x * Main.TILE_WIDTH; image.y = y * Main.TILE_HEIGHT;
Når vi kaller setLoc ()
funksjon, passerer vi i x
og y
koordinater i henhold til vår kartklasse (formel kommer snart, jeg lover!). Funksjonen tar disse verdiene og oversetter dem til pikselkoordinater ved å multiplisere dem med TILE_WIDTH
og TILE_HEIGHT
, henholdsvis.
Det eneste du har å gjøre for å få våre fliser på skjermen, er å fortelle vårt Hoved
klasse for å lage fliser og sette dem på plass basert på deres plasseringer i kartet. La oss gå tilbake til Hoved
og implementere det:
for (jeg i 0 ... map.length) var tile = new Tile (); var x = i% w; var y = Math.floor (i / w); tile.setLoc (x, y); addChild (flis);
Oh yeah! Det er riktig, vi har nå en skjerm full av fliser. La oss bryte ned hva som skjer over.
Formelen som jeg fortsetter å nevne er endelig her.
Vi beregner x
ved å ta modul (%
) av Jeg
og w
(som er 10, husk).
Modulen er bare resten etter heltall divisjon: \ (14 \ div 3 = 4 \ text rest \\\\\\\\\\\\\ modulus 3 = 2 \).
Vi bruker dette fordi vi vil ha vår verdi av x
å tilbakestille tilbake til 0
på begynnelsen av hver rad, så tegner vi respektive flis helt til venstre:
Som for y
, vi tar gulv()
av i / w
(det vil si, vi runder det resultatet ned) fordi vi bare vil y
å øke når vi har nådd slutten av hver rad og flyttet ned ett nivå:
Math.floor ()
når det deles heltall Haxe håndterer heltall divisjon annerledes. Hvis du bruker et språk som har heltall divisjon, kan du bare bruke i / w
(forutsatt at de begge er heltall). Til slutt ønsket jeg å nevne litt om rullingsnivåer. Vanligvis oppretter du ikke nivåer som passer perfekt i visningsporten din. Kartene dine vil trolig være mye større enn skjermen, og du vil ikke fortsette å tegne bilder som spilleren ikke vil kunne se. Med litt rask og enkel matte, bør du kunne beregne hvilke fliser som skal være på skjermen, og hvilke fliser som ikke skal tegnes.
For eksempel: Skjermstørrelsen din er 500x500, flisene dine er 100x100, og verdensstørrelsen er 1000x1000. Du trenger bare å gjøre en rask sjekk før du tegner fliser for å finne ut hvilke fliser som er på skjermen. Bruk plasseringen av visningsporten din - la oss si 400x500 - du trenger bare å tegne fliser fra radene 4 til 9 og kolonnene 5 til 10. Du kan få disse tallene ved å dele plasseringen av flisestørrelsen, og deretter kompensere disse verdiene med skjermen størrelse dividert med flisestørrelsen. Enkel.
Det ser kanskje ikke ut som mye siden alle fliser er de samme, men grunnlaget er nesten der. De eneste tingene du må gjøre er å skape forskjellige typer fliser og designe vårt kart slik at de retter seg og skaper noe fint.
Vi har nå et kart som er fullt av de som dekker skjermen. Ved dette punktet bør du ha mer enn en flisertype, noe som betyr at vi må endre vår Tile
konstruktør for å redegjøre for det:
offentlig funksjon ny (id: Int) super (); bytte (id) tilfelle 1: image = new Bitmap (Assets.getBitmapData ("assets / grassLeftBlock.png")); tilfelle 2: image = ny Bitmap (Assets.getBitmapData ("assets / grassCenterBlock.png")); tilfelle 3: image = ny Bitmap (Assets.getBitmapData ("assets / grassRightBlock.png")); tilfelle 4: image = ny Bitmap (Assets.getBitmapData ("assets / goldBlock.png")); sak 5: image = ny bitmap (Assets.getBitmapData ("assets / globe.png")); tilfelle 6: image = new Bitmap (Assets.getBitmapData ("assets / mushroom.png")); addChild (bilde);
Siden jeg brukte seks forskjellige fliser til kartet, trengte jeg en bytte om
uttalelse som dekker tallene ett til seks. Du vil legge merke til at konstruktøren nå tar et heltall som en parameter slik at vi vet hva slags fliser å lage.
Nå må vi gå tilbake til vår Hoved
konstruktør og fikse flisene i vår til
løkke:
for (jeg i 0 ... map.length) var tile = new Tile (map [i]); var x = i% w; var y = Math.floor (i / w); tile.setLoc (x, y); addChild (flis);
Alt vi måtte gjøre var å passere kart [i]
til Tile
konstruktør for å få det til å fungere igjen. Hvis du prøver å løpe uten å sende et heltall til Tile
, det vil gi deg noen feil.
Nesten alt er på plass, men nå trenger vi en måte å designe kart i stedet for bare å fylle dem med tilfeldige fliser. Fjern først til
sløyfe i Hoved
konstruktør som setter hvert element til ett. Da kan vi lage vårt eget kart for hånd:
map = [0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, 6 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3];
Hvis du formaterer matrisen som jeg har over, kan du lett se hvordan flisene våre blir organisert. Du kan også bare angi arrayet som en lang linje med tall, men førstnevnte er fint fordi du kan se 10 over og 6 ned.
Når du prøver å kjøre programmet nå, bør du få noen nullpeker unntak. Problemet er at vi bruker nuller i vårt kart og vår Tile
klassen vet ikke hva jeg skal gjøre med null. Først vil vi fikse konstruktøren ved å legge til i en sjekk før vi legger til bildebarnet:
hvis (image! = null) addChild (bilde);
Denne hurtige sjekken sørger for at vi ikke legger noen nullbarn til Tile
objekter vi lager. Den siste endringen for denne klassen er setLoc ()
funksjon. Akkurat nå prøver vi å sette x
og y
verdier for en variabel som ikke er initialisert.
La oss legge til en annen rask sjekk:
offentlig funksjon setLoc (x: Int, y: Int) if (image! = null) image.x = x * Main.TILE_WIDTH; image.y = y * Main.TILE_HEIGHT;
Med disse to enkle løsningene på plass, og flisene som jeg oppgav ovenfor, bør du kunne kjøre spillet og se et enkelt plattformsnivå. Siden vi forlot 0 som en "ikke-fliser", kan vi la den være gjennomsiktig (tom). Hvis du vil spice opp nivået, kan du sette en bakgrunn inn før du legger ut flisene; Jeg har nettopp lagt til en lyseblå gradient som ser ut som en himmel i bakgrunnen.
Det er stort sett alt du trenger for å sette opp en enkel måte å redigere nivåene på. Prøv å eksperimentere litt med matrisen og se hvilke design du kan komme opp på for andre nivåer.
Når du har det grunnleggende nede, kan du vurdere å bruke noen andre verktøy for å hjelpe deg med å designe nivåer raskt og se hvordan de ser ut før du kaster dem inn i spillet. Ett alternativ er å lage din egen nivåredigerer. Alternativet er å få tak i noen programvare som er tilgjengelig for fri bruk. De to populære verktøyene er Tiled Map Editor og Ogmo Editor. De gjør begge nivåredigering mye enklere med flere eksportalternativer.
Relaterte innleggNå vet du hva et flisebasert spill er, hvordan får du noen fliser som skal brukes til nivåene dine, og hvordan du får hendene skitne og skriver koden til motoren din, og du kan til og med gjøre noen grunnleggende redigeringsoppgaver med en enkel rekkefølge . Bare husk at dette bare er begynnelsen; Det er mange eksperimenter du kan gjøre for å gjøre en enda bedre motor.
Også, jeg har gitt kildefilene for deg å sjekke ut. Slik ser den endelige SWF ut:
Last ned SWF her.... og her, igjen, er arrayet det genereres av:
map = [0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0, 6 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3];
Din neste oppgave er å gjøre litt forskning og prøve noen nye ting for å forbedre det du gjorde her. Sprite-ark er en fin måte å forbedre ytelsen på og gjøre livet lettere. Trikset er å få noen solid kode lagt ut for å lese fra et spritark slik at spillet ditt ikke trenger å lese hvert bilde individuelt.
Du bør også begynne å øve dine kunstferdigheter ved å lage noen få fliser til deg selv. På den måten kan du få den rette kunsten til fremtidige spill.
Som alltid, la noen kommentarer nedenfor om hvordan din motor jobber for deg - og kanskje til og med noen demoer slik at vi kan prøve ut ditt neste flisespill.