Creați un shooter spațial cu PlayCanvas Partea 2

Aceasta este a doua parte a misiunii noastre de a crea un shooter spațial 3D. În prima parte ne-am uitat la modul de a configura un joc de bază PlayCanvas, cu fizică și coliziune, propriile noastre modele și o cameră foto.

Pentru referință, iată un nou demo al rezultatului nostru final.

În această parte, ne vom concentra pe crearea de entități dinamice cu scripturi (pentru a distruge gloanțe și asteroizi), precum și cum să adăugați lucruri precum un contor FPS și un text în joc. Dacă ați urmat deja partea anterioară și sunteți mulțumit de ceea ce aveți, puteți începe să construiți și să omiteți următoarea secțiune minimă de configurare. În caz contrar, dacă trebuie să reporniți de la zero:

Setare minimă

  1. Începeți un nou proiect.
  2. Ștergeți toate obiectele din scenă, cu excepția camerei, a casetei și a luminii.
  3. Puneți atât lumina, cât și camera interiorobiectul caseta din panoul ierarhiei.
  4. Plasați camera în poziție (0,1,5,2) și rotație (-20,0,0).
  5. Asigurați-vă că obiectul luminos este așezat într-o poziție care arată bine (am pus-o pe partea superioară a cutiei).
  6. Atașați a corp rigid componentă la cutie. Setați tipul său la dinamic. Și puneți-o amortizare până la 0,95 (atât liniar cât și unghiular).
  7. Atașați a coliziune componentă la cutie.
  8. Seteaza gravitatea la 0 (din setările de scenă).
  9. Plasați o sferă la (0,0,0) doar pentru a marca această poziție în spațiu.
  10. Creați și atașați acest script la casetă și apelați-l Fly.js:
var Fly = pc.createScript ("zbura"); Fly.attributes.add ('viteză', tip: 'număr', implicit: 50); // inițializați codul numit o dată pe entitate Fly.prototype.initialize = function () ; // actualizați codul numit fiecare cadru Fly.prototype.update = funcția (dt) // Apăsați Z pentru a împinge dacă (this.app.keyboard.isPressed (pc.KEY_Z)) // Deplasați-vă în direcția cu care se confruntă var force = această scală de acțiune.forward.clone () (această viteză); this.entity.rigidbody.applyForce (forță);  // Rotire sus / jos / stânga / dreapta dacă (acest.app.keyboard.isPrestit (pc.KEY_UP)) var force_up = această.entity.right.clone () scară (1); this.entity.rigidbody.applyTorque (force_up);  dacă this.app.keyboard.isPressed (pc.KEY_DOWN)) var force_down = această.entity.right.clone () scară (-1); this.entity.rigidbody.applyTorque (force_down);  dacă (this.app.keyboard.isPrestit (pc.KEY_RIGHT)) // Rotiți spre dreapta var scale_right = this.entity.up.clone () scale (-1); this.entity.rigidbody.applyTorque (force_right);  dacă (acest.app.keyboard.isPrescut (pc.KEY_LEFT)) var force_left = această valoare. (.) scară (); this.entity.rigidbody.applyTorque (force_left); ; // metoda swap a sunat pentru script-reloading // moșteni starea scriptului aici Fly.prototype.swap = function (old) ; // pentru a afla mai multe despre anatomia scriptului, vă rugăm să citiți: // http://developer.playcanvas.com/en/user-manual/scripting/

Verificați că totul a funcționat. Ar trebui să puteți zbura cu Z pentru a împinge tastele săgeată și săgeată!

8. Asteroizii de reproducere

Crearea obiectelor dinamice este crucială pentru aproape orice tip de joc. În demo-ul pe care l-am creat, aș produce două tipuri de asteroizi. Primul tip plutește în jurul valorii și acționează ca obstacole pasive. Acestea respawn atunci când acestea sunt prea departe pentru a crea un câmp asteroid dens constant în jurul jucătorului. Cel de-al doilea tip provine de la distanță și se deplasează spre jucator (pentru a crea un sentiment de pericol, chiar dacă jucătorul nu se mișcă). 

