Scopri le basi di come funziona FlashPunk: una biblioteca incredibile per farti risparmiare tempo e aiutarti a creare il gioco perfetto!
Diamo un'occhiata al risultato finale su cui lavoreremo:
Usa i tasti freccia per spostare il tuo personaggio (il ragazzo blu). Il ragazzo rosso / marrone è un NPC; l'area rossa ombreggiata è una zona pericolosa e la casella verde è un pulsante. Imparerai come creare tutto questo in questo tutorial.
FlashPunk è una libreria ActionScript 3 creata per lo sviluppo di giochi Flash. Fondamentalmente, fa tutto il lavoro duro per te e ti permette di concentrarti interamente sullo sviluppo del tuo gioco, piuttosto che sul motore dietro di esso. La parte migliore è che non hai bisogno di Flash Pro per lavorarci: puoi fare tutto con un editor di codice gratuito come FlashDevelop. Per non dire che è molto più veloce quando si tratta di disegnare le cose sullo schermo, dal momento che usa il blitting!
Questo tutorial tratterà tutte le basi di FlashPunk. Dopo averlo seguito, sarai pronto per creare un gioco semplice con questa fantastica libreria!
Inizia scaricando l'ultima versione di FlashPunk dal sito ufficiale (questo tutorial utilizza la versione dal 30 agosto 2011). Metti la cartella "net", con tutti i suoi contenuti, nella cartella "src".
FlashPunk ha una classe chiamata Motore
. Questa classe è ciò che inizia tutto nella libreria. Pensalo come a Principale
classe, ma con un codice speciale per attivare tutte le classi in FlashPunk. Per usare il Motore
classe, modificheremo il Principale
classe un po '.
package import net.flashpunk.Engine; [Frame (factoryClass = "Preloader")] public class Main estende Engine public function Main (): void
Ora, la nostra classe si estende Motore
. Nel Principale
Costruttore, abbiamo bisogno di fare una chiamata al Motore
costruttore: questo è ciò che imposta le informazioni importanti sul gioco: larghezza, altezza, framerate e se il motore deve girare a un framerate fisso o no.
funzione pubblica Main (): void super (550, 400, 30, false);
Esiste una funzione che può (e deve essere) sovrascritta da Motore
classe: il dentro()
funzione. Verrà eseguito solo una volta e inizializzerà tutto per far funzionare il gioco.
sovrascrivi la funzione pubblica init (): void trace ("Il gioco è iniziato!");
Sono abbastanza sicuro che tutti vogliono mettere qualcosa sullo schermo e vedere questo motore funziona! Per questo motivo, i prossimi passi copriranno le basi degli elementi di FlashPunk, aggiungendo profondità man mano che il tutorial prosegue.
In FlashPunk, ci sono elementi chiamati mondi
e Entità
. Questi sono gli elementi principali della libreria e lavorerai con loro dall'inizio alla fine del tuo gioco.
I mondi sono molto simili a quello che è comunemente noto come "schermo". Tutto nel tuo gioco avverrà in un mondo: il menu principale è un mondo che ti darà accesso al mondo di gioco attuale, dove combatterai alcuni nemici e morirai, che ti condurranno al mondo del gioco, con i tuoi punteggi e statistiche su quanto bene hai fatto. Più informazioni sui mondi verranno spiegate più tardi.
Le entità sono esattamente ciò che sembrano essere; vivono in un mondo e fanno qualcosa in esso: un pulsante è un'entità; il tuo personaggio è un'entità; i nemici e i proiettili sono entità. Sono le cose che danno vita al gioco.
Detto questo, creeremo il mondo di gioco (c'è tempo per rendere il mondo del menu principale più tardi, saltiamo a qualche azione!) Estendendo le funzionalità di FlashPunk Mondo
classe:
pacchetto import net.flashpunk.World; classe pubblica GameWorld estende World funzione pubblica GameWorld ()
Ora che hai creato un mondo, devi dire a FlashPunk che vuoi che questo mondo sia quello attivo. Facciamolo dentro Main.as
:
private var _gameWorld: GameWorld; funzione pubblica Main (): void super (550, 400, 30, false); _gameWorld = new GameWorld (); sovrascrive la funzione pubblica init (): void trace ("Il gioco è iniziato!"); FP.world = _gameWorld;
E non dimenticare di importare net.flashpunk.FP
!
Ora che abbiamo il nostro mondo, possiamo creare un'entità estendendo il Entità
classe e aggiungendolo al nostro mondo di gioco:
package import net.flashpunk.Entity; classe pubblica GameEntity estende Entity funzione pubblica GameEntity ()
E in GameWorld.as
:
private var _gameEntity: GameEntity; funzione pubblica GameWorld () _gameEntity = new GameEntity (); aggiungi (_gameEntity);
Si noti che se si compila e si esegue il gioco, l'entità non viene visualizzata sullo schermo. Questo perché non ha ancora un'immagine! Ogni entità può avere un oggetto grafico. Questo grafico può essere una singola immagine, uno spritesheet con animazioni, immagini affiancate - praticamente tutto.
Aggiungeremo questa piccola immagine alla nostra entità:
L'immagine di un'entità è accessibile al grafico
proprietà. Ecco come inseriremo l'immagine! Primo, incorporalo; quindi, basta passarlo a Immagine
Il costruttore e FlashPunk si prenderanno cura di trasformarlo in qualcosa di visibile per te. Compila ed esegui ora. Sorpresa! La nostra entità è lì!
package import net.flashpunk.Entity; import net.flashpunk.graphics.Image; public class GameEntity estende Entity [Embed (source = "/? /img/EntityImage.png")] private const IMAGE: Class; funzione pubblica GameEntity () graphic = new Image (IMAGE);
Questo è ciò che dovresti ottenere:
Ora che abbiamo la nostra entità sullo schermo, che ne dici di farla muovere? Ogni Entità
ha una funzione chiamata aggiornare()
, che devi sostituire per usare. Questa funzione è chiamata da ogni mondo all'inizio di ogni frame. Se hai bisogno di spostare la tua entità, questo è il posto in cui inserisci il tuo codice!
sovrascrivere la funzione pubblica update (): void x + = 10 * FP.elapsed; y + = 5 * FP.elasciato;
E non dimenticare di importare:
import net.flashpunk.FP;
Guardalo in azione! (Aggiorna la pagina se non riesci a vedere nulla qui.)
Potresti aver notato l'uso di FP.elapsed
. FP.elapsed
fornisce la quantità di tempo trascorso dall'ultimo fotogramma (in secondi), rendendo molto facile la creazione di movimenti basati sul tempo. Tuttavia, affinché funzioni, è necessario aver impostato il quarto parametro su Motore
Costruttore di falso
. Ricorda questo (passaggio 2)? Impostandolo a falso
significa che si desidera che FlashPunk venga eseguito con un timestep variabile, mentre lo si imposta su vero
rende FlashPunk eseguito su un timestep fisso. Facendo quest'ultimo, non è necessario utilizzare FP.elapsed
. Lo saprai ogni volta che il aggiornare()
la funzione è chiamata, è passato un frame.
Abbiamo l'entità che si muove in una sola direzione nell'ultimo passaggio. Introducendo l'input da tastiera: ora sarai in grado di spostare l'entità nel punto desiderato!
FlashPunk ha una classe chiamata Ingresso
che si occupa sia della tastiera che del mouse. In questo tutorial, utilizzeremo solo l'input da tastiera per il movimento. È molto facile:
sovrascrivere la funzione pubblica update (): void if (Input.check (Key.A) || Input.check (Key.LEFT)) x - = 50 * FP.elapsed; else if (Input.check (Key.D) || Input.check (Key.RIGHT)) x + = 50 * FP.elapsed; if (Input.check (Key.W) || Input.check (Key.UP)) y - = 50 * FP.elapsed; else if (Input.check (Key.S) || Input.check (Key.DOWN)) y + = 50 * FP.elapsed;
E le dichiarazioni di importazione:
import net.flashpunk.utils.Input; import net.flashpunk.utils.Key;
Input.check ()
ritorna vero
se la Chiave
passato come argomento viene premuto nel momento in cui la funzione è stata chiamata. Ci sono altre funzioni molto utili, come Input.pressed ()
, che ritorna vero
se il tasto è stato premuto nel momento in cui è stata richiamata la funzione (cioè la chiave era su un frame fa ed è ora inattiva), oppure Input.released ()
, che fa esattamente il contrario.
Un'altra cosa interessante che il Ingresso
la classe ci permette di definire molte chiavi con un solo nome. Ad esempio, potremmo definire Key.UP
, Key.W
e Key.I
come "SU"
, e controlla solo per Input.check ( "UP")
. In questo modo, possiamo migliorare la nostra funzione:
funzione pubblica GameEntity () graphic = new Image (IMAGE); Input.define ("UP", Key.W, Key.UP); Input.define ("DOWN", Key.S, Key.DOWN); Input.define ("LEFT", Key.A, Key.LEFT); Input.define ("RIGHT", Key.D, Key.RIGHT); override public function update (): void if (Input.check ("LEFT")) x - = 50 * FP.elapsed; else if (Input.check ("DESTRA")) x + = 50 * FP.selezionato; if (Input.check ("UP")) y - = 50 * FP.stato; else if (Input.check ("DOWN")) y + = 50 * FP.stato;
E questo è ciò che dovresti ottenere:
Le entità possono fare molto di più che muoversi e avere immagini. Diamo un'occhiata a quali sorprese possono contenere!
Le entità hanno una proprietà chiamata genere
. È possibile impostare questa proprietà su qualsiasi stringa desiderata. Questo ti permette di organizzare le tue entità in gruppi, il che si rivelerà molto utile nel prossimo passo (sui mondi). Ad esempio, possiamo impostare il tipo della nostra entità su "GameEntity":
funzione pubblica GameEntity () graphic = new Image (IMAGE); Input.define ("UP", Key.W, Key.UP); Input.define ("DOWN", Key.S, Key.DOWN); Input.define ("LEFT", Key.A, Key.LEFT); Input.define ("RIGHT", Key.D, Key.RIGHT); type = "GameEntity";
A seguito di ciò, abbiamo l'utile mondo
proprietà e il aggiunto ()
e rimosso()
funzioni. Il mondo
La proprietà ti consente di accedere al mondo dall'interno del codice dell'entità una volta che l'entità è stata aggiunta a un mondo. È come il palcoscenico
proprietà in comune sviluppo di Flash; le funzioni sono come il ADDED_TO_STAGE
e REMOVED_FROM_STAGE
ascoltatori di eventi. Ecco un esempio delle funzioni che funzionano GameEntity.as
:
override public function added (): void trace ("L'entità è stata aggiunta al mondo!"); traccia ("Entità nel mondo:" + world.count); override public function removed (): void trace ("L'entità è stata rimossa dal mondo!");
È tempo di dare un'occhiata più profonda ai mondi e al loro funzionamento. Innanzitutto, FlashPunk può avere solo un mondo in esecuzione contemporaneamente, ma il tuo gioco può avere tanti mondi quanti ne vuoi, purché solo uno resti attivo ogni volta.
I mondi hanno aggiornare()
funziona esattamente come le entità, ma la loro funzione è leggermente diversa: c'è un codice reale nel Mondo
classe. Ciò significa che dovrai chiamare super.update ()
ogni volta che si sostituisce questa funzione.
Oltre alle entità, i mondi possono anche avere grafica aggiunto a loro. Le immagini sono immagini che non devono essere aggiornate da te (FlashPunk crea ancora un'entità per aggiungerle al mondo, quindi il motore invierà comunque una chiamata a un aggiornare()
funzione). Puoi aggiungerli chiamando addGraphic ()
.
La cosa più importante dei mondi è che hanno diverse funzioni per recuperare determinate entità: getType ()
, getClass ()
, prendi tutto()
, getLayer ()
e getInstance ()
. In questo modo, puoi fare in modo che il mondo restituisca una matrice di tutti i proiettili attualmente presenti nel gioco, in modo che tu possa eseguire un controllo contro tutti per la collisione. Molto utile, devo dire!
Dai un'occhiata al codice aggiunto a World.as
. Useremo anche una seconda immagine:
[Incorpora (source = "/? /Img/EntityImage2.png")] private const IMAGE: Class; funzione pubblica GameWorld () _gameEntity = new GameEntity (); aggiungi (_gameEntity); addGraphic (nuova immagine (IMAGE), 0, 50, 50); override public function update (): void super.update (); var entityArray: Array = []; getType ("GameEntity", entityArray); per ciascuna (entità var: Entity in entityArray) entity.x = entity.x> 550? 550: entity.x; entity.y = entity.y> 400? 400: entity.y;
E non dimenticare di importare net.flashpunk.graphics.Image
!
In questo codice, il addGraphic ()
chiamata di funzione aggiunge un altro grafico simile a _gameEntity
Il grafico - pensalo come un NPC! - al mondo nella posizione (50, 50). Le righe 23-31 mostrano un esempio di come recuperare solo entità di un tipo particolare: chiamiamo getType ()
per ottenere solo entità del tipo "GameEntity" (attualmente solo una entità). Dopodiché, iteriamo attraverso ogni entità recuperata e impediamo loro di superare i bordi destro e inferiore. (Quindi, l'entità può muoversi fuori dallo schermo, ma non lontano.) Semplice, non è vero??
Tempo per qualcosa di più interessante! FlashPunk supporta animazioni di tutti i tipi. Tutto quello che devi fare è, invece di creare un'istanza di Immagine
, creare un'istanza di Spritemap
. Questa classe riceve un foglio di sprite e ti consente di mappare i frame e collegarti alle animazioni.
Nella classe della nostra entità, incorpora questa spritemap:
Quindi, crea un'istanza di Spritemap
e passare il foglio sprite come parametro al costruttore. Dopodiché, si tratta di chiamare il Inserisci()
e giocare()
funzioni!
[Incorpora (source = "/? /Img/EntitySheet.png")] private const SHEET: Class; private var _timeInterval: Number; funzione pubblica GameEntity () graphic = new Spritemap (SHEET, 40, 20, onAnimationEnd); Spritemap (grafico) .add ("Stopped", [0]); Spritemap (grafico) .add ("Lampeggiante", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 24); Input.define ("UP", Key.W, Key.UP); Input.define ("DOWN", Key.S, Key.DOWN); Input.define ("LEFT", Key.A, Key.LEFT); Input.define ("RIGHT", Key.D, Key.RIGHT); type = "GameEntity"; Spritemap (grafico) .play ( "lampeggiante"); private function onAnimationEnd (): void Spritemap (graphic) .play ("Stopped"); _timeInterval = 0; sostituisce l'aggiornamento della funzione pubblica (): void _timeInterval + = FP.lapsed; if (_timeInterval> = 3) Spritemap (graphic) .play ("Blinking"); if (Input.check ("LEFT")) x - = 50 * FP.stato; else if (Input.check ("DESTRA")) x + = 50 * FP.selezionato; if (Input.check ("UP")) y - = 50 * FP.stato; else if (Input.check ("DOWN")) y + = 50 * FP.stato;
Il costruttore di Spritemap
(riga 19) prende quattro argomenti: una fonte da cui ottenere un grafico, la larghezza e l'altezza di ciascun fotogramma dello spritesheet e una funzione di callback da chiamare quando termina l'animazione (opzionale). Nel GameEntity
Costruttore, creiamo il Spritemap
e definire due animazioni: "Stopped", che contiene solo il primo frame e viene eseguito a 0 fps (interrotto!) e "Blinking", che contiene tutti i frame e viene eseguito a 24 fotogrammi al secondo.
Il resto del codice è lì per riprodurre l'animazione "lampeggiante" ogni tre secondi.
Dai un'occhiata alla nostra entità lampeggiante:
Con tutto ciò che funziona bene, è il momento di introdurre un'altra funzionalità: il rilevamento delle collisioni. FlashPunk ha un ottimo sistema di rilevamento delle collisioni: tutto ciò che dobbiamo fare è impostare hitboxes per le nostre entità e chiedere al mondo di verificare la presenza di collisioni. Per questo, creeremo un'altra entità chiamata Scatola
che conterrà il seguente grafico:
package import net.flashpunk.Entity; import net.flashpunk.graphics.Image; public class Box estende Entity [Embed (source = "/? /img/BoxImage.png")] private const IMAGE: Class; public function Box () graphic = new Image (IMAGE); setHitbox (60, 60);
E dentro GameWorld.as
:
private var _box: Box; funzione pubblica GameWorld () _gameEntity = new GameEntity (); _box = new Box (); aggiungi (_gameEntity); aggiungi (_box); _box.x = 200; _box.y = 150; addGraphic (nuova immagine (IMAGE), 0, 50, 50);
Il setHitbox ()
la funzione imposta un rettangolo che fungerà da hit box per l'entità. I primi due parametri sono la larghezza e l'altezza della scatola. I prossimi due parametri (facoltativo) sono le coordinate di origine (x e y) del rettangolo. Fare lo stesso per GameEntity
:
funzione pubblica GameEntity () graphic = new Spritemap (SHEET, 40, 20, onAnimationEnd); Spritemap (grafico) .add ("Stopped", [0]); Spritemap (grafico) .add ("Lampeggiante", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 24); Input.define ("UP", Key.W, Key.UP); Input.define ("DOWN", Key.S, Key.DOWN); Input.define ("LEFT", Key.A, Key.LEFT); Input.define ("RIGHT", Key.D, Key.RIGHT); type = "GameEntity"; Spritemap (grafico) .play ( "lampeggiante"); setHitbox (40, 20);
Ora che abbiamo sia la nostra entità che la scatola configurata con hitbox, dobbiamo verificare la presenza di collisioni nella classe mondiale:
override public function update (): void super.update (); var entityArray: Array = []; getType ("GameEntity", entityArray); per ciascuna (entità var: Entity in entityArray) entity.x = entity.x> 550? 550: entity.x; entity.y = entity.y> 400? 400: entity.y; if (_gameEntity.collideWith (_box, _gameEntity.x, _gameEntity.y)) trace ("Collision!");
Il scontrarsi con()
la funzione verifica la collisione con l'entità passata come argomento, collocando virtualmente la prima entità (in questo caso, _gameEntity
) nella posizione specificata dal secondo e dal terzo argomento.
Una volta rilevata una collisione, deve esserci una risposta. Modificheremo solo la posizione dell'entità in movimento:
override public function update (): void super.update (); var entityArray: Array = []; getType ("GameEntity", entityArray); per ciascuna (entità var: Entity in entityArray) entity.x = entity.x> 550? 550: entity.x; entity.y = entity.y> 400? 400: entity.y; if (_gameEntity.collideWith (_box, _gameEntity.x, _gameEntity.y)) _gameEntity.x = _gameEntity.y = 0;
Dai un'occhiata all'esempio. Prova a spostare l'entità nella casella.
FlashPunk non ha pulsanti per impostazione predefinita. Quasi tutti i giochi richiedono pulsanti, quindi in questo passaggio creeremo un Pulsante
classe. Prima di tutto, un pulsante ha tre stati (come forse saprai dal comune sviluppo di Flash): "Su", "Sopra" e "Giù". Questo sprite illustra questo:
E ora iniziamo la lezione:
package import net.flashpunk.Entity; import net.flashpunk.graphics.Spritemap; pulsante di classe pubblica estende Entity protected var _map: Spritemap; pulsante funzione pubblica (x: Number = 0, y: Number = 0) super (x, y); public function setSpritemap (asset: *, frameW: uint, frameH: uint): void _map = new Spritemap (asset, frameW, frameH); _map.add ("Su", [0]); _map.add ("Over", [1]); _map.add ("Giù", [2]); grafico = _map; setHitbox (frameW, frameH); override public function render (): void super.render ();
Il setSpritemap ()
funzione imposta una spritemap per il pulsante e imposta "animazioni" per il pulsante. Sempre l'immagine deve avere prima il fotogramma "Su", quindi "Sopra", seguito dal fotogramma "Giù". C'è anche una chiamata a setHitbox ()
. La hitbox verrà utilizzata per verificare se il mouse si trova o meno sulla casella del pulsante.
Ora che il nostro pulsante mostra con successo un'immagine, è ora di creare controlli su e giù. Lo faremo creando due attributi booleani: "sopra" e "cliccato". Scopriremo anche se il mouse si trova sopra la hit box del pulsante o no. Aggiungi queste funzioni in Button.as
:
protected var _over: Boolean; protected var _clicked: Boolean; sovrascrivere la funzione pubblica update (): void if (! world) return; _over = false; _clicked = false; if (collidePoint (x - world.camera.x, y - world.camera.y, Input.mouseX, Input.mouseY)) if (Input.mouseDown) mouseDown (); else mouseOver (); funzione protetta mouseOver (): void _over = true; function protected mouseDown (): void _clicked = true; sovrascrive public function render (): void if (_clicked) _map.play ("Down"); else if (_over) _map.play ("Over"); else _map.play ("Su"); super.render ();
E non dimenticare di importare net.flashpunk.utils.Input
.
Seguendo la logica in aggiornare()
: prima di tutto, entrambi gli attributi (_clicked
e _al di sopra di
) sono impostati su false. Dopodiché, controlliamo se il mouse è sopra il pulsante. Se non lo è, gli attributi rimarranno falsi e il pulsante si troverà nello stato "Su". Se il mouse è finito, controlliamo se il pulsante del mouse è attualmente inattivo. Se ciò è vero, il pulsante è nello stato "Giù" e _clicked
è impostato su true; se è falso, il pulsante si trova nello stato "Sopra" e il pulsante _al di sopra di
l'attributo è impostato su true. Questi attributi definiranno a quale frame dovrebbe andare lo spritemap.
Questo pulsante sarà inutile se non è possibile rilevare quando l'utente ha effettivamente fatto clic su di esso. Cambiamo un po 'la classe per supportare le funzioni di callback:
protected var _callback: Function; var_argument protetto: *; Pulsante funzione pubblica (callback: Funzione, argomento: *, x: Number = 0, y: Number = 0) super (x, y); _callback = callback; _argument = argomento; sovrascrive la funzione pubblica update (): void if (! world) return; _over = false; _clicked = false; if (collidePoint (x - world.camera.x, y - world.camera.y, Input.mouseX, Input.mouseY)) if (Input.mouseReleased) clic (); else if (Input.mouseDown) mouseDown (); else mouseOver (); funzione protetta clickked (): void if (! _argument) _callback (); else _callback (_argument);
Il nostro pulsante è fatto! Questo codice ti permetterà di passare una funzione di callback (e opzionalmente un argomento) al tuo pulsante, quindi ogni volta che l'utente fa clic sul pulsante, la funzione verrà chiamata.
Molti passaggi e niente sullo schermo? È ora di mettere un pulsante! È semplice come aggiungere questo codice GameWorld.as
:
[Incorpora (source = "/? /Img/ButtonSheet.png")] private const BUTTONSHEET: Class; private var _button: Button; funzione pubblica GameWorld () _gameEntity = new GameEntity (); _box = new Box (); _button = new Button (onButtonClick, null); _button.setSpritemap (BUTTONSHEET, 50, 40); aggiungi (_gameEntity); aggiungi (_box); aggiungi (_button); _box.x = 200; _box.y = 150; _button.x = 400; _button.y = 200; addGraphic (nuova immagine (IMAGE), 0, 50, 50); private function onButtonClick (): void FP.screen.color = Math.random () * 0xFFFFFF; traccia ("Il pulsante è stato cliccato!");
Ora tutto ciò che devi fare è compilare il progetto e il pulsante sarà lì!
E ora l'ultima caratteristica di FlashPunk che verrà presentata in questo tutorial! Il consolle
è lo strumento di FlashPunk per il debugging: presenta log, che sono simili alle tracce; mostra il tempo impiegato per eseguire importanti passaggi del motore; e mostra quante entità sono sullo schermo e l'FPS corrente. È un ottimo strumento da usare per sviluppare il tuo gioco. Per abilitarlo, basta aggiungere la seguente riga a Main.as
:
sovrascrivi la funzione pubblica init (): void trace ("Il gioco è iniziato!"); FP.console.enable (); FP.world = _gameWorld;
E per registrare qualcosa, usa il FP.log ()
funzione. Ad esempio, cambiamo traccia()
chiamata:
sovrascrivere la funzione pubblica init (): void FP.console.enable (); FP.log ("Il gioco è iniziato!"); FP.world = _gameWorld;
Questo è praticamente tutto! Vedrai che la parte "Output" dalla console di debug ora mostra il log. Puoi andare avanti e cambiare tutto traccia()
chiama nel nostro codice alle chiamate a FP.log ()
.
E questa è la nostra introduzione a FlashPunk, che copre gli aspetti più importanti di questa straordinaria libreria: entità, mondi, immagini e animazioni; collisione, pulsanti, input e movimento. Spero che ti piaccia questa libreria tanto quanto me - rende davvero il lavoro più semplice!