Crea uno sparatutto spaziale con PlayCanvas parte 2

Questa è la seconda parte della nostra ricerca per creare uno sparatutto spaziale in 3D. Nella prima parte abbiamo visto come impostare un gioco PlayCanvas di base, con fisica e collisione, i nostri modelli e una fotocamera.

Per riferimento, ecco di nuovo una demo dal vivo del nostro risultato finale.

In questa parte, ci concentreremo sulla creazione dinamica di entità con script (per generare proiettili e asteroidi) e su come aggiungere elementi come un contatore FPS e un testo di gioco. Se hai già seguito la parte precedente e sei soddisfatto di quello che hai, puoi iniziare a costruire da quello e saltare la seguente sezione di configurazione minima. Altrimenti, se è necessario riavviare da zero:

Setup minimo

  1. Inizia un nuovo progetto.
  2. Elimina tutti gli oggetti nella scena tranne Fotocamera, Casella e Luce.
  3. Metti sia la luce che la fotocamera dentrol'oggetto casella nel pannello della gerarchia.
  4. Posiziona la fotocamera in posizione (0,1,5,2) e rotazione (-20,0,0).
  5. Assicurati che l'oggetto luminoso sia posizionato in una posizione che abbia un bell'aspetto (l'ho messo sopra la scatola).
  6. Allegare a corpo rigido componente alla scatola. Imposta il suo tipo su dinamico. E impostare il suo smorzamento a 0,95 (sia lineare che angolare).
  7. Allegare a collisione componente alla scatola.
  8. Impostare il gravità a 0 (dalle impostazioni della scena).
  9. Metti una sfera a (0,0,0) solo per segnare questa posizione nello spazio.
  10. Crea e allega questo script alla scatola e chiamalo Fly.js:
var Fly = pc.createScript ('vola'); Fly.attributes.add ('speed', type: 'number', default: 50); // inizializza il codice chiamato una volta per entità Fly.prototype.initialize = function () ; // codice di aggiornamento chiamato ogni frame Fly.prototype.update = function (dt) // Premere Z per applicare if (this.app.keyboard.isPressed (pc.KEY_Z)) // Spostarsi nella direzione opposta alla forza var = this.entity.forward.clone (). scale (this.speed); this.entity.rigidbody.applyForce (forza);  // Ruota su / giù / sinistra / destra se (this.app.keyboard.isPressed (pc.KEY_UP)) var force_up = this.entity.right.clone (). Scale (1); this.entity.rigidbody.applyTorque (force_up);  if (this.app.keyboard.isPressed (pc.KEY_DOWN)) var force_down = this.entity.right.clone (). scale (-1); this.entity.rigidbody.applyTorque (force_down);  if (this.app.keyboard.isPressed (pc.KEY_RIGHT)) // Ruota a destra var force_right = this.entity.up.clone (). scale (-1); this.entity.rigidbody.applyTorque (force_right);  if (this.app.keyboard.isPressed (pc.KEY_LEFT)) var force_left = this.entity.up.clone (). scale (1); this.entity.rigidbody.applyTorque (force_left); ; // metodo di swap chiamato per lo script hot-reloading // eredita lo stato del tuo script qui Fly.prototype.swap = function (old) ; // per saperne di più sull'anatomia dello script, leggi: // http://developer.playcanvas.com/en/user-manual/scripting/

Prova che tutto ha funzionato. Dovresti essere in grado di volare con Z alla spinta e ai tasti freccia per ruotare!

8. Asteroidi riproduttivi

La creazione dinamica di oggetti è fondamentale per quasi ogni tipo di gioco. Nella demo che ho creato, sto generando due tipi di asteroidi. Il primo tipo fluttua e agisce da ostacolo passivo. Si rigenerano quando arrivano troppo lontano per creare un campo di asteroidi costantemente denso attorno al giocatore. Il secondo tipo si genera da più lontano e si sposta verso il giocatore (per creare un senso di pericolo anche se il giocatore non si muove). 

Abbiamo bisogno di tre cose per generare i nostri asteroidi:

  1. Un AsteroidModel entità da cui clonare tutti gli altri asteroidi.
  2. Un AsteroidSpawner script allegato al radice oggetto che fungerà da fabbrica / clonatore.
  3. Un Asteroide script per definire il comportamento di ciascun asteroide. 

Creazione di un modello di asteroidi 

Crea una nuova entità da un modello di tua scelta. Questo potrebbe essere qualcosa fuori dal negozio PlayCanvas, o qualcosa da BlendSwap, o semplicemente una forma di base. (Se stai usando i tuoi modelli, è buona norma aprirlo prima in Blender per verificare il numero di volti usati e ottimizzarlo se necessario).

Dagli una forma di collisione appropriata e un componente del corpo rigido (assicurati che sia dinamico). Una volta che sei soddisfatto, deseleziona l'opzione Abilitato scatola:

Quando disattivi un oggetto come questo, è equivalente a rimuoverlo dal mondo per quanto riguarda il giocatore. Questo è utile per rimuovere temporaneamente oggetti, o nel nostro caso, per mantenere un oggetto con tutte le sue proprietà ma non averlo nel gioco.

Creazione dello script Spawner asteroide 

Crea un nuovo script chiamato AsteroidSpawner.js e collegalo al Radice oggetto nella gerarchia. (Nota che il Root è solo un oggetto normale a cui possono essere collegati tutti i componenti, proprio come la Camera.)

Ora apri lo script che hai appena creato. 

Il modo generale di clonare un'entità e aggiungerlo al mondo tramite script è simile a questo:

// Crea il clone var newEntity = oldEntity.clone (); // Aggiungilo all'oggetto root this.app.root.addChild (newEntity); // Dagli un nuovo nome, altrimenti ottiene anche il nome oldEntity newEntity.name = "ClonedEntity"; // Abilita, supponendo che oldEntity sia disabilitato newEntity.enabled = true;

Questo è il modo in cui clonare un oggetto se si dispone già di un oggetto "oldEntity". Questo lascia una domanda senza risposta: Come accediamo all'AsteroidModel che abbiamo creato? 

Ci sono due modi per farlo. Il modo più flessibile consiste nel creare un attributo di script che trattiene l'entità da clonare, in modo da poter scambiare facilmente i modelli senza toccare lo script. (Questo è esattamente il modo in cui la fotocamera ha guardato lo script al punto 7).

L'altro modo è utilizzare la funzione findByName. Puoi chiamare questo metodo su qualsiasi entità per trovare qualcuno dei suoi figli. Quindi possiamo chiamarlo sull'oggetto root:

var oldEntity = this.app.root.findByName ("AsteroidModel");

E così questo completerà il nostro codice dall'alto. Lo script completo di AsteroidSpawner ora si presenta così:

var AsteroidSpawner = pc.createScript ('asteroidSpawner'); // inizializza il codice chiamato una volta per entità AsteroidSpawner.prototype.initialize = function () var oldEntity = this.app.root.findByName ("AsteroidModel"); // Crea il clone var newEntity = oldEntity.clone (); // Aggiungilo all'oggetto root this.app.root.addChild (newEntity); // Dagli un nuovo nome, altrimenti ottiene anche il nome oldEntity newEntity.name = "ClonedEntity"; // Abilita, supponendo che oldEntity sia disabilitato newEntity.enabled = true; // Imposta la sua posizione newEntity.rigidbody.teleport (new pc.Vec3 (0,0,1)); ; // codice di aggiornamento chiamato ogni frame AsteroidSpawner.prototype.update = function (dt) ; 

Prova che questo ha funzionato lanciando e cercando di vedere se il tuo modello di asteroide esiste.

Nota: l'ho usato newEntity.rigidbody.teleport invece di newEntity.setPosition. Se un'entità ha un corpo rigido, allora il corpo rigido annullerà la posizione e la rotazione dell'entità, quindi ricorda di impostare queste proprietà sul corpo rigido e non sull'entità stessa.

Prima di andare avanti, prova a farlo generare dieci o più asteroidi attorno al giocatore, in modo casuale o in qualche modo sistematico (forse anche in un cerchio?). Sarebbe utile mettere tutto il codice di spawn in una funzione in modo che assomiglierebbe a questo:

AsteroidSpawner.prototype.initialize = function () this.spawn (0,0,0); this.spawn (1,0,0); this.spawn (1,1,0); // eccetera… ; AsteroidSpawner.prototype.spawn = function (x, y, z) // Codice di spawn qui ...

Creazione dello script asteroide

Dovresti sentirti a tuo agio nell'aggiungere nuovi script ora. Crea un nuovo script (chiamato Asteroid.js) e collegalo a AsteroidModel. Poiché tutti i nostri asteroidi generati sono cloni, avranno tutti lo stesso script ad essi associato. 

Se creiamo molti asteroidi, sarebbe una buona idea assicurarci che vengano distrutti quando non abbiamo più bisogno di loro o quando sono abbastanza lontani. Ecco un modo per farlo:

Asteroid.prototype.update = function (dt) // Ottiene il player var player = this.app.root.findByName ("Ship"); // Sostituisci "Spedisci" con qualsiasi nome del tuo giocatore // Clona la posizione dell'asteroide var distance = this.entity.getPosition (). Clone (); // Sottrai la posizione del giocatore dalla posizione di questo asteroide distance.sub (player.getPosition ()); // Ottieni la lunghezza di questo vettore if (distance.length ()> 10) // Qualche soglia arbitraria this.entity.destroy (); ;

Suggerimento per il debugging: Se vuoi stampare qualcosa, puoi sempre usare la console del browser come se fosse una normale app JavaScript. Quindi potresti fare qualcosa del genere console.log (distance.toString ()); per stampare il vettore distanza, e verrà visualizzato nella console.

Prima di proseguire, controlla che l'asteroide scompaia quando ti allontani da esso.

9. Proiettili generati

I proiettili generati dalle uova saranno più o meno la stessa idea degli asteroidi che generano, con un nuovo concetto: Vogliamo rilevare quando il proiettile colpisce qualcosa e rimuoverlo. Per creare il nostro sistema di proiettili, abbiamo bisogno di:

  1. Un modello di proiettile da clonare. 
  2. Uno script Shoot.js per generare proiettili quando si preme X. 
  3. Uno script Bullet.js per definire il comportamento di ciascun proiettile. 

Creazione di un modello di proiettile

Puoi usare qualsiasi forma per il tuo proiettile. Ho usato una capsula solo per avere un'idea di quale direzione il proiettile stava affrontando. Proprio come prima, crea la tua entità, ridimensionala e assegnagli un corpo rigido dinamico e una scatola di collisione appropriata. Dagli il nome "Bullet" in modo che sia facile da trovare.

Una volta terminato, assicurati di disabilitarlo (con la casella di controllo Abilitato).

Creazione di uno script di ripresa

Crea un nuovo script e collegalo alla tua nave giocatore. Questa volta utilizzeremo un attributo per ottenere un riferimento alla nostra entità bullet:

Shoot.attributes.add ('bullet', type: 'entity');

Torna all'editor e fai clic su "analisi" per visualizzare il nuovo attributo e seleziona l'entità proiettile che hai creato. 

Ora nella funzione di aggiornamento, vogliamo:

  1. Clona e aggiungi al mondo.
  2. Applicare una forza nella direzione in cui il giocatore è rivolto. 
  3. Mettilo di fronte al giocatore.

Sei già stato presentato a tutti questi concetti. Hai visto come clonare gli asteroidi, come applicare una forza in una direzione per far muovere la nave e come posizionare le cose. Lascerò l'implementazione di questa parte come una sfida. (Ma se ti blocchi, puoi sempre dare un'occhiata a come ho implementato il mio script Shoot.j nel mio progetto).