Avem nevoie de trei lucruri pentru a ne arunca asteroizii:

  1. Un AsteroidModel entitate din care să cloneze toți ceilalți asteroizi.
  2. Un AsteroidSpawner script atașat la rădăcină obiect care va acționa ca fabrica / clonerul nostru.
  3. Un Asteroid script pentru a defini comportamentul fiecărui asteroid. 

Crearea unui model de asteroizi 

Creați o entitate nouă dintr-un model ales de dvs. Acest lucru ar putea fi ceva din magazinul PlayCanvas, sau ceva de la BlendSwap, sau doar o formă de bază. (Dacă utilizați propriile modele, este o practică bună să o deschideți mai întâi în Blender pentru a verifica numărul de fețe folosite și pentru a optimiza dacă este necesar.)

Dați-i o formă potrivită de coliziune și o componentă rigidă a caroseriei (asigurați-vă că este dinamică). Odată ce sunteți mulțumit de aceasta, debifați Activat cutie:

Când dezactivezi un astfel de obiect, e echivalent cu eliminarea lui din lume în ceea ce privește jucătorul. Acest lucru este util pentru eliminarea temporară a obiectelor sau, în cazul nostru, pentru păstrarea unui obiect cu toate proprietățile acestuia, dar fără ca acesta să apară în joc.

Crearea Scriptului Asteroid Spawner 

Creați un script nou numit AsteroidSpawner.js și atașați-l la Rădăcină obiect în ierarhie. (Rețineți că Rootul este doar un obiect obișnuit care poate avea orice componente atașate la acesta, la fel ca Camera.)

Acum deschideți scenariul pe care tocmai l-ați creat. 

Modul general de a clona o entitate și de ao adăuga în lume prin scenariu arată astfel:

// Creați clona var newEntity = oldEntity.clone (); // Adăugați-l la obiectul rădăcină this.app.root.addChild (newEntity); // Dați-i un nou nume, în caz contrar devine oldEntity's name newEntity.name = "ClonedEntity"; // Activați-l, presupunând că oldEntity este setat la dezactivat newEntity.enabled = true;

Acesta este modul în care ați clona un obiect dacă aveați deja un obiect "oldEntity". Acest lucru lasă o întrebare fără răspuns: Cum accesăm AsteroidModelul pe care l-am creat? 

Există două moduri de a face acest lucru. Modul mai flexibil este crearea unui atribut de script care să dețină entitatea care să cloneze, astfel încât să puteți schimba cu ușurință modelele fără să atingeți scenariul. (Acesta este exact modul în care am făcut-o înapoi la pasul 7.)

Cealaltă este folosirea funcției findByName. Puteți apela această metodă pe orice entitate pentru a găsi oricare dintre copiii săi. Așa că o putem numi pe obiectul rădăcină:

Var oldEntity = această.app.root.findByName ("AsteroidModel");

Și așa va termina codul nostru de sus. Scriptul complet AsteroidSpawner arată acum:

var AsteroidSpawner = pc.createScript ("asteroidSpawner"); // inițializați codul numit o singură dată pe entitate AsteroidSpawner.prototype.initialize = function () var oldEntity = this.app.root.findByName ("AsteroidModel"); // Creați clona var newEntity = oldEntity.clone (); // Adăugați-l la obiectul rădăcină this.app.root.addChild (newEntity); // Dați-i un nou nume, în caz contrar devine oldEntity's name newEntity.name = "ClonedEntity"; // Activați-l, presupunând că oldEntity este setat la dezactivat newEntity.enabled = true; // Setați poziția lui newEntity.rigidbody.teleport (nou pc.Vec3 (0,0,1)); ; // actualizați codul numit fiecare cadru AsteroidSpawner.prototype.update = function (dt) ; 

Testați că acest lucru a funcționat prin lansarea și căutarea pentru a vedea dacă modelul dvs. de asteroizi există.

Notă: Am folosit newEntity.rigidbody.teleport in loc de newEntity.setPosition. Dacă o entitate are un corp rigid, atunci corpul rigid va suprascrie poziția și rotația entității, așa că nu uitați să setați aceste proprietăți pe corpul rigid și nu pe entitatea însăși.

