În acest tutorial, veți învăța cum să utilizați Physi.js pentru a adăuga fizica jocurilor la o scenă 3D creată folosind Three.js. Vom crea un joc simplu în care conducem un cărucior în jurul obiectelor de colectare, utilizând forme fizice de bază și constrângeri fizice.
Acest tutorial se va baza pe conceptele partajate în tutorialul meu precedent Three.js. V-aș ruga să o citiți dacă sunteți nou la Three.js și la crearea scenei 3D.
Datorită unei limitări tehnice în ceea ce privește găzduirea soluțiilor bazate pe lucrătorii web pe JSFiddle, nu putem încorpora jocul interactiv în această pagină de instruire. Utilizați codul sursă furnizat pentru a verifica exemplul de lucru pe orice IDE bazat pe cloud sau prin auto-găzduire.
Există în prezent mai multe cadre și motoare care pot fi folosite pentru a crea conținut 3D pentru web cu fizica. Unele dintre cele care merită menționate sunt Turbulenz, BabylonJS, PlayCanvas și construirea evidentă a Unity WebGL. Dar când vine vorba de popularitate și ușurință în utilizare, majoritatea oamenilor le place să folosească Three.js pentru experimentele lor. Deoarece Three.js este un motor de randare și nu are fizică integrată, trebuie să explorăm cadre suplimentare pentru a adăuga capacitatea fizică.
O soluție foarte populară de fizică JavaScript este Ammo.js, care este un port direct al fizicii Bullet. În timp ce utilizați direct Ammo.js este posibil, nu este foarte ușor de început și are o mulțime de cod de boilerplate pentru fiecare aspect. De asemenea, deoarece nu este scris manual, dar portat folosind Emscripten, codul nu este ușor de înțeles.
O soluție alternativă este utilizarea Cannon.js sau Physijs. Lucrul interesant cu Physijs este că accentul se pune întotdeauna pe facilitarea lucrurilor, ceea ce îl face alegerea ideală pentru începători. Este construit pe baza Ammo.js și are chiar și o ramură Cannon.js. Acest tutorial folosește Physijs și Three.js pentru a construi un prototip de joc de lucru cu capabilități fizice.
O altă opțiune, deși simplificată, ar fi utilizarea cadrului Whitestorm, care este un cadru bazat pe componente bazat pe Three.js și Physijs.
Trebuie să avem fișierele ammo.js, physi.js, fizijs_worker.js și three.js în structura de foldere sau în mediul de codare pentru a folosi Physijs. Physijs utilizează un lucrător web pentru a folosi diferite fire pentru calculele fizicii. Deci, primul pas în procesul de integrare este crearea unui lucrător web, după cum urmează.
Physijs.scripts.worker = 'lib / physijs_worker.js'; Physijs.scripts.ammo = 'ammo.js';
În acest moment, setarea este completă și putem începe să folosim cadrul fizic. Physijs sa asigurat că a urmat stilul de codare al lui Three.js, iar cele mai multe dintre concepte sunt înlocuiri simple ale conceptului corespunzător Three.js.
In loc de THREE.Scene
, trebuie să folosim Physijs.Scene
.
Există mai multe ochiuri de plasă disponibile în Physijs care trebuie folosite în locul lui THREE.Mesh
. Opțiunile disponibile sunt PlaneMesh
, BoxMesh
, SphereMesh
, CylinderMesh
, ConeMesh
, CapsuleMesh
, ConvexMesh
, ConcaveMesh
, și HeighfieldMesh
.
Trebuie să sunăm scene.simulate
metoda de a face calculele fizicii fie în face
sau în intervale frecvente. Permiteți-mi să vă reamintesc că calculele de fizică se întâmplă într-un fir diferit și nu vor fi sincronizate sau la fel de repede ca și buclele de redare a scenei.
Chiar și următorul apel către scene.simulate
se poate întâmpla în timp ce calculele anterioare sunt încă în desfășurare. Pentru a face corect sincronizarea cu calculele de fizică, am putea folosi scena lui Physijs Actualizați
eveniment.
scene.addEventListener ('actualizare', funcția () // codul dvs., calculele fizicii au făcut actualizarea);
Pentru a înregistra o coliziune pe un obiect Mesh Physijs denumit arbitrar ca cub
, putem asculta coliziune
eveniment.
cube.addEventListener ("coliziune", funcție (objCollidedWith, linearVelOfCollision, angularVelOfCollision) );
În cadrul metodei de mai sus, acest
se va referi la cub
, in timp ce objCollidedWith
este obiectul cub
sa ciocnit cu.
Pentru acest tutorial, vom crea un joc foarte simplu bazat pe fizică în care vom folosi constrângerile fizice pentru a crea un vehicul. Jucătorul poate folosi tastele săgeată pentru a conduce autovehiculul și pentru a colecta o minge de bouncing care apar întâmplător în zona de joacă.
Interesant, Physijs are deja o specială vehicul
caracteristică care poate fi utilizată direct pentru crearea vehiculelor, dar nu o vom folosi.
Lumea noastră de joc este un teren vast, cu pereți pe cele patru laturi ale sale, ca în cele de mai jos.
Folosim Physijs.BoxMesh
pentru sol și cele patru pereți, așa cum se arată în codul de mai jos.
ground_material = Physijs.createMaterial (nou THREE.MeshStandardMaterial (culoare: 0x00ff00), frecare, .9 // restituire redusă); // Masa terenului = noul Physijs.BoxMesh (noua THREE.BoxGeometrie (150, 1, 150), materialul de la sol, 0 / masa); ground.receiveShadow = adevărat; scene.add (la sol);
Observați utilizarea de Physijs.createMaterial
pentru a crea materialele fizice necesare prin trecerea unei valori de frecare și a unei valori de restituire. Valoarea de frecare determină aderența pe teren, iar valoarea restituirii determină bounciness. Un lucru important este faptul că atunci când oferim o valoare de masă 0
, noi creăm un obiect fix de plasă.
Vom crea un vehicul special care are două părți conectate. Partea frontală, care are trei roți, acționează ca motor, iar partea din spate, având două roți, va acționa ca un transportor. Partea de tractare este conectată la piesa motorului utilizând o articulație articulată implementată cu ajutorul unui a Physijs.HingeContraint
.
Roțile folosesc Physijs.DOFConstraint
, care este un grad de libertate de constrângere pentru a fi atașat la caroseria vehiculului păstrând în același timp capacitatea de a se roti independent. Vă invit să citiți documentația oficială cu privire la diferitele constrângeri disponibile în Physijs.
Corpul motorului și caroseria sunt simple BoxMesh
obiecte precum sol
prezentate mai sus, dar cu o valoare definită a masei. Acestea sunt conectate între ele folosind o articulație articulată, după cum se arată în codul următor. O articulație articulată restricționează mișcarea obiectului conectat, ca cea a unei uși normale.
car.carriage_constraint = new Physijs.HingeConstraint (autoturism, // primul obiect care urmează să fie constrâns car.body, // constrâns la acest nou THREE.Vector3 (6, 0, 0), // la acest punct nou THREE.Vector3 (0, 1, 0) // de-a lungul acestei axe); scene.addConstraint (car.carriage_constraint); car.carriage_constraint.setLimits (-Math.PI / 3, // unghiul minim de mișcare, în radiani Math.PI / 3, // unghiul maxim de mișcare, în radiani 0, // aplicat ca factor de eroare de constrângere 0 / / controlează sări la limită (0.0 == fără sări);
A doua parte a codului aplică limite la rotirea balamalei, care este în acest caz între -Math.PI / 3
și Math.PI / 3
.
Roțile folosesc o constrângere de gradul de libertate care poate fi utilizată pentru a stabili limite atât la mișcarea liniară cât și la mișcarea unghiulară în toate cele trei axe. O metodă addWheel
este creat pentru adăugarea de roți, care implică mai mulți parametri.
Parametrii sunt poziția roții, greutatea roții, dacă roata este mare sau mică și obiectul de referință al roții. Metoda returnează un nou creat DOFConstraint
, care este utilizat pentru conducerea vehiculului.
funcția addWheel (roată, pos, esteBig, greutate) var geometry = wheel_geometry; dacă (esteBig) geometry = big_wheel_geometry; roată = masă nouă Physijs.CylinderMesh (geometrie, material de roată, greutate); wheel.name = "coș"; wheel.rotation.x = Math.PI / 2; wheel.position.set (pos.x, pos.y, pos.z); wheel.castShadow = adevărat; scene.add (roată); wheel.setDamping (0, amortizare); var wheelConstraint = nou Physijs.DOFConstraint (roată, car.body, pos); dacă (isBig) wheelConstraint = new Physijs.DOFConstraint (roată, autoturism, pos); scene.addConstraint (wheelConstraint); wheelConstraint.setAngularLowerLimit (x: 0, y: 0, z: 0); wheelConstraint.setAngularUpperLimit (x: 0, y: 0, z: 0); roată de întoarcere;
Roțile mari trebuie atașate la cărucior, iar roțile mici sunt atașate la motor. Roțile primesc o valoare de amortizare, astfel încât viteza lor unghiulară se reduce atunci când nu se aplică nici o forță exterioară. Acest lucru face ca vehiculul să încetinească atunci când eliberăm acceleratorul.
Vom explora logica conducerii într-o secțiune ulterioară. Fiecare plasă de roată, împreună cu ochiurile de plasă ale autovehiculului, poartă numele de cart
pentru identificare în timpul apelului de coliziune. Fiecare constrângere a roților are limite unghiulare diferite, care sunt setate independent odată ce acestea sunt create. De exemplu, aici este codul roții din față mediană a motorului, car.wheel_fm
, și constrângerea corespunzătoare, car.wheel_fm_constraint
.
car.wheel_fm_constraint = addWheel (car.wheel_fm, noul THREE.Vector3 (-7.5, 6.5, 0), false, 300); car.wheel_fm_constraint.setAngularLowerLimit (x: 0, y: -Math.PI / 8, z: 1); car.wheel_fm_constraint.setAngularUpperLimit (x: 0, y: Math.PI / 8, z: 0);
Mingea este a Physijs.SphereMesh
obiect cu o valoare de masă mai mică de 20
. Noi folosim releaseBall
metoda de a poziționa mingea în mod aleatoriu în zona noastră de joc ori de câte ori se colectează.
funcția addBall () var ball_material = Physijs.createMaterial (noul TRI.MeshStandardMaterial (culoare: 0x0000ff, umbrire: THREE.FlatShading), frecare, .9 / bun restituție); var ball_geometry = Noua Trei ScăriGeometrie (2,16,16); minge = noi Physijs.SphereMesh (ball_geometry, ball_material, 20); ball.castShadow = adevărat; releaseBall (); scene.add (minge); ball.setDamping (0,0.9); ball.addEventListener ("coliziune", onCollision); funcția releaseBall () var domeniu = 10 + Math.random () * 30; ball.position.y = 16; ball.position.x = ((2 * Math.floor (Math.random () * 2)) - 1) * interval; ball.position.z = ((2 * Math.floor (Math.random () * 2)) - 1) * interval; minge .__ dirtyPosition = true; // forțarea unei noi poziții // De asemenea, trebuie să anulați viteza obiectului ball.setLinearVelocity (new THREE.Vector3 (0, 0, 0)); ball.setAngularVelocity (nou THREE.Vector3 (0, 0, 0));
Un lucru demn de remarcat este faptul că trebuie să depășim valorile poziției stabilite de simularea fizică pentru a repoziționa mingea. Pentru aceasta, folosim __dirtyPosition
care asigură faptul că noua poziție este utilizată pentru simulare fizică suplimentară.
Mingea se colectează când se ciocnește cu orice parte a vehiculului care se întâmplă în onCollision
metoda ascultătorului.
funcția onCollision (other_object, linear_velocity, angular_velocity) if (other_object.name === "cart") scor ++; releaseBall (); scoreText.innerHTML = score.toString ();
Adăugăm ascultători de evenimente pentru onkeydown
și onkeyup
evenimente din document, unde determinăm cod cheie
pentru a seta valorile constrângerilor de roată corespunzătoare. Teoria este că roata frontală unică a motorului controlează rotirea vehiculului, iar cele două roți din spatele motorului acționează accelerația și decelerarea. Roțile de pe cărucior nu joacă nici un rol în conducere.
document.onkeydown = handleKeyDown; document.onkeyup = handleKeyUp; funcția handleKeyDown (keyEvent) comutator (keyEvent.keyCode) caz 37: // Stânga car.wheel_fm_constraint.configureAngularMotor (1, -Math.PI / 3, Math.PI / 3, 1, 200); car.wheel_fm_constraint.enableAngularMotor (1); pauză; cazul 39: // dreapta car.wheel_fm_constraint.configureAngularMotor (1, -Math.PI / 3, Math.PI / 3, -1, 200); car.wheel_fm_constraint.enableAngularMotor (1); pauză; cazul 38: // Sus car.wheel_bl_constraint.configureAngularMotor (2, 1, 0, 6, 2000); car.wheel_br_constraint.configureAngularMotor (2, 1, 0, 6, 2000); car.wheel_bl_constraint.enableAngularMotor (2); car.wheel_br_constraint.enableAngularMotor (2); pauză; cazul 40: // jos car.wheel_bl_constraint.configureAngularMotor (2, 1, 0, -6, 2000); car.wheel_br_constraint.configureAngularMotor (2, 1, 0, -6, 2000); car.wheel_bl_constraint.enableAngularMotor (2); car.wheel_br_constraint.enableAngularMotor (2); pauză; funcția handleKeyUp (keyEvent) comutator (keyEvent.keyCode) caz 37: // Stânga car.wheel_fm_constraint.disableAngularMotor (1); pauză; cazul 39: // dreapta car.wheel_fm_constraint.disableAngularMotor (1); pauză; cazul 38: // Sus car.wheel_bl_constraint.disableAngularMotor (2); car.wheel_br_constraint.disableAngularMotor (2); pauză; cazul 40: // jos car.wheel_bl_constraint.disableAngularMotor (2); car.wheel_br_constraint.disableAngularMotor (2); pauză;
DOFConstraint
utilizează enableAngularMotor
metoda de aplicare a vitezei unghiulare pe roată care transformă roata pe baza valorii axei furnizate ca parametru. Practic, rotim doar roțile, iar mișcarea vehiculului se întâmplă din cauza răspunsului la frecare al solului, ca și în lumea reală.
Nu putem încorpora jocul de lucru în această pagină așa cum am menționat la începutul tutorialului. Treceți prin sursa completă pentru a înțelege cum este conectat totul.
Aceasta este o introducere foarte simplă a aplicării fizicii în lumea 3D Three.js. Documentația Physijs lipsește foarte mult, dar există deja multe exemple care merită să fie analizate. Physijs este un cadru foarte ușor de început, cum ar fi Three.js, când vă gândiți la complexitatea prezentă sub capotă.
JavaScript este în mod evident popular pentru dezvoltarea jocurilor, precum și pentru dezvoltarea web-ului. Nu este fără curbele sale de învățare, și există o mulțime de cadre și biblioteci pentru a vă menține ocupați. Dacă sunteți în căutarea unor resurse suplimentare pentru a studia sau a utiliza în munca dvs., verificați ce avem la dispoziție pe piața Envato.
Sper că acest tutorial vă ajută să începeți să explorați lumea interesantă a fizicii 3D a jocurilor web.