Platforma web a înregistrat o creștere extraordinară în ultima vreme cu ajutorul HTML5, WebGL și a creșterii puterii generației actuale de dispozitive. Acum, dispozitivele mobile și browserele sunt capabile să furnizeze conținut de înaltă performanță atât în 2D, cât și în 3D. Familiaritatea JavaScript (JS) ca limbaj de scripting a fost, de asemenea, un factor de conducere, după decesul platformei web Flash.
Majoritatea dezvoltatorilor de web-uri sunt bine conștienți de cât de complicat este ecosistemul JS cu toate cadrele și standardele disponibile, care ar putea uneori să fie copleșitoare pentru un nou dezvoltator. Dar când vine vorba de 3D, alegerile sunt simple, datorită domnului Doo. Setul său Three.js este în prezent cea mai bună opțiune acolo pentru a crea conținut 3D WebGL performant. O altă alternativă puternică este Babylon.js, care ar putea fi de asemenea folosită pentru a face jocuri 3D.
În acest tutorial, veți învăța să creați un simplu joc fără precedent în stilul de joc nativ 3D folosind cadrul puternic Three.js. Utilizați tastele săgeată pentru a controla o bulgăre de zăpadă care se rostogolește pe o parte a muntelui, pentru a evita arborii în calea ta. Nu există o artă implicată, iar toate imaginile sunt create în cod.
Envato Tuts + are deja cateva tutoriale care te-ar putea incepe cu Three.js. Iată câteva dintre ele pentru a începe.
Să facem mai întâi o scenă 3D de bază, așa cum se arată aici, unde există un cub rotativ. Puteți utiliza trageți mouse-ul pentru a orbita în jurul cubului.
Orice grafic afișat pe un ecran bidimensional are practic 2D în natură, cu câteva elemente importante care oferă iluzia 3D: iluminarea, umbrirea, umbrele și magia de proiectare 3D până la 2D care se întâmplă prin intermediul camerei. În scenia de mai sus, permitem iluminarea eficientă utilizând aceste linii de cod.
camera = noua cameră THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // perspectivă renderer cameră = nouă THREE.WebGLRenderer (alpha: true); // renderer cu backdrop transparent renderer.shadowMap.enabled = true; // enable shadow renderer.shadowMap.type = THREE.PCFSoftShadowMap; // ... erou = nou TREI.Mesh (eroGeometrie, heroMaterial); hero.castShadow = true; hero.receiveShadow = false; // ... ground.receiveShadow = adevărat; ground.castShadow = false; // ... sun = nouă lumină tridimensională (0xffffff, 0,8); poziția sun.position (0,4,1); sun.castShadow = adevărat; scene.add (soare); // Configurați proprietățile umbrelor pentru lumina soarelui sun.shadow.mapSize.width = 256; sun.shadow.mapSize.height = 256; sun.shadow.camera.near = 0.5; sun.shadow.camera.far = 50;
renderer
trebuie să aibă shadowMap
activat, scena trebuie să aibă o lumină cu castShadow
activat și toate obiectele 3D au nevoie castShadow
și receiveShadow
proprietățile stabilite corespunzător. Pentru ca umbrele să se producă, ar trebui să folosim și MeshStandardMaterial
sau un material mai bogat în caracteristici pentru obiectele noastre 3D. Camera este controlată cu ajutorul scriptului OrbitControls. Aș recomanda să jucați cu scena 3D de bază prin adăugarea de forme mai primitive sau prin joc cu iluminare etc. înainte de a continua cu tutorialul.
Există multe tipuri de jocuri fără sfârșit, iar a noastră este o "rolă nesfârșită". Vom crea un joc în care un bulgăre de zăpadă se rostogolește pe o suprafață montană nesfârșită, unde folosim săgețile pentru a evita arborii care intră. Un lucru interesant este că acest joc simplu nu va implica niciun fel de elemente de artă, deoarece toate componentele ar fi create prin cod. Aici este jocul complet de jucat.
Principalele componente sau elemente ale jocului sunt:
Vom explora fiecare dintre acestea unul câte unul în următoarea secțiune.
ceaţă
este o proprietate a scenei 3D din Trei. Este întotdeauna un truc ușor de folosit pentru a simula adâncimea sau pentru a arăta un orizont. Culoarea ceață este importantă pentru ca iluzia să funcționeze corect și depinde de culoarea scenei și de iluminare. După cum puteți vedea în codul de mai jos, setăm de asemenea renderer
„s clearColor
valoare pentru a fi aproape de culoarea ceaţă
.
scenă = nouă TRIȘIUNI (); scene.fog = nou THREE.FogExp2 (0xf0fff0, 0.14); camera = nouă cameră THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // vizualizator de cameră cu perspectivă = new THREE.WebGLRenderer (alpha: true; // // renderer cu backdrop transparent renderer.setClearColor (0xfffafa, ;
Pentru a potrivi ambianța, folosim, de asemenea, valori similare de culoare pentru luminile utilizate în scenă. Fiecare culoare înconjurătoare este o nuanță de culoare albă, care coagulează împreună pentru a crea efectul necesar.
var hemisphereLight = noua lumină THREE.HemisphereLight (0xfffafa, 0x000000, .9) scene.add (hemisphereLight); soare = nouă lumină tridimensională (0xcdc1c5, 0.9); poziția sun.position (12,6, -7); sun.castShadow = adevărat; scene.add (soare);
Bătața noastră de zăpadă este a DodecahedronGeometry
trei forme primitive create după cum se arată mai jos.
sferă varăGeometrie = nouă TREI.DecodectronăGeometrie (heroRadius, 1); var sphereMaterial = nou TRI.MeshStandardMaterial (culoare: 0xe5f2f2, umbrire: THREE.FlatShading) heroSphere = nou TRE.Mesh (sferaGeometrie, sferaMaterial);
Pentru toate elementele 3D din acest joc pe care le folosim THREE.FlatShading
pentru a obține aspectul dorit de poli-scăzut.
Țara de defilare numită rollingGroundSphere
este mare SphereGeometry
primitiv, și îl rotim pe X
axa pentru a crea iluzia terenului în mișcare. Blocul de zăpadă nu se rostogolește cu nimic; noi creăm doar iluzia prin menținerea sferei de la sol în timp ce păstrăm mingia de zăpadă staționară.
O primitivă sferică normală va arăta foarte netedă și, prin urmare, nu va asigura robustețea necesară pentru panta montană. Așa că facem unele manipulări de vârfuri pentru a schimba suprafața sferei netede într-un teren accidentat. Aici este codul corespunzător urmat de o explicație.
var laturi = 40; var tiers = 40; sferă varăGeometrie = nouă treime geometrie (lumeRadius, laturi, niveluri); var sphereMaterial = nou TRI.MeshStandardMaterial (culoare: 0xfffafa, umbrire: THREE.FlatShading) var vertexIndex; var vertexVector = nou THREE.Vector3 (); var următorulVertexVector = nou THREE.Vector3 (); var firstVertexVector = nou THREE.Vector3 (); var offset = nou THREE.Vector3 (); var currentTier = 1; var lerpValue = 0,5; var heightValue; var maxHeight = 0,07; pentru (var j = 1;Creați o primă sferică cu 40 de segmente orizontale (
fete
) și 40 de segmente verticale (niveluri
). Fiecare vertex al unei geometrii a trei poate fi accesat prinnoduri
proprietate array. Vom trece prin toate nivelurile între vârfurile extreme de sus și cele de jos extreme pentru a face manipulările noastre de vârfuri. Fiecare nivel al geometriei sferei conține exactfete
numărul de vârfuri, care formează un inel închis în jurul sferei.Primul pas este să rotiți fiecare inel ciudat de vârfuri pentru a rupe uniformitatea conturului suprafeței. Deplasăm fiecare vârf din inel cu o fracțiune aleatorie între 0,25 și 0,75 de la distanța până la următorul vârf. Drept urmare, nodurile verticale ale sferei nu mai sunt aliniate în linie dreaptă și avem un contur zigzag frumos.
Ca a doua etapă, oferim fiecărui vârf o ajustare aleatoare a înălțimii aliniată la normal la vârf, indiferent de nivelul de care aparține. Aceasta are ca rezultat o suprafață neuniformă și accidentată. Sper că matematica vectorială folosită aici este simplă odată ce consideri că centrul sferei este considerat originea
(0,0)
.Copacii
Arborii apar în afara căii noastre de rulare pentru a adăuga adâncime lumii și înăuntru ca obstacole. Crearea arborelui este un pic mai complicat decât terenul accidentat, dar urmează aceeași logică. Noi folosim a
ConeGeometry
primitiv pentru a crea partea verde de sus a copacului și aCylinderGeometry
pentru a crea partea inferioară a trunchiului.Pentru partea de sus, buclele prin fiecare nivel de vârfuri și extindeți inelul de vârfuri, urmată de micșorarea următorului inel. Următorul cod arată
blowUpTree
metoda folosită pentru a extinde inelul alternativ de vârfuri spre exterior șitightenTree
metoda folosită pentru a micșora următorul inel de vârfuri.funcția createTree () var sides = 8; var tiers = 6; var scalarMultiplier = (Math.random () * (0,25-0,1)) + 0,05; var midPointVector = nou THREE.Vector3 (); var vertexVector = nou THREE.Vector3 (); var treeGeometrie = nouă GESometrie triunghiulară (0,5, 1, laturi, niveluri); var treeMaterial = nou TRI.MeshStandardMaterial (culoare: 0x33ff33, umbrire: THREE.FlatShading); var offset; midPointVector = treeGeometry.vertices [0] .clone (); var currentTier = 0; var vertexIndex; blowUpTree (treeGeometry.vertices, fete, 0, scalarMultiplier); tightenTree (treeGeometry.vertices, fete, 1); blowUpTree (treeGeometry.vertices, fete, 2, scalarMultiplier * 1.1, true); tightenTree (treeGeometry.vertices, fete, 3); blowUpTree (treeGeometry.vertices, fete, 4, scalarMultiplier * 1.2); tightenTree (treeGeometry.vertices, fete, 5); var treeTop = nou THREE.Mesh (treeGeometry, treeMaterial); treeTop.castShadow = true; treeTop.receiveShadow = false; treeTop.position.y = 0,9; treeTop.rotation.y = (Math.random () * (Math.PI)); var treeTrunkGeometry = noua gaometrie de trei ori (0,1, 0,1,0,5); var trunkMaterial = nou THREE.MeshStandardMaterial (culoare: 0x886633, umbrire: THREE.FlatShading); var treeTrunk = TREE.Mesh (TreeTrunkGeometrie, trunkMaterial); treeTrunk.position.y = 0,25; var tree = nou THREE.Object3D (); tree.add (treeTrunk); tree.add (treetop); retur copac; funcția blowUpTree (vârfuri, laturi, currentTier, scalarMultiplier, ciudat) var vertexIndex; var vertexVector = nou THREE.Vector3 (); var midPointVector = noduri [0] .clone (); var offset; pentru (var i = 0; i
blowUpTree
metoda împinge fiecare vertex alternativ într-un inel de vârfuri, păstrând în același timp celelalte noduri din inel la o înălțime mai mică. Acest lucru creează ramurile cu vârfuri arborice. Dacă vom folosi vârfurile ciudate într-un singur nivel, vom folosi vârfurile uniforme din următorul nivel, astfel încât uniformitatea să fie întreruptă. Odată ce se formează arborele complet, îi oferim o rotație aleatorie pe axa y pentru a face să pară puțin diferită.Efectul de explozie
Efectul de explozie a pixelilor bloc nu este cel mai elegant pe care l-am putea folosi, dar cu siguranță se comportă bine. Acest efect particular de particule este de fapt o geometrie 3D care este manipulată pentru a arăta ca un efect care utilizează
THREE.Points
clasă.funcția addExplosion () particleGeometry = noua GESometrie (); pentru (var i = 0; i < particleCount; i ++ ) var vertex = new THREE.Vector3(); particleGeometry.vertices.push( vertex ); var pMaterial = new THREE.ParticleBasicMaterial( color: 0xfffafa, size: 0.2 ); particles = new THREE.Points( particleGeometry, pMaterial ); scene.add( particles ); particles.visible=false; function explode() particles.position.y=2; particles.position.z=4.8; particles.position.x=heroSphere.position.x; for (var i = 0; i < particleCount; i ++ ) var vertex = new THREE.Vector3(); vertex.x = -0.2+Math.random() * 0.4; vertex.y = -0.2+Math.random() * 0.4 ; vertex.z = -0.2+Math.random() * 0.4; particleGeometry.vertices[i]=vertex; explosionPower=1.07; particles.visible=true; function doExplosionLogic()//called in update if(!particles.visible)return; for (var i = 0; i < particleCount; i ++ ) particleGeometry.vertices[i].multiplyScalar(explosionPower); if(explosionPower>1.005) exploionPower- = 0.001; altceva particles.visible = false; particleGeometry.verticesNeedUpdate = true;
addExplosion
metoda adaugă 20 de vârfuri lanoduri
array of theparticleGeometry
.exploda
metoda se numește atunci când avem nevoie de efectul de a alerga, care poziționează aleator fiecare vârf al geometriei.doExplosionLogic
este chemat înActualizați
dacă obiectul particulei este vizibil, unde mutăm fiecare vârf spre exterior. Fiecare vârf din apuncte
obiect devine redat ca un bloc pătrat.4. Modul de joc
Acum, că știm cum să creăm fiecare dintre elementele necesare jocului, să intrăm în joc. Principalele elemente de joc sunt:
- jocul de buzunar
- plasarea copacilor
- interacțiunea utilizatorului
- detectarea coliziunii
Să analizăm detaliile.
Jocul Loop
Toți mecanicii jocului de bază se întâmplă în bucla de joc, care în cazul nostru este
Actualizați
metodă. Noi o numim pentru prima dată dininit
metoda, care devine apelată la încărcarea ferestrelor. După aceasta, se cuplează pe bucla de randare a documentelor utilizândrequestAnimationFrame
astfel încât să se numească repetat.actualizare functie () rollingGroundSphere.rotation.x + = rollingSpeed; heroSphere.rotation.x - = heroRollingSpeed; în cazul în care (heroSphere.position.y<=heroBaseY) jumping=false; bounceValue=(Math.random()*0.04)+0.005; heroSphere.position.y+=bounceValue; heroSphere.position.x=THREE.Math.lerp(heroSphere.position.x,currentLane, 2*clock.getDelta());//clock.getElapsedTime()); bounceValue-=gravity; if(clock.getElapsedTime()>treeReleaseInterval) clock.start (); addPathTree (); dacă ! hasCollided) scorul + = 2 * treeReleaseInterval; scoreText.innerHTML = score.toString (); doTreeLogic (); doExplosionLogic (); face(); requestAnimationFrame (actualizare); // cere noua actualizare function render () renderer.render (scena, camera); // drawÎn
Actualizați
, noi numimface
care utilizeazărenderer
pentru a desena scena. Noi numimdoTreeLogic
care verifică coliziunea și, de asemenea, scoate copacii după ce au ieșit din vedere.Bilele de zăpadă și sferele de pământ se rotesc în timp ce adăugăm, de asemenea, o logică aleatorie la balonul de zăpadă. Arborii noi sunt plasați în cale prin apel
addPathTree
după ce a trecut un timp prestabilit. Timpul este urmărit folosind aTHREE.Clock
obiect. De asemenea, actualizămscor
cu excepția cazului în care a survenit o coliziune.Plasarea copacilor
Un set de copaci este plasat în afara căii ferate pentru a crea lumea folosind
addWorldTrees
metodă. Toți copacii sunt adăugați ca un copil al familieirollingGroundSphere
astfel încât să se miște și atunci când rotim sfera.funcția addWorldTrees () var numTrees = 36; var gap = 6,28 / 36; pentru (var i = 0; iPentru a planta copacii lumii, numim
addTree
prin trecerea valorilor în jurul circumferinței sferei noastre pământești.sphericalHelper
utilitate ne ajută să găsim poziția pe suprafața unei sfere.Pentru a planta copaci pe traseu, vom folosi o piscina de copaci care sunt create la inceput folosind
createTreesPool
metodă. De asemenea, avem valori predefinite ale unghiului pentru fiecare cale din sfera stocată înpathAngleValues
mulțime.pathAngleValues = [1.52,1.57,1.62]; // ... funcția createTreesPool () var maxTreesInPool = 10; var newTree; pentru (var i = 0; i0.5) Lane = Math.floor (Math.random () * 2); addTree (true, opțiunile [banda]);
addPathTree
metoda este chemată din actualizare când a trecut suficient timp după plantarea ultimului copac. La rândul său, îl cheamăaddTree
metoda prezentată mai devreme cu un alt set de parametri în care arborele este plasat în calea selectată.doTreeLogic
metoda va returna copacul în piscină odată ce acesta nu mai este vizibil.Interacțiunea cu utilizatorul
Adăugăm un document de ascultător pentru a căuta evenimente de tastatură relevante.
handleKeyDown
metoda stabileștecurrentLane
dacă tastele săgeată din dreapta sau din stânga sunt apăsate sau seteazăbounceValue
dacă este apăsată săgeata în sus.document.onkeydown = handleKeyDown; // ... handleKeyDown (keyEvent) if (jumping) retur; var validMove = true; dacă (keyEvent.keyCode === 37) // a plecat dacă (currentLane == middleLane) currentLane = leftLane; altfel dacă (currentLane == rightLane) currentLane = middleLane; altfel validMove = false; altfel dacă (keyEvent.keyCode === 39) // dreapta dacă (currentLane == middleLane) currentLane = rightLane; altfel dacă (currentLane == leftLane) currentLane = middleLane; altfel validMove = false; altceva if (keyEvent.keyCode === 38) // sus, salt bounceValue = 0.1; sărituri = true; validMove = false; dacă (validMove) jumping = true; bounceValue = 0,06;În
Actualizați
,X
poziția mingii noastre de zăpadă crește încet pentru a ajunge lacurrentLane
poziția acolo prin comutarea benzilor.Detectarea coliziunii
Nu există o fizică reală implicată în acest joc, deși am putea folosi diferite cadre fizice pentru scopul nostru de detectare a coliziunii. Dar, după cum știți, un motor de fizică adaugă la jocul nostru o mulțime de performanțe și ar trebui să încercăm întotdeauna să vedem dacă putem evita acest lucru.
În cazul nostru, se calculează distanța dintre globul nostru de zăpadă și fiecare copac pentru a declanșa o coliziune dacă acestea sunt foarte apropiate. Acest lucru se întâmplă în
doTreeLogic
metoda, care este chemat de laActualizați
.funcția doTreeLogic () var oneTree; var treePos = nou THREE.Vector3 (); arboreleInPath.forEach (funcție (element, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); if (treePos.distanceTo (heroSphere.position)<=0.6) console.log("hit"); hasCollided=true; explode(); ); //…După cum probabil ați observat, toți copacii prezenți în prezent în calea noastră sunt stocați în
treesInPath
matrice.doTreeLogic
De asemenea, metoda elimină arborii de pe ecran și în piscină odată ce au ieșit din viziunea noastră folosind codul de mai jos.var copaciToRemove = []; copaciInPath.forEach (functie (element, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); daca (treePos.z> 6 && oneTree.visible) // plecat din zona noastra de vedere tree treesToRemove.push (un copac); ); var dincolo; treesToRemove.forEach (funcție (element, index) oneTree = treesToRemove [index]; fromWhere = treesInPath.indexOf (oneTree); treesInPath.splice (fromWhere, 1); copaciPool.push (oneTree); oneTree.visible = false; .log ("eliminați copacul"););Concluzie
Crearea unui joc 3D este un proces complicat dacă nu folosiți un instrument vizual cum ar fi Unitatea. S-ar părea intimidantă sau copleșitoare, dar permiteți-mi să vă asigur că, odată ce veți fi atârnat de ea, veți simți mult mai puternic și mai creativ. Aș dori să explorați în continuare folosind diverse cadre fizice sau sisteme de particule sau exemple oficiale.