Înainte de a vă deplasa, încercați să faceți ca în jurul playerului să apară zece sau mai mulți asteroizi, fie la întâmplare, fie într-un mod sistematic (poate chiar într-un cerc?). Aceasta ar ajuta să vă puneți codul de reproducere într-o funcție, astfel încât să pară așa:

AsteroidSpawner.prototype.initialize = funcția () this.spawn (0,0,0); this.spawn (1,0,0); this.spawn (1,1,0); // etc ...; AsteroidSpawner.prototype.spawn = funcția (x, y, z) // Codul de reproducere aici ...

Crearea scriptului asteroid

Ar trebui să fiți confortabil să adăugați noi scripturi până acum. Creați un script nou (numit Asteroid.js) și atașați-l la AsteroidModel. Deoarece toți asteroizii născuți sunt clone, toți vor avea același script atașat la ei. 

Dacă creăm o mulțime de asteroizi, ar fi o idee bună să ne asigurăm că sunt distruși atunci când nu mai avem nevoie de ei sau când sunt departe de a fi departe. Iată un mod în care puteți face acest lucru:

Asteroid.prototype.update = funcția (dt) // Obțineți playerul var player = this.app.root.findByName ("Ship"); // Înlocuiți "Ship" cu numele playerului dvs. // Clonați poziția asteroidului var distance = this.entity.getPosition () clone (); // Scoateți poziția jucătorului de la distanța de poziție a asteroidului.sub (player.getPosition ()); // Obțineți lungimea acestui vector dacă (distance.length ()> 10) // Un prag arbitrar this.entity.destroy (); ;

Sfat de depanare: Dacă doriți să imprimați ceva, puteți utiliza întotdeauna consola browserului ca și cum aceasta ar fi o aplicație JavaScript obișnuită. Deci ai putea să faci ceva de genul ăsta console.log (distance.toString ()); pentru a imprima vectorul distanței și va apărea în consola.

Înainte de a continua, verificați dacă asteroidul dispare atunci când vă îndepărtați de el.

9. Gloante de reproducere

Gloanțele de reproducere vor fi aproximativ aceeași idee ca și asteroizii de reproducere, cu un nou concept: Vrem să detectăm când glonțul lovește ceva și să îl eliminăm. Pentru a crea sistemul nostru de bullet, avem nevoie de:

  1. Un model de glonț pentru clonare. 
  2. Un script Shoot.js pentru gloanțe de reproducere când apăsați X. 
  3. Un script Bullet.js pentru definirea comportamentului fiecărui glonț. 

Crearea unui model de bullet

Puteți folosi orice formă pentru gloanțele dvs. Am folosit o capsulă doar pentru a avea o idee în ce direcție se află glonțul. La fel ca înainte, creați-vă entitatea, scalați-o în jos și dați-i un corp dinamic rigid și o cutie de coliziune corespunzătoare. Dați-i numele "Bullet", astfel că va fi ușor de găsit.

După ce ați terminat, asigurați-vă că ați dezactivat (cu caseta de selectare Activată).

Crearea unui script de tragere

Creați un script nou și atașați-l la nava jucătorului. De data aceasta vom folosi un atribut pentru a obține o referință la entitatea bulletului nostru:

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

Mergeți înapoi la editor și apăsați "parsează" pentru ca noul atribut să apară și selectați entitatea bulletului pe care ați creat-o. 

Acum, în funcția de actualizare, dorim să:

  1. Clonați-l și adăugați-l în lume.
  2. Aplicați o forță în direcția în care se află jucătorul. 
  3. Plasați-l în fața playerului.

Ați fost deja prezentați la toate aceste concepte. Ați văzut cum să clonați asteroizii, cum să aplicați o forță într-o direcție pentru a face nava să se miște și cum să poziționați lucrurile. Voi lăsa implementarea acestei părți ca o provocare. (Dar, dacă rămâneți blocat, puteți merge mereu la modul în care am implementat propriul meu script Shoot.js în proiectul meu).

