In questo tutorial ti aiuterò a creare livelli per qualsiasi genere di gioco e renderà i livelli di progettazione molto più semplici. Imparerai come creare il tuo primo motore di mappe per piastrelle da utilizzare in uno qualsiasi dei tuoi progetti futuri. Userò Haxe con OpenFL, ma dovresti essere in grado di seguire in qualsiasi lingua.
Ecco cosa tratteremo:
Potresti iniziare bene anche con una nuova idea di gioco!
Naturalmente, Wikipedia ha una definizione approfondita di cosa sia un gioco basato su tile, ma per ottenere l'essenziale sono solo alcune cose che devi sapere:
Per essere ancora più semplice, lo metto in questo modo:
Un gioco basato su tessere espone tessere per creare ogni livello.
In riferimento ai tipi di tile comuni, rettangolari e isometrici, utilizzeremo le tessere rettangolari in questo articolo per la loro semplicità. Se decidi di provare i livelli isometrici un giorno, c'è bisogno di ulteriore matematica per farlo funzionare. (Una volta che hai finito qui, dai un'occhiata a questo ottimo primer sulla creazione di mondi isometrici).
Ci sono alcuni vantaggi piuttosto interessanti che si ottengono dall'utilizzo di un motore di piastrelle. Il vantaggio più evidente è che non avrai bisogno di creare immagini massicce a mano per ogni livello individuale. Ciò ridurrà i tempi di sviluppo e ridurrà le dimensioni dei file. Con 50 immagini 1280x768px per un gioco a 50 livelli e una con 100 tessere, fa una grande differenza.
Un altro effetto collaterale è che localizzare le cose sulla mappa usando il codice diventa un po 'più facile. Invece di controllare elementi come la collisione basata su un pixel esatto, puoi usare una formula semplice e veloce per determinare quale tessera devi accedere. (Ne parlerò un po 'più tardi).
La prima cosa di cui avrai bisogno quando costruisci il tuo motore di piastrelle è un insieme di tessere. Hai due opzioni: usa le tessere di qualcun altro o creane di tue.
Se decidi di usare le tessere che sono già state realizzate, puoi trovare arte liberamente disponibile in tutto il web. Il lato negativo di questo è che l'arte non è stata creata appositamente per il tuo gioco. D'altra parte, se si sta solo prototipando o cercando di imparare un nuovo concetto, le tessere libere faranno il trucco.
Ci sono un bel po 'di risorse là fuori per l'arte libera e open source. Ecco alcuni punti in cui iniziare la ricerca:
Questi tre collegamenti dovrebbero darti più di abbastanza posti per trovare alcune tessere decenti per i tuoi prototipi. Prima di impazzire, afferrando tutto ciò che trovi, assicurati di capire quale licenza è coperta e quali sono le restrizioni. Molte licenze ti permetteranno di usare l'arte liberamente e per uso commerciale, ma potrebbero richiedere l'attribuzione.
Per questo tutorial, ho usato alcune tessere da un pacchetto The Open Game Art per i platform. È possibile scaricare le versioni ridotte o gli originali.
Se non hai ancora deciso di fare arte per i tuoi giochi, potrebbe essere un po 'intimidatorio. Fortunatamente ci sono alcuni incredibili e semplici software che ti fanno entrare nel vivo in modo che tu possa iniziare a esercitarti.
Molti sviluppatori iniziano con pixel art per i loro giochi e qui ci sono alcuni grandi strumenti per questo:
Questi sono alcuni dei programmi più popolari per creare pixel art. Se vuoi qualcosa di un po 'più potente, GIMP è un'opzione eccellente. Puoi anche fare un po 'di grafica vettoriale con Inkscape e seguire alcuni fantastici tutorial su 2D Game Art For Programmers.
Una volta afferrato il software, puoi iniziare a sperimentare con le tue tessere. Poiché questo tutorial ha lo scopo di mostrarti come creare la tua mappa delle tessere motore Non parlerò troppo dettagliatamente di come creare le tessere, ma c'è sempre una cosa da tenere a mente:
Assicurati che le tue tessere combacino perfettamente e aggiungi delle varianti per renderle interessanti.
Se le tue tessere mostrano linee evidenti tra loro una volta messe insieme, il tuo gioco non sarà molto bello. Assicurati di dedicare un po 'di tempo alla creazione di un bel "puzzle" di tessere rendendole semplici e aggiungendo qualche variazione.
Ora abbiamo tolto tutto questo materiale artistico, possiamo immergerci nel codice per mettere sullo schermo le tessere appena acquisite (o create).
Iniziamo con il compito fondamentale di visualizzare una singola tessera sullo schermo. Assicurati che le tue tessere siano tutte della stessa dimensione e salvate in file di immagine separati (parleremo più a proposito dei fogli di sprite in seguito).
Una volta che hai le tessere nella tua cartella dei beni del tuo progetto, puoi scrivere in modo molto semplice Piastrella
classe. Ecco un esempio in Haxe:
import flash.display.Sprite; import flash.display.Bitmap; importazione openfl.Assets; class Tile estende Sprite immagine var privata: Bitmap; public function new () super (); image = new Bitmap (Assets.getBitmapData ("assets / grassLeftBlock.png")); addChild (immagine);
Poiché tutto ciò che stiamo facendo ora è mettere una singola tessera sullo schermo, l'unica cosa che fa la classe è importare l'immagine della tessera dal risorse
cartella e aggiungerlo come un bambino all'oggetto. Questa classe probabilmente varierà molto in base al linguaggio di programmazione che usi, ma dovresti essere in grado di trovare facilmente una guida su come visualizzare un'immagine sullo schermo.
Ora che abbiamo un Piastrella
classe, abbiamo bisogno di creare un'istanza di a Piastrella
e aggiungilo alla nostra classe principale:
import flash.display.Sprite; import flash.events.Event; importazione flash.Lib; class Main extends Sprite public function new () super (); var tile = new Tile (); addChild (piastrelle); public static function main () Lib.current.addChild (new Main ());
Il Principale
la classe crea un nuovo Piastrella
quando il costruttore (il nuovo()
funzione, chiamata all'avvio del gioco) viene chiamata e la aggiunge all'elenco di visualizzazione.
Quando il gioco è in esecuzione, il principale()
verrà anche chiamata la funzione e una nuova Principale
l'oggetto sarà aggiunto allo stage. Ora dovresti avere la tua tessera che appare in alto a sinistra dello schermo. Fin qui tutto bene.
Mancia: Se nessuno di questi ha senso, non preoccuparti! È solo un codice Haxe standard per le piastre. La cosa fondamentale da capire è che questo crea un Piastrella
oggetto e lo aggiunge allo schermo.
Il prossimo passo è trovare un modo per riempire l'intero schermo con le tessere. Uno dei modi più semplici per farlo è riempire un array con numeri interi che rappresentano ciascuno un tessera. Quindi puoi scorrere l'array per decidere quale tessera visualizzare e dove visualizzarla.
Hai la possibilità di utilizzare un array tipico o utilizzare una matrice bidimensionale. Nel caso in cui non si abbia familiarità con gli array 2D, è fondamentalmente un singolo array riempito con più array. La maggior parte delle lingue indicherà questo come NameOfArray [x] [y]
dove X
e y
sono indici per l'accesso a un singolo elemento.
Da X
e y
sono anche usati per le coordinate dello schermo, potrebbe avere senso usare X
e y
come coordinate per le nostre tessere. Il trucco con gli array 2D è capire come sono organizzati gli interi. Potrebbe essere necessario invertire il y
e X
quando si scorre attraverso gli array (esempio sotto).
private var exampleArr = [[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]];
Si noti che in questa implementazione, l'elemento "0th" nell'array è esso stesso una matrice di cinque numeri interi. Ciò significherebbe che accedi a ciascun elemento con y
primo, poi X
. Se si tenta di accedere exampleArr [1] [0]
, tu accederai al sesto piastrella.
Se non capisci come funzionano gli array 2D in questo momento, non preoccuparti. Per questo tutorial userò un array normale per mantenere le cose semplici e rendere le cose più facili da visualizzare:
private var exampleArr = [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];
L'esempio sopra mostra come un array normale può essere un po 'più semplice. Possiamo visualizzare esattamente dove sarà la tessera e tutto ciò che dobbiamo fare è usare una formula semplice (non preoccuparti, arriverà!) Per ottenere la tessera che vogliamo.
Ora scriviamo un po 'di codice per creare il nostro array e riempirlo con uno. Il numero uno sarà l'ID che rappresenta la nostra prima tessera.
Per prima cosa dobbiamo creare una variabile all'interno della nostra classe Main per tenere il nostro array:
mappa privata var: matrice;
Questo potrebbe sembrare un po 'strano, quindi lo romperò per te.
Il nome della variabile è map e gli ho dato un tipo molto specifico: schieramento
. Il
porzione dice solo al nostro programma che l'array conterrà solo interi. Le matrici possono contenere praticamente qualsiasi tipo tu voglia, quindi se stai usando qualcos'altro per identificare le tue tessere, sentiti libero di cambiare qui il parametro.
Quindi dobbiamo aggiungere del codice al nostro Principale
costruttore della classe (ricorda, questo è il nuovo()
funzione) in modo che possiamo creare un'istanza della nostra mappa:
map = new Array();
Questa linea creerà un array vuoto che potremo presto riempire con i nostri per provarlo. Innanzitutto, definiamo alcuni valori che ci aiuteranno con la nostra matematica:
public static var TILE_WIDTH = 60; public static var TILE_HEIGHT = 60; public static var SCREEN_WIDTH = 600; public static var SCREEN_HEIGHT = 360;
Ho fatto questi valori statico pubblico
perché questo ci darà accesso a loro da qualsiasi parte del nostro programma (via Main.Tile_WIDTH
e così via). Inoltre, potresti aver notato quella divisione SCREEN_WIDTH
di TILE_WIDTH
ci da 10
e dividendo SCREEN_HEIGHT
di TILE_HEIGHT
ci da 6
. Questi due numeri verranno utilizzati per decidere quanti numeri interi memorizzare nel nostro array.
var w = Std.int (SCREEN_WIDTH / TILE_WIDTH); var h = Std.int (SCREEN_HEIGHT / TILE_HEIGHT); per (i in 0 ... w * h) map [i] = 1
In questo blocco di codice, assegniamo 10
e 6
a w
e h
, come ho detto sopra. Quindi dobbiamo saltare in a per
loop per creare un array che possa adattarsi 10 * 6
interi. Ciò renderà conto di un numero sufficiente di tessere per riempire l'intero schermo.
Ora abbiamo costruito la nostra mappa di base, ma come faremo a dire alle tessere di entrare nel loro posto appropriato? Per questo dobbiamo tornare al Piastrella
classe e creare una funzione che ci permetterà di spostare le tessere a volontà:
funzione pubblica setLoc (x: Int, y: Int) image.x = x * Main.TILE_WIDTH; image.y = y * Main.TILE_HEIGHT;
Quando chiamiamo il setLoc ()
funzione, passiamo nel X
e y
coordinate in base alla nostra classe di mappa (formula in arrivo, lo prometto!). La funzione prende quei valori e li traduce in coordinate pixel moltiplicandoli per TILE_WIDTH
e TILE_HEIGHT
, rispettivamente.
L'unica cosa che rimane da fare per ottenere le nostre tessere sullo schermo è dirlo alla nostra Principale
classe per creare le tessere e posizionarle in base alle loro posizioni all'interno della mappa. Torniamo a Principale
e attuare che:
for (i in 0 ... map.length) var tile = new Tile (); var x = i% w; var y = Math.floor (i / w); tile.setLoc (x, y); addChild (piastrelle);
O si! Esatto, ora abbiamo uno schermo pieno di tessere. Analizziamo ciò che sta succedendo sopra.
La formula che continuo a menzionare è finalmente qui.
Calcoliamo X
prendendo il modulo (%
) di io
e w
(che è 10, ricorda).
Il modulo è il resto dopo la divisione intero: \ (14 \ div 3 = 4 \ text restinder 2 \) so \ (14 \ text modulo 3 = 2 \).
Lo usiamo perché vogliamo il nostro valore X
ripristinare a 0
all'inizio di ogni riga, quindi disegniamo il rispettivo riquadro all'estrema sinistra:
Quanto a y
, prendiamo il pavimento()
di i / w
(cioè, arrotondiamo quel risultato) perché vogliamo solo y
per aumentare una volta raggiunta la fine di ogni riga e spostato verso il basso di un livello:
Math.floor ()
quando si dividono numeri interi; Haxe gestisce semplicemente la divisione intera diversamente. Se stai usando un linguaggio che fa divisione in interi, puoi semplicemente usarlo i / w
(supponendo che siano entrambi interi). Infine, volevo menzionare un po 'sui livelli di scorrimento. Di solito non creerai livelli che si adattano perfettamente alla tua vista. Le tue mappe saranno probabilmente molto più grandi dello schermo e non vuoi continuare a disegnare immagini che il giocatore non sarà in grado di vedere. Con una matematica facile e veloce, dovresti essere in grado di calcolare quali tessere dovrebbero essere sullo schermo e quali tessere per evitare di disegnare.
Ad esempio: le dimensioni dello schermo sono 500 x 500, le tessere sono 100 x 100 e le dimensioni del mondo sono 1000 x 1000. Dovresti semplicemente fare un rapido controllo prima di disegnare le tessere per scoprire quali tessere sono sullo schermo. Usando la posizione del tuo viewport - diciamo 400x500 - devi solo disegnare le tessere dalle righe 4 a 9 e le colonne da 5 a 10. Puoi ottenere quei numeri dividendo la posizione in base alla dimensione della tessera, quindi compensando quei valori con lo schermo dimensione divisa per la dimensione della piastrella. Semplice.
Potrebbe non sembrare ancora molto visto che tutte le tessere sono uguali, ma le fondamenta sono quasi arrivate. Le uniche cose da fare sono creare diversi tipi di tessere e progettare la nostra mappa in modo che si allineino e crei qualcosa di carino.
Bene, ora abbiamo una mappa piena di quelle che coprono lo schermo. A questo punto dovresti avere più di un tipo di tessera, il che significa che dobbiamo cambiare il nostro Piastrella
costruttore per tener conto di ciò:
funzione pubblica new (id: Int) super (); switch (id) case 1: image = new Bitmap (Assets.getBitmapData ("assets / grassLeftBlock.png")); caso 2: image = new Bitmap (Assets.getBitmapData ("assets / grassCenterBlock.png")); caso 3: image = new Bitmap (Assets.getBitmapData ("assets / grassRightBlock.png")); caso 4: image = new Bitmap (Assets.getBitmapData ("assets / goldBlock.png")); caso 5: image = new Bitmap (Assets.getBitmapData ("assets / globe.png")); caso 6: image = new Bitmap (Assets.getBitmapData ("assets / mushroom.png")); addChild (immagine);
Dal momento che ho usato sei tessere diverse per la mia mappa, avevo bisogno di un interruttore
dichiarazione che copre i numeri da uno a sei. Noterai che il costruttore ora prende un intero come parametro in modo da sapere quale tipo di tile creare.
Ora, dobbiamo tornare al nostro Principale
costruttore e fissare la creazione di piastrelle nel nostro per
ciclo continuo:
for (i in 0 ... map.length) var tile = new Tile (mappa [i]); var x = i% w; var y = Math.floor (i / w); tile.setLoc (x, y); addChild (piastrelle);
Tutto quello che dovevamo fare era passare il Mappa [i]
al Piastrella
costruttore per farlo funzionare di nuovo. Se si tenta di eseguire senza passare un numero intero a Piastrella
, ti darà degli errori.
Quasi tutto è a posto, ma ora abbiamo bisogno di un modo per progettare mappe invece di riempirle con tessere casuali. Innanzitutto, rimuovi il per
loop in Principale
costruttore che imposta ogni elemento in uno. Quindi possiamo creare la nostra mappa a mano:
map = [0, 4, 0, 0, 0, 0, 0, 5, 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];
Se si formatta la matrice come sopra, si può facilmente vedere come saranno organizzate le nostre tessere. Puoi anche inserire l'array come una lunga serie di numeri, ma il primo è bello perché puoi vedere 10 across e 6 down.
Quando si tenta di eseguire il programma ora, si dovrebbero ottenere alcune eccezioni del puntatore nullo. Il problema è che stiamo usando zeri nella nostra mappa e nella nostra Piastrella
la classe non sa cosa fare con uno zero. Innanzitutto, ripareremo il costruttore aggiungendo un controllo prima di aggiungere il child dell'immagine:
if (image! = null) addChild (image);
Questo controllo rapido si accerta che non stiamo aggiungendo alcun figlio nullo al Piastrella
oggetti che creiamo. L'ultimo cambiamento per questa classe è il setLoc ()
funzione. In questo momento stiamo cercando di impostare il X
e y
valori per una variabile che non è stata inizializzata.
Aggiungiamo un altro controllo rapido:
funzione pubblica setLoc (x: Int, y: Int) if (image! = null) image.x = x * Main.TILE_WIDTH; image.y = y * Main.TILE_HEIGHT;
Con queste due semplici correzioni, e le tessere che ho fornito sopra, dovresti essere in grado di eseguire il gioco e vedere un semplice livello di piattaforma. Dato che abbiamo lasciato 0 come "non-tile", possiamo lasciarlo trasparente (vuoto). Se vuoi aumentare il livello, puoi inserire uno sfondo prima di disporre le tessere; Ho appena aggiunto una leggera sfumatura blu per apparire come un cielo sullo sfondo.
Questo è praticamente tutto ciò che serve per impostare un modo semplice per modificare i livelli. Prova a sperimentare un po 'con la matrice e vedere quali disegni puoi creare per altri livelli.
Una volta che hai le nozioni di base, potresti prendere in considerazione l'utilizzo di altri strumenti per aiutarti a progettare rapidamente i livelli e vedere come sono fatti prima di lanciarli nel gioco. Un'opzione è creare il tuo editor di livelli. L'alternativa è prendere un software disponibile gratuitamente. I due strumenti popolari sono Tiled Map Editor e Ogmo Editor. Entrambi rendono la modifica di livello molto più semplice con più opzioni di esportazione.
Post correlatiOra sai cos'è un gioco basato su tessere, come ottenere alcune tessere da usare per i tuoi livelli e come sporcarti le mani e scrivere il codice per il tuo motore, e puoi anche fare un po 'di editing di base con un semplice array . Ricorda solo che questo è solo l'inizio; ci sono molti esperimenti che puoi fare per creare un motore ancora migliore.
Inoltre, ho fornito i file sorgente per il check-out. Ecco come appare il file SWF finale:
Scarica il SWF qui.... e qui, ancora una volta, è la matrice che viene generata da:
map = [0, 4, 0, 0, 0, 0, 0, 5, 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];
Il tuo prossimo compito è fare delle ricerche e provare alcune nuove cose per migliorare ciò che hai fatto qui. I fogli Sprite sono un ottimo modo per migliorare le prestazioni e semplificare la vita. Il trucco è ottenere un codice solido disposto per la lettura da un foglio sprite in modo che il tuo gioco non debba leggere ogni immagine individualmente.
Dovresti anche iniziare a praticare le tue abilità artistiche creando alcuni riquadri di tua scelta. In questo modo puoi ottenere l'arte giusta per i tuoi giochi futuri.
Come sempre, lascia qui sotto alcuni commenti su come il tuo motore sta funzionando per te - e forse anche qualche demo, così possiamo provare il tuo prossimo gioco di tessere.