La piattaforma web ha avuto un'enorme crescita negli ultimi tempi con l'aiuto di HTML5, WebGL e la maggiore potenza dell'attuale generazione di dispositivi. Ora i dispositivi mobili e i browser sono in grado di offrire contenuti ad alte prestazioni sia in 2D che in 3D. La familiarità di JavaScript (JS) come linguaggio di scripting è stata anche un fattore trainante, dopo la scomparsa della piattaforma web Flash.
La maggior parte degli sviluppatori web è ben consapevole di quanto sia complicato l'ecosistema JS con tutti i vari framework e standard disponibili, che a volte potrebbero essere schiaccianti per un nuovo sviluppatore. Ma quando si parla di 3D, le scelte sono semplici, grazie a Mr.Doob. Il suo Three.js è attualmente l'opzione migliore là fuori per creare contenuti WebGL 3D ad alte prestazioni. Un'altra potente alternativa è Babylon.js, che potrebbe essere utilizzata anche per creare giochi 3D.
In questo tutorial, imparerai a creare un semplice gioco web 3D nativo per corridori senza fine utilizzando la potente struttura Three.js. Utilizzerai i tasti freccia per controllare una palla di neve che rotola giù da una montagna per evitare gli alberi sul tuo percorso. Non c'è arte coinvolta e tutte le immagini sono create in codice.
Envato Tuts + ha già alcune esercitazioni che potrebbero aiutarti a iniziare con Three.js. Ecco alcuni di questi per iniziare.
Creiamo prima una scena 3D di base, come mostrato qui dove c'è un cubo rotante. Puoi utilizzare il trascinamento del mouse in orbita attorno al cubo.
Qualsiasi immagine visualizzata su uno schermo bidimensionale è praticamente di natura 2D, con alcuni elementi importanti che forniscono l'illusione 3D: l'illuminazione, l'ombreggiatura, le ombre e la magia di proiezione 3D in 2D che avviene tramite la fotocamera. Nella scena sopra, abilitiamo l'illuminazione efficace usando queste linee di codice.
camera = new THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // prospettiva camera renderer = new THREE.WebGLRenderer (alpha: true); // renderer con sfondo trasparente renderer.shadowMap.enabled = true; // enable shadow renderer.shadowMap.type = THREE.PCFSoftShadowMap; // ... hero = new THREE.Mesh (heroGeometry, heroMaterial); hero.castShadow = true; hero.receiveShadow = falso; // ... ground.receiveShadow = true; ground.castShadow = falso; // ... sun = new THREE.DirectionalLight (0xffffff, 0.8); sun.position.set (0,4,1); sun.castShadow = true; scene.add (sole); // Imposta le proprietà delle ombre per la luce del sole sun.shadow.mapSize.width = 256; sun.shadow.mapSize.height = 256; sun.shadow.camera.near = 0.5; sun.shadow.camera.far = 50;
Il renderer
deve avere shadowmap
abilitato, la scena deve avere una luce con castShadow
abilitato, e tutti gli oggetti 3D hanno bisogno del castShadow
e receiveShadow
proprietà impostate in modo appropriato. Perché l'ombreggiatura corretta avvenga, dovremmo usare anche il MeshStandardMaterial
o un materiale più ricco di funzionalità per i nostri oggetti 3D. La telecamera è controllata usando lo script nifty OrbitControls. Consiglierei di giocare con la scena 3D di base aggiungendo forme più primitive o giocando con l'illuminazione, ecc., Prima di procedere con il tutorial.
Esistono molti tipi di giochi per corridori senza fine e il nostro è un "rullo infinito". Creeremo un gioco in cui una palla di neve sta rotolando giù per una montagna senza fine dove usiamo i tasti freccia per schivare gli alberi in arrivo. Una cosa interessante è che questo semplice gioco non coinvolgerà alcun patrimonio artistico, in quanto tutti i componenti sarebbero creati dal codice. Ecco il gioco completo per giocare.
I componenti o gli elementi principali del gioco sono:
Esploreremo ognuno di questi uno per uno nella sezione seguente.
Il nebbia
è una proprietà della scena 3D in Tre. È sempre un trucco utile da usare per simulare la profondità o mostrare un orizzonte. Il colore della nebbia è importante perché l'illusione funzioni correttamente e dipende dal colore della scena e dall'illuminazione. Come puoi vedere nel codice qui sotto, impostiamo anche il renderer
'S clearColor
valore per essere vicino al colore del nebbia
.
scene = new THREE.Scene (); scene.fog = new THREE.FogExp2 (0xf0fff0, 0.14); camera = new THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // prospettiva camera renderer = new THREE.WebGLRenderer (alpha: true); // renderer con sfondo trasparente renderer.setClearColor (0xfffafa, 1) ;
Per abbinare l'atmosfera, stiamo anche utilizzando valori di colore simili alle luci utilizzate nella scena. Ogni colore ambientale è una diversa tonalità di bianco che si unisce per creare l'effetto necessario.
var hemisphereLight = new THREE.HemisphereLight (0xfffafa, 0x000000, .9) scene.add (hemisphereLight); sun = new THREE.DirectionalLight (0xcdc1c5, 0.9); sun.position.set (12,6, -7); sun.castShadow = true; scene.add (sole);
La nostra palla di neve è un DodecahedronGeometry
tre forme primitive create come mostrato di seguito.
var sphereGeometry = new THREE.DodecahedronGeometry (heroRadius, 1); var sphereMaterial = new THREE.MeshStandardMaterial (color: 0xe5f2f2, shading: THREE.FlatShading) heroSphere = new THREE.Mesh (sphereGeometry, sphereMaterial);
Per tutti gli elementi 3D in questo gioco, stiamo usando THREE.FlatShading
per ottenere l'aspetto desiderato a basso numero di poligoni.
Il terreno di scorrimento chiamato rollingGroundSphere
è un grande SphereGeometry
primitivo e lo ruotiamo sul X
asse per creare l'illusione del terreno in movimento. La palla di neve non scorre davvero su nulla; stiamo solo creando l'illusione mantenendo la sfera terrestre roteando mantenendo la palla di neve ferma.
Una primitiva di sfera normale apparirà molto liscia e quindi non fornirà la necessaria robustezza necessaria per il pendio della montagna. Quindi facciamo alcune manipolazioni dei vertici per cambiare la superficie della sfera liscia in un terreno accidentato. Ecco il codice corrispondente seguito da una spiegazione.
lati var = 40; var tiers = 40; var sphereGeometry = new THREE.SphereGeometry (worldRadius, sides, tiers); var sphereMaterial = new THREE.MeshStandardMaterial (color: 0xfffafa, shading: THREE.FlatShading) var vertexIndex; var vertexVector = new THREE.Vector3 (); var nextVertexVector = new THREE.Vector3 (); var firstVertexVector = new THREE.Vector3 (); var offset = new THREE.Vector3 (); var currentTier = 1; var lerpValue = 0.5; var heightValue; var maxHeight = 0,07; per (var j = 1; jStiamo creando una sfera primitiva con 40 segmenti orizzontali (
lati
) e 40 segmenti verticali (livelli
). È possibile accedere a ciascun vertice di una tre geometria tramitevertici
proprietà dell'array. Effettuiamo un ciclo attraverso tutti i livelli tra i vertici estremi in alto e in basso estremo per eseguire le nostre manipolazioni dei vertici. Ogni livello della geometria della sfera contiene esattamentelati
numero di vertici, che forma un anello chiuso attorno alla sfera.Il primo passo consiste nel ruotare ogni anello dispari di vertici per rompere l'uniformità dei contorni della superficie. Spostiamo ogni vertice nell'anello di una frazione casuale tra 0,25 e 0,75 della distanza dal vertice successivo. Come risultato di ciò, i vertici verticali della sfera non sono più allineati in linea retta, e otteniamo un bel contorno a zigzag.
Come secondo passo, forniamo a ciascun vertice una regolazione dell'altezza casuale allineata con la normale al vertice, indipendentemente dal livello a cui appartiene. Ciò si traduce in una superficie irregolare e robusta. Spero che la matematica vettoriale utilizzata qui sia semplice una volta che si considera che il centro della sfera è considerato l'origine
(0,0)
.Gli alberi
Gli alberi appaiono fuori dalla nostra pista di rotolamento per aggiungere profondità al mondo e dentro come ostacoli. Creare l'albero è un po 'più complicato di un terreno accidentato, ma segue la stessa logica. Noi usiamo a
ConeGeometry
primitivo per creare la parte superiore verde dell'albero e aCylinderGeometry
per creare la parte inferiore del tronco.Per la parte superiore, passiamo attraverso ogni livello di vertici ed espandiamo l'anello dei vertici seguito dal restringimento lungo l'anello successivo. Il seguente codice mostra il
blowUpTree
metodo utilizzato per espandere l'anello alternativo dei vertici verso l'esterno e iltightenTree
metodo utilizzato per ridurre il prossimo anello di vertici.function createTree () var sides = 8; var tiers = 6; var scalarMultiplier = (Math.random () * (0.25-0.1)) + 0.05; var midPointVector = new THREE.Vector3 (); var vertexVector = new THREE.Vector3 (); var treeGeometry = new THREE.ConeGeometry (0,5, 1, lati, livelli); var treeMaterial = new THREE.MeshStandardMaterial (color: 0x33ff33, shading: THREE.FlatShading); var offset; midPointVector = treeGeometry.vertices [0] .clone (); var currentTier = 0; var vertexIndex; blowUpTree (treeGeometry.vertices, fianchi, 0, scalarMultiplier); tightenTree (treeGeometry.vertices, lati, 1); blowUpTree (treeGeometry.vertices, lati, 2, scalarMultiplier * 1.1, true); tightenTree (treeGeometry.vertices, fianchi, 3); blowUpTree (treeGeometry.vertices, lati, 4, scalarMultiplier * 1.2); tightenTree (treeGeometry.vertices, fianchi, 5); var treeTop = new THREE.Mesh (treeGeometry, treeMaterial); treeTop.castShadow = true; treeTop.receiveShadow = falso; treeTop.position.y = 0,9; treeTop.rotation.y = (Math.random () * (Math.PI)); var treeTrunkGeometry = new THREE.CylinderGeometry (0,1, 0,1,0,5); var trunkMaterial = new THREE.MeshStandardMaterial (color: 0x886633, shading: THREE.FlatShading); var treeTrunk = new THREE.Mesh (treeTrunkGeometry, trunkMaterial); treeTrunk.position.y = 0.25; var tree = new THREE.Object3D (); tree.add (treetrunk); tree.add (TREETOP); albero di ritorno; function blowUpTree (vertici, lati, currentTier, scalarMultiplier, dispari) var vertexIndex; var vertexVector = new THREE.Vector3 (); var midPointVector = vertici [0] .clone (); var offset; per (var i = 0; iIl
blowUpTree
il metodo spinge ogni vertice alternativo in un anello di vertici mantenendo gli altri vertici nell'anello ad un'altezza minore. Questo crea i rami appuntiti sull'albero. Se usiamo i vertici dispari in un livello, usiamo i vertici pari nel livello successivo in modo che l'uniformità sia interrotta. Una volta formato l'albero completo, diamo una rotazione casuale sull'asse y per renderlo leggermente diverso.L'effetto di esplosione
L'effetto esplosione del pixel blocco non è il più elegante che potremmo usare, ma sicuramente funziona bene. Questo particolare effetto particellare è in realtà una geometria 3D che viene manipolata per apparire come un effetto usando il
THREE.Points
classe.function addExplosion () particleGeometry = new THREE.Geometry (); per (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) explosionPower- = 0.001; else particles.visibile = falso; particleGeometry.verticesNeedUpdate = true;Il
addExplosion
il metodo aggiunge 20 vertici alvertici
matrice delparticleGeometry
. Ilesplodere
il metodo viene chiamato quando è necessario eseguire l'effetto, che posiziona casualmente ciascun vertice della geometria. IldoExplosionLogic
viene chiamato nelaggiornare
metodo se l'oggetto particella è visibile, dove spostiamo ciascun vertice all'esterno. Ogni vertice in apunti
l'oggetto viene reso come un blocco quadrato.4. Il gameplay
Ora che sappiamo come creare ciascuno degli elementi necessari per il gioco, entriamo nel gameplay. Gli elementi principali del gameplay sono:
- il ciclo di gioco
- il posizionamento degli alberi
- l'interazione dell'utente
- il rilevamento delle collisioni
Analizziamo quelli in dettaglio.
Il ciclo di gioco
Tutta la meccanica di base del gioco avviene nel ciclo di gioco, che nel nostro caso è il
aggiornare
metodo. Lo chiamiamo per la prima volta daldentro
metodo, che viene richiamato sul carico della finestra. Dopo questo, si aggancia al ciclo di rendering del documento usando ilrequestAnimationFrame
metodo in modo che venga chiamato ripetutamente.function update () 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 (); if (! hasCollided) score + = 2 * treeReleaseInterval; scoreText.innerHTML = score.toString (); doTreeLogic (); doExplosionLogic (); render (); requestAnimationFrame (update); // request next update function render () renderer.render (scene, camera); // disegnaNel
aggiornare
, noi chiamiamo ilrendere
metodo, che utilizza ilrenderer
per disegnare la scena. Chiamiamo ildoTreeLogic
metodo, che verifica la collisione e rimuove anche gli alberi una volta scomparsi.La palla di neve e le sfere terrestri vengono ruotate mentre aggiungiamo una logica di rimbalzo casuale alla palla di neve. I nuovi alberi vengono posizionati nel percorso chiamando
addPathTree
dopo che è trascorso un tempo predefinito. Il tempo è tracciato usando aTHREE.Clock
oggetto. Aggiorniamo anche ilPunto
a meno che non si sia verificata una collisione.Posizionamento degli alberi
Un gruppo di alberi è posizionato all'esterno della pista di rotolamento per creare il mondo usando il
addWorldTrees
metodo. Tutti gli alberi sono aggiunti come figlio dirollingGroundSphere
così che si muovano anche quando ruotiamo la sfera.function addWorldTrees () var numTrees = 36; var gap = 6,28 / 36; per (var i = 0; iPer piantare alberi del mondo, chiamiamo il
addtree
metodo passando valori attorno alla circonferenza della nostra sfera terrestre. IlsphericalHelper
l'utilità ci aiuta a trovare la posizione sulla superficie di una sfera.Per piantare alberi sul sentiero, faremo uso di un pool di alberi che vengono creati all'avvio usando il
createTreesPool
metodo. Abbiamo anche valori di angolo predefiniti per ogni percorso sulla sfera memorizzato nelpathAngleValues
schieramento.pathAngleValues = [1.52,1.57,1.62]; // ... function createTreesPool () var maxTreesInPool = 10; var newTree; per (var i = 0; i0.5) lane = Math.floor (Math.random () * 2); addtree (vero, le opzioni [corsia]); Il
addPathTree
il metodo viene chiamato dall'aggiornamento quando è trascorso un tempo sufficiente dopo aver piantato l'ultimo albero. A sua volta chiama iladdtree
metodo mostrato in precedenza con un diverso set di parametri in cui l'albero viene posizionato nel percorso selezionato. IldoTreeLogic
il metodo restituirà l'albero alla piscina quando non sarà visibile.Interazione dell'utente
Stiamo aggiungendo un listener al documento per cercare eventi di tastiera rilevanti. Il
handleKeyDown
il metodo imposta ilcurrentLane
valore se i tasti freccia destra o sinistra sono premuti o imposta ilbounceValue
valore se viene premuta la freccia su.document.onkeydown = handleKeyDown; // ... function handleKeyDown (keyEvent) if (jumping) return; var validMove = true; if (keyEvent.keyCode === 37) // left if (currentLane == middleLane) currentLane = leftLane; else if (currentLane == rightLane) currentLane = middleLane; else validMove = false; else if (keyEvent.keyCode === 39) // right if (currentLane == middleLane) currentLane = rightLane; else if (currentLane == leftLane) currentLane = middleLane; else validMove = false; else if (keyEvent.keyCode === 38) // up, jump bounceValue = 0.1; salto = true; validMove = false; if (validMove) jumping = true; bounceValue = 0.06;Nel
aggiornare
, ilX
la posizione della nostra palla di neve viene lentamente incrementata per raggiungere lacurrentLane
posizionarsi lì cambiando corsia.Rilevamento collisione
Non c'è una vera fisica coinvolta in questo particolare gioco, sebbene potremmo usare vari framework fisici per il nostro scopo di rilevamento delle collisioni. Ma come ben sai, un motore fisico aggiunge un sacco di prestazioni generali al nostro gioco, e dovremmo sempre cercare di vedere se possiamo evitarlo.
Nel nostro caso, calcoliamo semplicemente la distanza tra la nostra palla di neve e ogni albero per innescare una collisione se sono molto vicini. Questo succede nel
doTreeLogic
metodo, da cui viene chiamatoaggiornare
.function doTreeLogic () var oneTree; var treePos = new THREE.Vector3 (); treesInPath.forEach (function (element, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); if (treePos.distanceTo (heroSphere.position)<=0.6) console.log("hit"); hasCollided=true; explode(); ); //…Come avrai notato, tutti gli alberi attualmente presenti nel nostro percorso sono memorizzati nel
treesInPath
array. IldoTreeLogic
il metodo rimuove anche gli alberi dal display e nella piscina quando escono dalla nostra vista usando il codice mostrato sotto.var treesToRemove = []; treesInPath.forEach (function (element, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); if (treePos.z> 6 && oneTree.visible) // è uscito dagli alberi della view zoneToRemove.push (Un albero); ); var fromhere; treesToRemove.forEach (function (element, index) oneTree = treesToRemove [index]; fromWhere = treesInPath.indexOf (oneTree); treesInPath.splice (fromWhere, 1); treesPool.push (oneTree); oneTree.visible = false; console .log ("remove tree"););Conclusione
Creare un gioco 3D è un processo complicato se non si utilizza uno strumento visivo come Unity. Potrebbe sembrare intimidatorio o travolgente, ma ti assicuro che una volta capito, ti sentirai molto più potente e creativo. Vorrei che tu approfondissi ulteriormente utilizzando i vari quadri di fisica o sistemi di particelle o gli esempi ufficiali.