Iată câteva sfaturi care ar putea să vă salveze dureri de cap:

  1. Utilizare keyboard.wasPressed in loc de keyboard.isPressed. Când se detectează când tasta X este apăsată pentru a trage o fotografie, prima este o modalitate convenabilă de a face foc doar atunci când apăsați, spre deosebire de ardere, atâta timp cât butonul este ținut.
  2. Utilizare rotateLocal în loc de a seta o rotație absolută. Pentru a vă asigura că glonțul este întotdeauna paralel cu nava, a fost o durere pentru a calcula corect unghiurile. O modalitate mult mai ușoară este să setați pur și simplu rotația glonțului la rotația navei și apoi să rotiți gloantele în spațiul local cu 90 de grade pe axa X.

Crearea Scripturii comportamentului bullet

În acest moment, gloanțele ar trebui să se reproducă, să lovească asteroizii și să se ricoșeze doar în spațiu gol. Numărul de gloanțe poate deveni rapid copleșitor și cunoașterea modului de a detecta coliziunea este utilă pentru tot felul de lucruri. (De exemplu, s-ar putea să fi observat că puteți crea obiecte care au doar o componentă de coliziune, dar nu un corp rigid. Acestea ar acționa ca declanșatoare, dar nu ar reacționa fizic.) 

Creați un nou script numit Bullet și atașați-l la modelul dvs. Bullet care este clonat. PlayCanvas are trei tipuri de evenimente de contact. O să ascultăm collisionend, care aprinde când obiectele se separă (în caz contrar glonțul ar fi distrus înainte ca asteroidul să aibă șansa de a reacționa).  

Pentru a asculta despre un eveniment de contact, tastați-l în funcția de inițializare:

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

Și apoi creați ascultătorul în sine:

Bullet.prototype.onCollisionEnd = funcție (rezultat) // Distrugeți glonțul dacă atinge un asteroid dacă (result.name == "Asteroid") this.entity.destroy (); ;

Acesta este locul în care numele pe care l-ați dat asteroizilor dvs. când le-ați dat naștere devine relevant. Vrem ca gloantele să fie distruse numai când se ciocnesc cu un asteroid. rezultat este entitatea cu care sa încheiat coliziunea.

Alternativ, ați putea să eliminați cecul și să îl distrugeți în caz de coliziune cu orice.

Este adevărat că nu există alte obiecte din lume care să se ciocnească, dar am avut unele probleme de timpuriu cu jucătorul care a declanșat coliziunea glonțului pentru un cadru și a dispărut înainte de a putea lansa. Dacă aveți nevoi mai complicate de coliziune, PlayCanvas nu suportă grupuri de ciocniri și măști, dar nu este foarte bine documentat la momentul redactării.

10. Adăugarea unui contor FPS

În esență, am terminat cu jocul în sine în acest moment. Desigur, există multe lucruri mici poloneze pe care le-am adăugat la demo-ul final, dar nu există nimic în care nu puteți face cu ceea ce ați învățat până acum. 

Am vrut să vă arăt cum să creați un contor FPS (chiar dacă PlayCanvas are deja un profiler; puteți să treceți cu mouse-ul peste butonul de redare și să verificați caseta Profiler) deoarece este un bun exemplu de adăugare a unui element DOM care este în afara motorului PlayCanvas. 

Vom folosi această bibliotecă FPSMeter. Primul lucru pe care trebuie să-l faceți este să vă îndreptați către site-ul bibliotecii și să descărcați versiunea de producție minificată.

Întoarceți-vă la editorul PlayCanvas, creați un script nou și copiați codul fpsMeter.min.js. Atașați acest script la obiectul rădăcină.

Acum, când biblioteca a fost încărcată, creați un script nou care va inițializa și utiliza biblioteca. Sunați-l metru.js, iar din exemplul de utilizare de pe site-ul bibliotecii, avem:

var Meter = pc.createScript (metru); Meter.prototype.initialize = funcția () this.meter = nou FPSMeter (document.body, graph: 1, heat: 1); ; Meter.prototype.update = funcția (dt) this.meter.tick (); ; 

