Het webplatform heeft de laatste tijd een enorme groei doorgemaakt met behulp van HTML5, WebGL en de toegenomen kracht van de huidige generatie apparaten. Nu kunnen mobiele apparaten en browsers goed presterende inhoud leveren, zowel in 2D als 3D. De bekendheid van JavaScript (JS) als een scripttaal is ook een sturende factor geweest, na het verdwijnen van het Flash-webplatform.
De meeste webontwikkelaars zijn zich bewust van hoe ingewikkeld het JS-ecosysteem is met alle verschillende frameworks en standaarden die beschikbaar zijn, wat soms overweldigend zou kunnen zijn voor een nieuwe ontwikkelaar. Maar als het op 3D aankomt, zijn de keuzes eenvoudig, dankzij Mr.Doob. Zijn Three.js is momenteel de beste optie om krachtige 3D WebGL-content te maken. Een ander krachtig alternatief is Babylon.js, dat ook kan worden gebruikt om 3D-spellen te maken.
In deze tutorial leer je een eenvoudige eindeloze runner-style native web 3D-game te maken met behulp van het krachtige Three.js-framework. Je zult de pijltjestoetsen gebruiken om een sneeuwbal te besturen die over een berghelling rolt om de bomen op je pad te ontwijken. Er is geen kunst bij betrokken en alle beelden worden in code gemaakt.
Envato Tuts + heeft al een paar zelfstudies die je op weg kunnen helpen met Three.js. Hier zijn enkele om u op weg te helpen.
Laten we eerst een eenvoudige 3D-scène maken, zoals hier weergegeven, waar een roterende kubus is. U kunt muisslepen gebruiken om rond de kubus te draaien.
Elke afbeelding die op een tweedimensionaal scherm wordt weergegeven, is praktisch 2D-van aard, met een paar belangrijke elementen die de 3D-illusie bieden: de verlichting, de schaduwwerking, de schaduwen en de 3D- tot 2D-projectiemagie die via de camera plaatsvindt. In de bovenstaande scène maken we effectieve belichting mogelijk met behulp van deze coderegels.
camera = nieuwe THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // perspectiefcamera-renderer = nieuw THREE.WebGLRenderer (alpha: true); // renderer met transparante achtergrond renderer.shadowMap.enabled = true; // enable shadow renderer.shadowMap.type = THREE.PCFSoftShadowMap; // ... held = nieuw THREE.Mesh (heroGeometry, heroMaterial); hero.castShadow = true; hero.receiveShadow = false; // ... ground.receiveShadow = true; ground.castShadow = false; // ... zon = nieuwe THREE.DirectionalLight (0xffffff, 0.8); sun.position.set (0,4,1); sun.castShadow = true; scene.add (zon); // Stel schaduweigenschappen in voor het zonlicht Sun.shadow.mapSize.width = 256; sun.shadow.mapSize.height = 256; sun.shadow.camera.near = 0,5; sun.shadow.camera.far = 50;
De renderer
moet hebben shadowMap
ingeschakeld, moet de scène een licht hebben met castShadow
ingeschakeld en alle 3D-objecten hebben de castShadow
en receiveShadow
eigenschappen op de juiste manier ingesteld. Voor de juiste schaduwwerking moeten we ook de MeshStandardMaterial
of een materiaal met meer functies voor onze 3D-objecten. De camera wordt bestuurd met het handige OrbitControls-script. Ik zou aanraden om rond te spelen met de standaard 3D-scène door meer primitieve vormen toe te voegen of met de verlichting enz. Te spelen, voordat je verdergaat met de tutorial.
Er zijn veel soorten eindeloze runnerspellen en de onze is een 'eindeloze rol'. We zullen een spel maken waarbij een sneeuwbal over een eindeloze berghelling rolt, waarbij we de pijltjestoetsen gebruiken om de inkomende bomen te ontwijken. Een interessant ding is dat deze eenvoudige game geen kunstitems bevat, omdat alle componenten door code worden gemaakt. Hier is het volledige spel om rond te spelen.
De belangrijkste componenten of elementen van het spel zijn:
We zullen elk van deze een voor een onderzoeken in de volgende sectie.
De mist
is een eigenschap van de 3D-scène in Drie. Het is altijd een handige truc om te gebruiken om diepte te simuleren of een horizon te laten zien. De kleur van de mist is belangrijk om de illusie goed te laten werken en hangt af van de kleur van de scène en de verlichting. Zoals je in de onderstaande code kunt zien, hebben we ook de renderer
's clearColor
waarde om dicht bij de kleur van de mist
.
scène = nieuwe THREE.Scene (); scene.fog = nieuwe THREE.FogExp2 (0xf0fff0, 0.14); camera = nieuwe THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // perspectiefcamera-renderer = nieuw THREE.WebGLRenderer (alpha: true); // renderer met transparante achtergrond renderer.setClearColor (0xfffafa, 1) ;
Om de sfeer te evenaren, gebruiken we vergelijkbare kleurwaarden voor de lichten die in de scène worden gebruikt. Elke omgevingskleur is een andere tint wit die samen geleert om het gewenste effect te creëren.
var hemisphereLight = new THREE.HemisphereLight (0xfffafa, 0x000000, .9) scene.add (hemisphereLight); zon = nieuwe THREE.DirectionalLight (0xcdc1c5, 0.9); sun.position.set (12,6, -7); sun.castShadow = true; scene.add (zon);
Onze sneeuwbal is een DodecahedronGeometry
drie primitieve vorm gemaakt zoals hieronder getoond.
var sphereGeometry = nieuwe THREE.DodecahedronGeometry (heroRadius, 1); var sphereMaterial = new THREE.MeshStandardMaterial (color: 0xe5f2f2, shading: THREE.FlatShading) heroSphere = new THREE.Mesh (sphereGeometry, sphereMaterial);
Voor alle 3D-elementen in dit spel die we gebruiken THREE.FlatShading
om de gewenste low-poly-look te krijgen.
De scrollende grond genoemd rollingGroundSphere
is een grote SphereGeometry
primitief, en we roteren het op de X
as om de bewegende grond illusie te creëren. De sneeuwbal rolt niet echt over iets heen; we creëren gewoon de illusie door de grondbol te laten rollen terwijl de sneeuwbal stationair blijft.
Een normale bolprimitief ziet er erg glad uit en biedt daarom niet de nodige robuustheid die nodig is voor de berghelling. Dus we doen enkele vertex-manipulaties om het oppervlak van de gladde bol te veranderen in een ruig terrein. Hier is de bijbehorende code gevolgd door een uitleg.
var zijden = 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 = nieuwe THREE.Vector3 (); var nextVertexVector = nieuwe THREE.Vector3 (); var firstVertexVector = nieuwe THREE.Vector3 (); var offset = nieuw THREE.Vector3 (); var currentTier = 1; var lerpValue = 0,5; var heightValue; var maxHeight = 0,07; voor (var j = 1; jWe creëren een bolprimitief met 40 horizontale segmenten (
zijden
) en 40 verticale segmenten (tiers
). Elke top van een drie geometrie is toegankelijk via dehoekpunten
matrixeigenschap. We doorlopen alle lagen tussen de extreme top en extreme onderste hoekpunten om onze vertex-manipulaties uit te voeren. Elke laag van de bolgeometrie bevat precieszijden
aantal hoekpunten, dat een gesloten ring rond de bol vormt.De eerste stap is om elke oneven hoek van hoekpunten te roteren om de uniformiteit van de oppervlaktecontouren te doorbreken. We verplaatsen elke hoek in de ring met een willekeurige breuklijn tussen 0,25 en 0,75 van de afstand tot de volgende hoek. Als gevolg hiervan zijn de verticale hoekpunten van de bol niet meer in een rechte lijn uitgelijnd en krijgen we een mooie zigzagcontour.
Als de tweede stap bieden we elk knooppunt een willekeurige hoogteaanpassing die is uitgelijnd met de normaal op het hoekpunt, ongeacht de laag waartoe het knooppunt behoort. Dit resulteert in een ongelijk en ruw oppervlak. Ik hoop dat de vector wiskunde hier eenvoudig is als je bedenkt dat het centrum van de bol als de oorsprong wordt beschouwd
(0,0)
.De bomen
De bomen verschijnen buiten ons rollend spoor om diepte aan de wereld toe te voegen, en binnen als obstakels. Het maken van de boom is een beetje ingewikkelder dan de ruige grond, maar volgt dezelfde logica. We gebruiken een
ConeGeometry
primitief om het bovenste groene deel van de boom te maken en eenCylinderGeometry
om het onderste deel van de kofferbak te maken.Voor het bovenste gedeelte doorlopen we elke hoek van hoekpunten en breiden we de hoek met hoekpunten uit, gevolgd door de volgende ring naar beneden te krimpen. De volgende code toont de
blowUpTree
methode die wordt gebruikt om de alternatieve ring van hoekpunten naar buiten uit te breiden en detightenTree
methode die wordt gebruikt om de volgende hoek met hoekpunten kleiner te maken.function createTree () var sides = 8; var tiers = 6; var scalarMultiplier = (Math.random () * (0.25-0.1)) + 0.05; var midPointVector = nieuwe THREE.Vector3 (); var vertexVector = nieuwe THREE.Vector3 (); var treeGeometry = nieuwe THREE.ConeGeometry (0,5, 1, zijden, lagen); var treeMaterial = nieuw THREE.MeshStandardMaterial (color: 0x33ff33, shading: THREE.FlatShading); var offset; midPointVector = treeGeometry.vertices [0] .clone (); var currentTier = 0; var vertexIndex; blowUpTree (treeGeometry.vertices, kanten, 0, scalarMultiplier); tightenTree (treeGeometry.vertices, zijkanten, 1); blowUpTree (treeGeometry.vertices, zijden, 2, scalarMultiplier * 1.1 true); tightenTree (treeGeometry.vertices, zijkanten, 3); blowUpTree (treeGeometry.vertices, zijden, 4, scalarMultiplier * 1,2); tightenTree (treeGeometry.vertices, zijkanten, 5); var treeTop = new THREE.Mesh (treeGeometry, treeMaterial); treeTop.castShadow = true; treeTop.receiveShadow = false; treeTop.position.y = 0,9; treeTop.rotation.y = (Math.random () * (Math.PI)); var treeTrunkGeometry = nieuwe THREE.CylinderGeometry (0.1, 0.1.0.5); var trunkMaterial = new THREE.MeshStandardMaterial (color: 0x886633, shading: THREE.FlatShading); var treeTrunk = nieuwe THREE.Mesh (treeTrunkGeometry, trunkMaterial); treeTrunk.position.y = 0,25; var tree = nieuw THREE.Object3D (); tree.add (boomstam); tree.add (TREETOP); terugkeer boom; functie blowUpTree (hoekpunten, zijden, currentTier, scalarMultiplier, oneven) var vertexIndex; var vertexVector = nieuwe THREE.Vector3 (); var midPointVector = vertices [0] .clone (); var offset; for (var i = 0; iDe
blowUpTree
methode duwt elke alternatieve hoek in een ring van hoekpunten naar buiten terwijl de andere hoekpunten in de ring op een lagere hoogte worden gehouden. Dit creëert de puntige takken op de boom. Als we de oneven hoekpunten in één laag gebruiken, gebruiken we de even hoekpunten in de volgende laag zodat de uniformiteit wordt verbroken. Zodra de volledige boom is gevormd, geven we deze een willekeurige rotatie op de y-as om hem er enigszins anders uit te laten zien.Het explosie-effect
Het block-pixel explosie-effect is niet de meest elegante die we zouden kunnen gebruiken, maar het presteert zeker goed. Dit specifieke deeltjeseffect is eigenlijk een 3D-geometrie die wordt gemanipuleerd om eruit te zien als een effect met behulp van de
Drie punten
klasse.function 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) explosionPower- = 0.001; else particles.visible = false; particleGeometry.verticesNeedUpdate = true;De
addExplosion
methode voegt 20 hoekpunten toe aan dehoekpunten
reeks van departicleGeometry
. Deontploffen
methode wordt aangeroepen als we het effect nodig hebben om uit te voeren, dat willekeurig elke hoek van de geometrie positioneert. DedoExplosionLogic
wordt gebeld in debijwerken
methode als het deeltjesobject zichtbaar is, waarbij we elk hoekpunt naar buiten verplaatsen. Elke vertex in apoints
object wordt weergegeven als een vierkant blok.4. De gameplay
Nu we weten hoe we elk van de items die nodig zijn voor het spel kunnen maken, gaan we in de gameplay. De belangrijkste gameplay-elementen zijn:
- de game-loop
- de plaatsing van de bomen
- de gebruikersinteractie
- de botsing detectie
Laten we die in detail analyseren.
The Game Loop
Alle kerngame-mechanica gebeurt in de spellus, wat in ons geval het geval is
bijwerken
methode. We noemen het voor de eerste keer van dein het
methode, die wordt aangeroepen voor het laden van vensters. Hierna haakt het in op de documentweergave-lus met behulp van derequestAnimationFrame
methode zodat deze herhaaldelijk wordt aangeroepen.functie-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); // vraag volgende update functie render () renderer.render (scène, camera); // tekenIn
bijwerken
, we noemen hetgeven
methode, die derenderer
om de scène te tekenen. We noemen hetdoTreeLogic
methode, die controleert op een botsing en ook de bomen verwijdert zodra ze uit het zicht zijn verdwenen.De sneeuwbal en de grond bollen worden geroteerd terwijl we ook een willekeurige stuiterende logica toevoegen aan de sneeuwbal. Nieuwe bomen worden in het pad geplaatst door te bellen
addPathTree
nadat een vooraf gedefinieerde tijd is verstreken. Tijd wordt bijgehouden met behulp van eenTHREE.Clock
voorwerp. We werken ook hetpartituur
tenzij een botsing is opgetreden.Plaatsing van de bomen
Eén set bomen wordt buiten de rollende baan geplaatst om de wereld te maken met behulp van de
addWorldTrees
methode. Alle bomen worden toegevoegd als een kind van derollingGroundSphere
zodat ze ook bewegen wanneer we de bol draaien.function addWorldTrees () var numTrees = 36; var gap = 6.28 / 36; for (var i = 0; iOm wereldbomen te planten, noemen we de
addTree
methode door waarden door te geven rond de omtrek van onze bodemsfeer. DesphericalHelper
hulpprogramma helpt ons de positie op het oppervlak van een bol te vinden.Om bomen op het pad te planten, zullen we gebruik maken van een pool van bomen die bij het begin gemaakt zijn met behulp van de
createTreesPool
methode. We hebben ook vooraf gedefinieerde hoekwaarden voor elk pad op de bol opgeslagen in depathAngleValues
rangschikking.pathAngleValues = [1.52,1.57,1.62]; // ... function createTreesPool () var maxTreesInPool = 10; var newTree; for (var i = 0; i0.5) lane = Math.floor (Math.random () * 2); addTree (true, opties [baan]); De
addPathTree
methode wordt aangeroepen vanuit update wanneer er voldoende tijd is verstreken na het planten van de laatste boom. Het roept op zijn beurt deaddTree
methode eerder getoond met een andere set parameters waarbij de boom in het geselecteerde pad wordt geplaatst. DedoTreeLogic
methode zal de boom terugbrengen naar het zwembad zodra het uit het zicht verdwijnt.Gebruikersinteractie
We voegen een luisteraar aan het document toe om naar relevante toetsenbordgebeurtenissen te zoeken. De
handleKeyDown
methode stelt decurrentLane
waarde als de rechter of linker pijltjestoetsen worden ingedrukt of debounceValue
waarde als op de pijl-omhoog wordt gedrukt.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) // omhoog, jump bounceValue = 0,1; jumping = true; validMove = false; if (validMove) jumping = true; bounceValue = 0,06;In
bijwerken
, deX
de positie van onze sneeuwbal wordt langzaam verhoogd om de sneeuwbal te bereikencurrentLane
positie daar door van rijstrook te wisselen.Collision Detection
Er is geen echte natuurkunde betrokken bij dit specifieke spel, hoewel we verschillende fysica-kaders zouden kunnen gebruiken voor onze botsdetectiedoelstelling. Maar zoals u wel weet, voegt een physics-engine een hoop prestatiekosten toe aan onze game, en we moeten altijd proberen te kijken of we deze kunnen vermijden.
In ons geval berekenen we alleen de afstand tussen onze sneeuwbal en elke boom om een botsing te veroorzaken als ze erg dichtbij zijn. Dit gebeurt in de
doTreeLogic
methode, waarvan wordt gebeldbijwerken
.function doTreeLogic () var oneTree; var treePos = nieuwe 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(); ); //…Zoals je misschien hebt gemerkt, worden alle bomen die momenteel in ons pad aanwezig zijn opgeslagen in de
treesInPath
matrix. DedoTreeLogic
methode verwijdert ook de bomen uit het display en in het zwembad zodra ze uit onze weergave gaan met behulp van de onderstaande code.var treesToRemove = []; treesInPath.forEach (function (element, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); if (treePos.z> 6 && oneTree.visible) // verdwenen uit onze weergavezone treesToRemove.push (een boom); ); var fromWhere; treesToRemove.forEach (function (element, index) oneTree = treesToRemove [index]; fromWhere = treesInPath.indexOf (oneTree); treesInPath.splice (fromWhere, 1); treesPool.push (oneTree); oneTree.visible = false; console .log ("verwijder boom"););Conclusie
Het maken van een 3D-game is een ingewikkeld proces als je geen visuele tool als Unity gebruikt. Het kan intimiderend of overweldigend lijken, maar laat me je verzekeren dat zodra je het onder de knie hebt, je je veel krachtiger en creatiever zult voelen. Ik zou graag willen dat je verder onderzoekt met behulp van de verschillende fysica-kaders of deeltjessystemen of de officiële voorbeelden.