Ecco alcuni suggerimenti che potrebbero farti risparmiare un po 'di mal di testa:

  1. Uso keyboard.wasPressed invece di keyboard.isPressed. Quando si rileva quando si preme il tasto X per sparare un colpo, il primo è un modo conveniente per farlo sparare solo quando si preme al contrario di sparare finché si tiene premuto il pulsante.
  2. Uso rotateLocal invece di impostare una rotazione assoluta. Per assicurarsi che il proiettile sia sempre parallelo alla nave, è stato un problema calcolare correttamente gli angoli. Un modo molto più semplice è semplicemente impostare la rotazione del proiettile sulla rotazione della nave, quindi ruotare il proiettile nello spazio locale di 90 gradi sull'asse X.

Creazione dello script di comportamento dei proiettili

A questo punto, i tuoi proiettili dovrebbero essere generati, colpire gli asteroidi e rimbalzare nello spazio vuoto. Il numero di proiettili può rapidamente diventare travolgente e sapere come rilevare la collisione è utile per ogni genere di cose. (Ad esempio, potresti aver notato che puoi creare oggetti che hanno solo un componente di collisione ma nessun corpo rigido, che agirebbero come inneschi ma non reagirebbero fisicamente). 

Crea un nuovo script chiamato Bullet e collegalo al modello Bullet che viene clonato. PlayCanvas ha tre tipi di eventi di contatto. Stiamo per ascoltare collisionend, che spara quando gli oggetti si separano (altrimenti il ​​proiettile verrebbe distrutto prima che l'asteroide abbia la possibilità di reagire).  

Per ascoltare un evento di contatto, digita questo nella funzione init:

this.entity.collision.on ('collisionend', this.onCollisionEnd, this);

E poi crea l'ascoltatore stesso:

Bullet.prototype.onCollisionEnd = function (result) // Distruggi il proiettile se colpisce un asteroide if (result.name == "Asteroid") this.entity.destroy (); ;

È qui che il nome che hai dato ai tuoi asteroidi quando li hai generati diventa rilevante. Vogliamo che il proiettile venga distrutto solo quando si scontra con un asteroide. risultato è l'entità con cui ha finito di scontrarsi.

In alternativa, puoi rimuovere quel controllo e farlo distruggere in caso di collisione con qualsiasi cosa.

È vero che non ci sono altri oggetti nel mondo con cui scontrarsi, ma all'inizio ho avuto alcuni problemi con il giocatore che ha innescato la collisione del proiettile per un fotogramma e spariva prima che potesse essere lanciato. Se hai esigenze di collisione più complicate, PlayCanvas supporta i gruppi di collisione e le maschere, ma non è molto ben documentato al momento della scrittura.

10. Aggiunta di un misuratore FPS

A questo punto, in sostanza, il gioco è finito. Naturalmente, ci sono un sacco di piccole cose polacche che ho aggiunto alla demo finale, ma non c'è niente che non puoi fare con ciò che hai imparato finora. 

Volevo mostrarti come creare un misuratore FPS (anche se PlayCanvas ha già un profiler, puoi passare il mouse sul pulsante di riproduzione e controllare la casella del profiler) perché è un buon esempio di aggiunta di un elemento DOM al di fuori del motore PlayCanvas. 

Useremo questa fantastica libreria FPSMeter. La prima cosa da fare è andare al sito web della biblioteca e scaricare la versione di produzione ridotta.

Torna al tuo editor di PlayCanvas, crea un nuovo script e copia il codice fpsMeter.min.js. Allegare questo script all'oggetto root.

Ora che la libreria è stata caricata, creare un nuovo script che inizializzerà e utilizzerà la libreria. Chiamalo meter.js e dall'esempio di utilizzo sul sito web della biblioteca, abbiamo:

var Meter = pc.createScript ('meter'); Meter.prototype.initialize = function () this.meter = new FPSMeter (document.body, graph: 1, heat: 1); ; Meter.prototype.update = function (dt) this.meter.tick (); ; 

Aggiungere lo script del misuratore all'oggetto root e avviarlo. Dovresti vedere il contatore FPS nell'angolo in alto a sinistra dello schermo!

11. Aggiunta di testo

Infine, aggiungiamo del testo nel nostro mondo. Questo è un po 'complicato dal momento che ci sono vari modi per farlo. Se vuoi solo aggiungere un'interfaccia utente statica, un modo per farlo è lavorare direttamente con il DOM, sovrapponendo gli elementi dell'interfaccia utente all'elemento canvas di PlayCanvas. Un altro metodo è usare gli SVG. Questo post discute alcuni di questi diversi modi.

Poiché questi sono tutti metodi standard per gestire il testo sul Web, ho scelto invece di guardare come creare un testo che esiste nello spazio del mondo di gioco. Quindi pensalo come un testo che andrebbe su un segno nell'ambiente o un oggetto nel gioco.

Il modo in cui lo facciamo è creando un Materiale per ogni pezzo di testo che vogliamo rendere. Quindi creiamo un tela invisibile che eseguiamo il rendering del testo utilizzando il noto metodo di riempimento del testo. Alla fine, noi rendere la tela sul materiale per farlo comparire nel gioco.

Si noti che questo metodo può essere utilizzato per più di un semplice testo. È possibile disegnare dinamicamente trame o fare qualsiasi cosa che una tela può fare.

Crea il materiale del testo

Crea un nuovo materiale e chiamalo qualcosa come "TextMaterial". Imposta il suo colore diffuso in nero poiché il nostro testo sarà bianco.

Crea un'entità piana e allega questo materiale.

Crea lo script di testo

Puoi trovare lo script completo text.j in questo elenco:

https://gist.github.com/OmarShehata/e016dc219da36726e65cedb4ab9084bd

Puoi vedere come imposta la texture per usare la tela come fonte, in particolare sulla linea: this.texture.setSource (this.canvas);

Crea questo script e collegalo al tuo aereo. Nota come crea due attributi: testo e dimensione del font. In questo modo puoi usare lo stesso script per qualsiasi oggetto di testo nel tuo gioco.

Avvia la simulazione e dovresti vedere il grande testo "Hello World" da qualche parte. Se non lo vedi, assicurati che a) abbia una fonte di luce nelle vicinanze eb) stai guardando il lato corretto di esso. Il testo non verrà visualizzato se lo stai guardando da dietro. (Aiuta anche a posizionare un oggetto fisico vicino al piano solo per individuarlo all'inizio).

12. Pubblicazione

Una volta creato il tuo fantastico prototipo, puoi fare clic sull'icona PlayCanvas nell'angolo in alto a sinistra dello schermo e selezionare "Pubblicazione". È qui che puoi pubblicare nuove build da ospitare su PlayCanvas e condividerle con il mondo!

Conclusione

Questo è tutto per questa demo. C'è molto altro da esplorare su PlayCanvas, ma spero che questa panoramica ti faccia sentire a tuo agio con le basi per iniziare a costruire i tuoi giochi. È un motore davvero bello che penso che più persone dovrebbero usare. Molto di ciò che è stato creato con esso è stato demo di tecnologia piuttosto che giochi completi, ma non c'è motivo per cui non si possa costruire e pubblicare qualcosa di fantastico con esso.

Una caratteristica di cui non ho davvero parlato, ma che potrebbe essere stato evidente è che l'editor di PlayCanvas ti consente di aggiornare il tuo gioco in tempo reale. Questo è vero per il design, in quanto è possibile spostare le cose nell'editor e si aggiorneranno nella finestra di avvio se è aperto, così come per il codice, con il suo caricamento a caldo.

Infine, mentre l'editor è veramente conveniente, tutto ciò che puoi fare con esso può essere fatto con codice puro. Quindi, se hai bisogno di usare PlayCanvas con un budget, un buon riferimento da usare è la cartella degli esempi su GitHub. (Un buon punto di partenza sarebbe questo semplice esempio di un cubo rotante.)

Se qualcosa è fonte di confusione, per favore fatemelo sapere nei commenti! O solo se hai costruito qualcosa di interessante e vuoi condividere, o hai trovato un modo più semplice per fare qualcosa, mi piacerebbe molto vedere!