Nettplattformen har hatt en enorm vekst i nyere tid ved hjelp av HTML5, WebGL og den økte kraften i den nåværende generasjonen av enheter. Nå er mobile enheter og nettlesere i stand til å levere høypresterende innhold både i 2D og 3D. Forståelsen av JavaScript (JS) som skriptspråk har også vært en drivende faktor etter at Flash-plattformen har gått ned..
De fleste webutviklere er godt klar over hvor komplisert JS økosystemet er med alle de ulike rammene og standardene som er tilgjengelige, noe som noen ganger kan være overveldende for en ny utvikler. Men når det gjelder 3D, er valgene enkle, takk til Mr.Doob. Hans Three.js er for tiden det beste alternativet der ute for å skape høyverdig 3D WebGL-innhold. Et annet kraftig alternativ er Babylon.js, som også kan brukes til å lage 3D-spill.
I denne opplæringen lærer du å lage et enkelt, uendelig runner-stil, innfødt 3D-spill med det kraftige Three.js-rammeverket. Du vil bruke piltastene til å styre en snøball som ruller nedover en fjellside for å unnvike trærne i veien. Det er ingen kunst involvert, og alle visualer er opprettet i kode.
Envato Tuts + har allerede noen opplæringsprogrammer som kan komme i gang med Three.js. Her er noen av dem for å komme i gang.
La oss lage en grunnleggende 3D-scene først, som vist her hvor det er en roterende terning. Du kan bruke musen til å bane rundt kuben.
Enhver grafikk som vises på en todimensjonal skjerm, er praktisk talt 2D i naturen, med noen få viktige elementer som gir 3D-illusjonen: belysningen, skyggen, skyggene og 3D til 2D-projeksjonens magi som skjer via kameraet. I den ovennevnte scenen aktiverer vi effektiv belysning ved hjelp av disse kodelinjene.
kamera = nytt THREE.PerspectiveCamera (60, scenevidde / sceneHeight, 0,1, 1000); // perspektiv kamera renderer = nytt THREE.WebGLRenderer (ala: true); // renderer med gjennomsiktig bakgrunn renderer.shadowMap.enabled = true; // aktiver skygge renderer.shadowMap.type = THREE.PCFSoftShadowMap; // ... hero = nytt THREE.Mesh (heroGeometry, heroMaterial); hero.castShadow = true; hero.receiveShadow = false; // ... ground.receiveShadow = true; ground.castShadow = false; // ... sol = ny THREE.DirectionalLight (0xffffff, 0.8); sun.position.set (0,4,1); sun.castShadow = true; scene.add (sol); // Sett opp skyggeegenskaper for sollyset sun.shadow.mapSize.width = 256; sun.shadow.mapSize.height = 256; sun.shadow.camera.near = 0.5; sun.shadow.camera.far = 50;
De renderer
må ha shadowMap
aktivert, scenen må ha et lys med kaste skygge
aktivert, og alle 3D-objekter trenger kaste skygge
og receiveShadow
eiendommer satt riktig. For riktig skygge skal skje, bør vi også bruke MeshStandardMaterial
eller et mer funksjonsrikt materiale for våre 3D-objekter. Kameraet styres ved hjelp av det skarpe OrbitControls-skriptet. Jeg vil anbefale å spille med den grunnleggende 3D-scenen ved å legge til flere primitive figurer eller spille med belysningen, etc. før du fortsetter med opplæringen.
Det finnes mange typer uendelige løpespill, og vår er en "endeløs rulle". Vi vil skape et spill hvor en snøball ruller ned en uendelig fjellside der vi bruker piltastene til å unnvike de innkommende trærne. En interessant ting er at dette enkle spillet ikke vil involvere noen kunstverdier, da alle komponentene vil bli opprettet med kode. Her er hele spillet å leke rundt.
Hovedkomponentene eller elementene i spillet er:
Vi vil utforske hver av disse en etter en i følgende avsnitt.
De tåke
er en egenskap av 3D-scenen i Tre. Det er alltid et praktisk triks å bruke for å simulere dybden eller vise en horisont. Tåkenes farge er viktig for at illusjonen skal fungere skikkelig og avhenger av fargene på scenen og belysningen. Som du kan se i koden nedenfor, setter vi også inn renderer
's clearColor
verdi å være nær fargen på tåke
.
scene = nytt THREE.Scene (); scene.fog = nytt THREE.FogExp2 (0xf0fff0, 0,14); kamera = nytt THREE.PerspectiveCamera (60 sceneVidth / sceneHeight, 0,1, 1000); // perspektiv kamera renderer = nytt THREE.WebGLRenderer (ala: true); // renderer med gjennomsiktig bakgrunn renderer.setClearColor (0xfffafa, 1) ;
For å matche atmosfæren bruker vi også lignende fargeverdier til lysene som brukes i scenen. Hver omgivende farge er en annen nyanse av hvitt som geler sammen for å skape den nødvendige effekten.
var hemisphereLight = new THREE.HemisphereLight (0xfffafa, 0x000000, .9) scene.add (hemisphereLight); sol = nytt tre DIREKTE LYS (0xcdc1c5, 0,9); solstilling (12,6, -7); sun.castShadow = true; scene.add (sol);
Vår snøball er en DodecahedronGeometry
tre primitiv form opprettet som vist nedenfor.
var sphereGeometry = ny THREE.DodecahedronGeometry (heroRadius, 1); var sphereMaterial = nytt THREE.MeshStandardMaterial (color: 0xe5f2f2, shading: THREE.FlatShading) heroSphere = nytt THREE.Mesh (sphereGeometry, sphereMaterial);
For alle 3D-elementene i dette spillet bruker vi THREE.FlatShading
for å få ønsket lavpoly-utseende.
Den rullende bakken heter rollingGroundSphere
er en stor SphereGeometry
primitiv, og vi roterer den på x
akse for å lage den bevegelige bakken illusjonen. Snøballen ruller egentlig ikke over noe; Vi skaper bare illusjonen ved å holde bakken sfærende og samtidig holde snøballen stasjonær.
En normal sfære primitiv vil se veldig jevnt ut og vil derfor ikke gi den nødvendige robustheten som trengs for fjellhellingen. Så vi gjør noen vertex manipulasjoner for å forandre glatt overflate til et robust terreng. Her er den tilhørende koden etterfulgt av en forklaring.
var sider = 40; var tiers = 40; var sphereGeometry = new THREE.SphereGeometry (worldRadius, sides, tiers); var sphereMaterial = nytt THREE.MeshStandardMaterial (color: 0xfffafa, shading: THREE.FlatShading) var vertexIndex; var vertexVector = nytt THREE.Vector3 (); var nextVertexVector = nytt THREE.Vector3 (); var firstVertexVector = nytt THREE.Vector3 (); var offset = nytt THREE.Vector3 (); var currentTier = 1; var lerpValue = 0,5; var heightValue; var maxHeight = 0,07; for (var j = 1; jVi lager en primitiv sfære med 40 horisontale segmenter (
sider
) og 40 vertikale segmenter (lag
). Hvert toppunkt av en tre geometri kan nås viatoppunkter
array eiendom. Vi går gjennom alle tiers mellom de ekstreme topp- og ekstreme bunnpunktene for å gjøre våre vertexmanipulasjoner. Hvert nivå av kule geometrien inneholder nøyaktigsider
Antall kryss, som danner en lukket ring rundt sfæren.Det første trinnet er å rotere hver merkelig ring av vertices for å bryte enhetligheten av overflatekonturene. Vi beveger hvert toppunkt i ringen med en tilfeldig fraksjon mellom 0,25 og 0,75 av avstanden til neste toppunkt. Som et resultat av dette er kuglens vertikale hjørner ikke justert i en rett linje lenger, og vi får en fin sikksagkontur.
Som det andre trinnet gir vi hvert toppunkt en tilfeldig høydejustering som er justert i forhold til normal i vertexet, uavhengig av hvilken nivå den tilhører. Dette resulterer i en ujevn og robust overflate. Jeg håper vektormatematikken som brukes her, er grei når du vurderer at senterets senter betraktes som opprinnelsen
(0,0)
.Trærne
Trærne ser ut utenfor vårt rullende spor for å legge dybde til verden, og innvendig som hindringer. Å lage treet er litt mer komplisert enn den robuste bakken, men følger den samme logikken. Vi bruker en
ConeGeometry
primitiv for å lage den øverste grønne delen av treet og aCylinderGeometry
å lage den nederste delen av kofferten.For den øverste delen går vi gjennom hvert lag av krysser og utvider ringenes ring etterfulgt av krymping ned neste ring. Følgende kode viser
blowUpTree
Metode som brukes til å utvide den alternative ringen av vinkler utover ogtightenTree
Metoden brukes til å krympe ned neste ring av hjørner.funksjon createTree () var sides = 8; var tiers = 6; var scalarMultiplier = (Math.random () * (0,25-0,1)) + 0,05; var midPointVector = nytt THREE.Vector3 (); var vertexVector = nytt THREE.Vector3 (); var treeGeometry = nye THREE.ConeGeometry (0,5, 1, sider, nivåer); var treeMaterial = nytt THREE.MeshStandardMaterial (color: 0x33ff33, shading: THREE.FlatShading); var offset; midPointVector = treeGeometry.vertices [0] .clone (); var currentTier = 0; var vertexIndex; blowUpTree (treeGeometry.vertices, sider, 0, scalarMultiplier); tightenTree (treeGeometry.vertices, sider, 1); blowUpTree (treeGeometry.vertices, sider, 2, scalarMultiplier * 1,1, true); tightenTree (treeGeometry.vertices, sider, 3); blowUpTree (treeGeometry.vertices, sider, 4, scalarMultiplier * 1,2); tightenTree (treeGeometry.vertices, sider, 5); var treeTop = nytt THREE.Mesh (treeGeometry, treeMaterial); treeTop.castShadow = true; treeTop.receiveShadow = false; treeTop.position.y = 0,9; treeTop.rotation.y = (Math.random () * (Math.PI)); var treeTrunkGeometry = nytt THREE.CylinderGeometry (0,1, 0,1,0,5); var trunkMaterial = nytt THREE.MeshStandardMaterial (color: 0x886633, shading: THREE.FlatShading); var treeTrunk = nytt THREE.Mesh (treeTrunkGeometry, trunkMaterial); treeTrunk.position.y = 0,25; var tree = nytt THREE.Object3D (); tree.add (treetrunk); tree.add (tretopp); tilbake treet; funksjon blowUpTree (vertices, sides, currentTier, scalarMultiplier, oddetall) var vertexIndex; var vertexVector = nytt THREE.Vector3 (); var midPointVector = vertices [0] .clone (); var offset; for (var i = 0; iDe
blowUpTree
Metoden skyver ut hvert alternativ toppunkt i en ring av hjørner, samtidig som de andre kryssene i ringen holdes i en mindre høyde. Dette skaper spisse grener på treet. Hvis vi bruker de ulike sporene i ett nivå, bruker vi de samme punktene i neste nivå slik at ensartetheten blir ødelagt. Når det komplette treet er dannet, gir vi det en tilfeldig rotasjon på y-aksen for å gjøre det litt annerledes.Eksplosjonseffekten
Blokkepixeleksplosjonseffekten er ikke den mest elegante vi kan bruke, men det fungerer sikkert godt. Denne spesielle partikkel-effekten er faktisk en 3D-geometri som manipuleres til å se ut som en effekt ved hjelp av
THREE.Points
klasse.funksjon addExplosion () particleGeometry = new THREE.Geometry (); for (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) eksplosjonPower- = 0.001; ellers particles.visible = false; particleGeometry.verticesNeedUpdate = true;De
addExplosion
Metoden legger til 20 hjørner tiltoppunkter
utvalg avparticleGeometry
. Deeksplodere
Metoden kalles når vi trenger effekten til å løpe, som tilfeldigvis posisjonerer hvert toppunkt av geometrien. DedoExplosionLogic
blir kalt iOppdater
Metode hvis partikkelobjektet er synlig, hvor vi beveger hvert toppunkt utover. Hvert toppunkt i apunkter
objekt blir gjengitt som en firkantet blokk.4. Gameplay
Nå som vi vet hvordan du lager hver av elementene som trengs for spillet, la oss komme inn i spillet. De viktigste spillelementene er:
- spillsløyfen
- Plasseringen av trærne
- brukerinteraksjonen
- kollisjonsdeteksjonen
La oss analysere dem i detalj.
The Game Loop
Alt kjernemekanikeren skjer i spillsløyfen, som i vårt tilfelle er
Oppdater
metode. Vi kaller det for første gang frai det
metode, som blir kalt på vinduets last. Etter dette hooks det på dokumentet gjengeløkken ved hjelp avrequestAnimationFrame
metode slik at det blir kalt flere ganger.funksjon oppdatering () rollingGroundSphere.rotation.x + = rollingSpeed; heroSphere.rotation.x - = heroRollingSpeed; if (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 (); hvis (! harCollided) score + = 2 * treeReleaseInterval; scoreText.innerHTML = score.toString (); doTreeLogic (); doExplosionLogic (); render (); requestAnimationFrame (update); // request next update funksjon gjengivelse () renderer.render (scene, kamera); // drawI
Oppdater
, vi kallergjengi
metode, som brukerrenderer
å tegne scenen. Vi kallerdoTreeLogic
metode, som sjekker for kollisjon og fjerner også trærne når de har gått ut av syn.Snøballet og bakken sfærer blir rotert mens vi også legger til en tilfeldig hoppende logikk til snøballet. Nye trær plasseres i banen ved å ringe
addPathTree
etter at en forhåndsdefinert tid er gått. Tiden spores ved hjelp av aTHREE.Clock
gjenstand. Vi oppdaterer ogsåpoengsum
med mindre en kollisjon har oppstått.Plassering av trærne
Ett sett med trær er plassert utenfor rullestien for å skape verden ved hjelp av
addWorldTrees
metode. Alle trær blir lagt til som barn avrollingGroundSphere
slik at de også beveger seg når vi roterer sfæren.funksjon addWorldTrees () var numTrees = 36; var gap = 6,28 / 36; for (var i = 0; iÅ plante verdenstrær, vi kaller
addTree
Metode ved å overføre verdier rundt omkretsen av vår jordkule. DesphericalHelper
verktøyet hjelper oss med å finne stillingen på overflaten av en sfære.For å plante trær på stien, vil vi gjøre bruk av et basseng av trær som er opprettet ved å begynne å bruke
createTreesPool
metode. Vi har også forhåndsdefinerte vinkelverdier for hver bane på sfæren som er lagret ipathAngleValues
matrise.pathAngleValues = [1.52,1.57,1.62]; // ... funksjon createTreesPool () var maxTreesInPool = 10; var newTree; for (var i = 0; i0,5) lane = Math.floor (Math.random () * 2); addTree (sant, opsjoner [kjørefelt]); De
addPathTree
Metoden kalles fra oppdatering når det er nok tid siden etter planting av det siste treet. Det kalles i sin turaddTree
metode vist tidligere med et annet sett med parametre hvor treet blir plassert i den valgte banen. DedoTreeLogic
Metoden vil returnere treet til bassenget når det går ut av visningen.Brukerinteraksjon
Vi legger til en lytter til dokumentet for å se etter relevante tastaturhendelser. De
handleKeyDown
Metoden settercurrentLane
verdi hvis høyre eller venstre piltast trykkes eller setterbounceValue
verdi hvis opp pil er trykket.document.onkeydown = handleKeyDown; // ... funksjon handleKeyDown (keyEvent) hvis (hopping) tilbake; var validMove = true; hvis (keyEvent.keyCode === 37) // left if (currentLane == middleLane) currentLane = leftLane; annet hvis (currentLane == rightLane) currentLane = middleLane; else validMove = false; annet hvis (keyEvent.keyCode === 39) // rett hvis (currentLane == middleLane) currentLane = rightLane; annet hvis (currentLane == leftLane) currentLane = middleLane; else validMove = false; annet hvis (keyEvent.keyCode === 38) // opp, hopp bounceValue = 0,1; hopping = true; validMove = false; hvis (validMove) jumping = true; bounceValue = 0,06;I
Oppdater
, dex
posisjonen til vår snøball økes sakte for å nåcurrentLane
plasser det ved å bytte baner.Kollisjonsdeteksjon
Det er ingen ekte fysikk involvert i dette spillet, selv om vi kunne bruke ulike fysikkrammer for vårt kollisjonssensjonsformål. Men som du er klar over, legger en fysikkmotor mye ytelse overfor spillet vårt, og vi bør alltid prøve å se om vi kan unngå det.
I vårt tilfelle beregner vi bare avstanden mellom vår snøball og hvert tre for å utløse en kollisjon hvis de er veldig nært. Dette skjer i
doTreeLogic
metode, som blir kalt fraOppdater
.funksjon doTreeLogic () var oneTree; var treePos = nytt THREE.Vector3 (); treesInPath.forEach (funksjon (element, indeks) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); hvis (treePos.distanceTo (heroSphere.position)<=0.6) console.log("hit"); hasCollided=true; explode(); ); //…Som du kanskje har lagt merke til, lagres alle trær som er tilstede i vår vei, i
treesInPath
array. DedoTreeLogic
Metoden fjerner også trærne fra skjerm og inn i bassenget når de går ut av vår visning ved hjelp av koden som er vist nedenfor.var treesToRemove = []; trærInPath.forEach (funksjon (element, indeks) oneTree = treesInPath [indeks]; treePos.setFromMatrixPosition (oneTree.matrixWorld); hvis (treePos.z> 6 && oneTree.visible) // gått ut av vår visningssone treesToRemove.push (oneTree);); var fra hvor; treesToRemove.forEach (funksjon (element, indeks) oneTree = treesToRemove [index]; fromWhere = treesInPath.indexOf (oneTree); treesInPath.splice (fromWhere, 1); treesPool.push (oneTree); oneTree.visible = false; .log ("remove tree"););Konklusjon
Å lage et 3D-spill er en komplisert prosess hvis du ikke bruker et visuelt verktøy som Unity. Det kan virke skremmende eller overveldende, men la meg forsikre deg om at når du får tak i det, vil du føle deg mye kraftigere og kreativere. Jeg vil at du skal utforske videre ved hjelp av de ulike fysikkrammene eller partikkelsystemene eller de offisielle eksemplene.