Adăugați scriptul contorului la obiectul rădăcină și lansați-l. Ar trebui să vedeți contorul FPS în colțul din stânga sus al ecranului!

11. Adăugarea textului

În cele din urmă, să adăugăm un text în lumea noastră. Aceasta este puțin implicată, deoarece există diferite modalități de a face acest lucru. Dacă doriți doar să adăugați un interfață statică, o modalitate de a face acest lucru este să lucrați direct cu DOM, suprapunând elementele dvs. UI pe elementul canvas al PlayCanvas. O altă metodă este utilizarea SVG-urilor. Acest post discută unele dintre aceste moduri diferite.

Deoarece toate acestea sunt modalități standard de manipulare a textului pe web, am optat în schimb să vă uit la cum să creați textul care există în spațiul lumii jocurilor. Deci, gândiți-vă la aceasta ca la un text care ar merge pe un semn în mediul înconjurător sau un obiect din joc.

Modul în care facem acest lucru este crearea unui material pentru fiecare piesa de text pe care dorim sa o facem. Apoi creăm un invizibil panza că facem textul să utilizeze metoda obișnuită fillText în panza. În cele din urmă, noi face panza pe material ca să apară în joc.

Rețineți că această metodă poate fi utilizată pentru mai mult decât textul. Ai putea să atragi dinamic texturi sau să faci ceva ce poate face o pânză.

Creați materialul de text

Creați un material nou și numiți-l "TextMaterial". Setați culoarea difuză la negru, deoarece textul nostru va fi alb.

Creați o entitate plană și atașați acest material la aceasta.

Creați scriptul text

Puteți găsi scriptul full text.js din acest conținut:

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

Puteți vedea cum configurează textura pentru a folosi panza ca sursă, în special la linia: this.texture.setSource (this.canvas);

Creați acest script și atașați-l la avionul dvs. Observați cum creează două atribute: text și marimea fontului. În acest fel, puteți utiliza același script pentru orice obiect text din joc.

Lansați simularea și ar trebui să vedeți marele text "Hello World" undeva în jur. Dacă nu îl vedeți, asigurați-vă că a) are o sursă de lumină în apropiere și b) căutați partea corectă a acesteia. Textul nu se va afișa dacă îl privești din spate. (De asemenea, ajută la plasarea unui obiect fizic în apropierea avionului doar pentru a-l localiza la început.)

12. Editarea

Odată ce ați pus împreună prototipul dvs. minunat, puteți face clic pe pictograma PlayCanvas din colțul din stânga sus al ecranului și selectați "Publicare". Aici puteți să publicați noi materiale care să fie găzduite pe PlayCanvas și să le distribuiți lumii!

Concluzie

Asta e pentru acest demo. Există mult mai multe de explorat în PlayCanvas, dar sperăm că această imagine de ansamblu vă va face destul de confortabil cu elementele de bază pentru a începe să vă construiți propriile jocuri. Este un motor foarte frumos pe care cred că ar trebui să-l folosească mai mulți oameni. Multe dintre ceea ce a fost creat cu acesta a fost demo-uri de tech, mai degrabă decât jocuri pline, dar nu există niciun motiv pentru care nu puteți construi și publica ceva minunat cu el.

O caracteristică despre care nu am vorbit, dar ar fi putut fi evident că editorul PlayCanvas vă permite să vă actualizați jocul în timp real. Acest lucru este valabil pentru design, prin faptul că puteți muta lucrurile în editor și se vor actualiza în fereastra de lansare, dacă îl aveți deschis, precum și pentru cod, cu reîncărcarea fierbinte.

În cele din urmă, în timp ce editorul este într-adevăr convenabil, tot ceea ce puteți face cu el se poate face cu codul pur. Deci, dacă trebuie să utilizați PlayCanvas pe un buget, o referință bună la utilizare este dosarul de exemple de pe GitHub. (Un bun loc pentru a începe ar fi acest exemplu simplu de cub de rotire.)

Dacă ceva este confuz, lasă-mă să știu în comentariile! Sau doar dacă ați construit ceva răcoros și doriți să împărțiți, sau ați dat seama că este mai ușor să faceți ceva, mi-ar plăcea să văd!