La plate-forme Web a connu une croissance extraordinaire ces derniers temps avec l'aide de HTML5, WebGL et la puissance accrue de la génération actuelle d'appareils. Désormais, les appareils mobiles et les navigateurs sont capables de fournir un contenu hautement performant en 2D et en 3D. La familiarité de JavaScript (JS) en tant que langage de script a également été un facteur déterminant après la disparition de la plate-forme Web Flash..
La plupart des développeurs Web sont bien conscients de la complexité de l'écosystème JS avec les divers cadres et normes disponibles, ce qui peut parfois être pénible pour un nouveau développeur. Mais en matière de 3D, les choix sont simples, grâce à M. Doob. Three.js est actuellement la meilleure option pour créer du contenu WebGL 3D hautement performant. Babylon.js est une autre alternative puissante qui pourrait également être utilisée pour créer des jeux en 3D..
Dans ce didacticiel, vous apprendrez à créer un jeu 3D Web natif de style coureur sans fin et simple à l’aide du puissant framework Three.js. Vous utiliserez les touches fléchées pour contrôler une boule de neige sur une montagne afin d'esquiver les arbres sur votre chemin. Il n'y a pas d'art impliqué, et tous les visuels sont créés dans le code.
Envato Tuts + a déjà quelques tutoriels qui pourraient vous aider à démarrer avec Three.js. En voici quelques-uns pour vous aider à démarrer.
Commençons par créer une scène 3D de base, comme illustré ici, où il y a un cube en rotation. Vous pouvez utiliser le glisser de la souris pour faire le tour du cube.
Tout graphique affiché sur un écran bidimensionnel est pratiquement de nature 2D, avec quelques éléments importants qui fournissent l’illusion 3D: l’éclairage, les ombres, les ombres et la magie de la projection 3D à 2D qui se produit via la caméra. Dans la scène ci-dessus, nous permettons un éclairage efficace en utilisant ces lignes de code.
camera = new THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // rendu de caméra en perspective = new THREE.WebGLRenderer (alpha: true); // rendu avec un fond transparent renderer.shadowMap.enabled = true; // activer shadow renderer.shadowMap.type = THREE.PCFSoftShadowMap; //… hero = new THREE.Mesh (heroGeometry, heroMaterial); hero.castShadow = true; hero.receiveShadow = false; //… ground.receiveShadow = true; ground.castShadow = false; //… sun = new THREE.DirectionalLight (0xffffff, 0.8); sun.position.set (0,4,1); sun.castShadow = true; scene.add (soleil); // Définition des propriétés d'ombre pour la lumière du soleil sun.shadow.mapSize.width = 256; sun.shadow.mapSize.height = 256; sun.shadow.camera.near = 0,5; sun.shadow.camera.far = 50;
le moteur de rendu
a besoin d'avoir shadowMap
activé, la scène doit avoir une lumière avec ombre
activé, et tous les objets 3D ont besoin de la ombre
et recevoir ombre
propriétés définies correctement. Pour que l’ombrage soit correct, nous devons également utiliser le MeshStandardMaterial
ou un matériau plus riche en fonctionnalités pour nos objets 3D. La caméra est contrôlée à l'aide du script astucieux OrbitControls. Je vous conseillerais de jouer avec la scène 3D de base en ajoutant des formes plus primitives ou en jouant avec l'éclairage, etc., avant de poursuivre le didacticiel..
Il existe de nombreux types de jeux de coureurs sans fin, et le nôtre est un «rouleau sans fin». Nous allons créer un jeu où une boule de neige dévalera une montagne sans fin et nous utiliserons les touches fléchées pour esquiver les arbres entrants. Une chose intéressante est que ce jeu simple ne comportera aucun actif artistique, car tous les composants seraient créés par le code. Voici le jeu complet à jouer.
Les composants ou éléments principaux du jeu sont:
Nous allons explorer chacune d’elles une à une dans la section suivante.
le brouillard
est une propriété de la scène 3D Trois. C'est toujours un truc pratique à utiliser pour simuler la profondeur ou afficher un horizon. La couleur du brouillard est importante pour que l’illusion fonctionne correctement et dépend de la couleur de la scène et de la lumière. Comme vous pouvez le voir dans le code ci-dessous, nous définissons également la moteur de rendu
de clearColor
valeur d'être proche de la couleur de la brouillard
.
scène = nouveau THREE.Scene (); scene.fog = new THREE.FogExp2 (0xf0fff0, 0,14); camera = new THREE.PerspectiveCamera (60, sceneWidth / sceneHeight, 0.1, 1000); // rendu de caméra en perspective = new THREE.WebGLRenderer (alpha: true); // rendu avec un fond transparent render.setClearColor (0xfffafa, 1) ;
Afin de correspondre à l'ambiance, nous utilisons également des valeurs de couleur similaires aux lumières utilisées dans la scène. Chaque couleur ambiante est une nuance de blanc différente qui se gélifie pour créer l'effet nécessaire.
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 (soleil);
Notre boule de neige est un Géométrie Dodécaèdre
trois formes primitives créées comme indiqué ci-dessous.
var sphereGeometry = new THREE.DodecahedronGeometry (heroRadius, 1); var sphereMaterial = new THREE.MeshStandardMaterial (color: 0xe5f2f2, ombrage: THREE.FlatShading) heroSphere = new THREE.Mesh (sphereGeometry, sphereMaterial);
Pour tous les éléments 3D de ce jeu, nous utilisons TROIS.FlatShading
pour obtenir le look low poly désiré.
Le terrain de défilement nommé rollingGroundSphere
est un grand SphèreGéométrie
primitif, et nous le faisons tourner sur le X
axe pour créer l'illusion de terrain en mouvement. La boule de neige ne tourne vraiment pas sur rien; nous créons simplement l'illusion en maintenant la sphère terrestre tout en maintenant la boule de neige immobile.
Une sphère normale primitive sera très lisse et ne fournira donc pas la robustesse nécessaire à la pente de la montagne. Nous faisons donc quelques manipulations de vertex pour changer la surface de la sphère lisse en un terrain accidenté. Voici le code correspondant suivi d'une explication.
var côtés = 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; pour (var j = 1; jNous créons une primitive de sphère avec 40 segments horizontaux (
côtés
) et 40 segments verticaux (les gradins
). Chaque sommet d’une géométrie à trois est accessible via le boutonsommets
propriété de tableau. Nous parcourons tous les niveaux entre les sommets extrême haut et extrême bas pour effectuer nos manipulations de sommet. Chaque niveau de la géométrie de la sphère contient exactementcôtés
nombre de sommets formant un anneau fermé autour de la sphère.La première étape consiste à faire pivoter chaque anneau de sommets impair afin de casser l'uniformité des contours de surface. Nous déplaçons chaque sommet de l'anneau d'une fraction aléatoire comprise entre 0,25 et 0,75 de la distance jusqu'au sommet suivant. En conséquence, les sommets verticaux de la sphère ne sont plus alignés en ligne droite et nous obtenons un joli contour en zigzag..
Dans un deuxième temps, nous fournissons à chaque sommet un réglage de hauteur aléatoire aligné sur la normale au sommet, quel que soit le niveau auquel il appartient. Il en résulte une surface inégale et accidentée. J'espère que les mathématiques vectorielles utilisées ici sont simples quand on considère que le centre de la sphère est considéré comme l'origine
(0,0)
.Les arbres
Les arbres apparaissent en dehors de notre piste de roulement pour ajouter de la profondeur au monde, et à l'intérieur comme des obstacles. La création de l'arbre est un peu plus compliquée que le terrain accidenté mais suit la même logique. Nous utilisons un
CôneGéométrie
primitif pour créer la partie verte supérieure de l’arbre et unGéométrie Cylindre
pour créer la partie inférieure du coffre.Pour la partie supérieure, nous parcourons chaque niveau de sommets et développons l'anneau de sommets, puis réduisons l'anneau suivant. Le code suivant montre le
blowUpTree
méthode utilisée pour élargir l’anneau alternatif de sommets vers l’extérieur et laserrerTree
méthode utilisée pour réduire l'anneau suivant de sommets.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, côtés, niveaux); 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, sides, 0, scalarMultiplier); tightenTree (treeGeometry.vertices, sides, 1); blowUpTree (treeGeometry.vertices, sides, 2, scalarMultiplier * 1.1, true); tightenTree (treeGeometry.vertices, sides, 3); blowUpTree (treeGeometry.vertices, sides, 4, scalarMultiplier * 1.2); tightenTree (treeGeometry.vertices, sides, 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 = nouvelle 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); arbre de retour; function blowUpTree (sommets, côtés, currentTier, scalarMultiplier, impair) var vertexIndex; var vertexVector = new THREE.Vector3 (); var midPointVector = sommets [0] .clone (); var offset; pour (var i = 0; ile
blowUpTree
méthode supprime chaque sommet alternatif dans un anneau de sommets tout en maintenant les autres sommets de l’anneau à une hauteur moindre. Cela crée les branches pointues sur l'arbre. Si nous utilisons les sommets impairs dans un niveau, nous utilisons les sommets pairs dans le niveau suivant afin que l'uniformité soit rompue. Une fois l’arbre complet formé, nous lui donnons une rotation aléatoire sur l’axe des y pour le rendre légèrement différent.L'effet d'explosion
L’effet d’explosion de pixels en bloc n’est pas le plus élégant que nous puissions utiliser, mais il fonctionne certainement bien. Cet effet de particule particulier est en fait une géométrie 3D qui est manipulée pour ressembler à un effet à l'aide de la
Trois points
classe.fonction addExplosion () particleGeometry = new THREE.Geometry (); pour (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 particules.visible = false; particleGeometry.verticesNeedUpdate = true;le
addExplosion
méthode ajoute 20 sommets à lasommets
tableau desparticuleGéométrie
. leexploser
La méthode est appelée lorsque nous avons besoin de l'effet, qui positionne de manière aléatoire chaque sommet de la géométrie. ledoExplosionLogic
se fait appeler dans lemettre à jour
méthode si l'objet particule est visible, où on déplace chaque sommet vers l'extérieur. Chaque sommet dans unpoints
l'objet est rendu sous forme de bloc carré.4. Le gameplay
Maintenant que nous savons comment créer chacun des éléments nécessaires au jeu, passons au gameplay. Les principaux éléments de jeu sont:
- la boucle du jeu
- le placement des arbres
- l'interaction de l'utilisateur
- la détection de collision
Analysons ceux en détail.
La boucle du jeu
Tout le mécanisme de base du jeu se passe dans la boucle de jeu, qui dans notre cas est la
mettre à jour
méthode. Nous l'appelons pour la première fois depuis leinit
méthode, qui est appelée sur le chargement de la fenêtre. Après cela, il s’accroche à la boucle de rendu du document à l’aide du boutonrequestAnimationFrame
méthode pour qu'il soit appelé à plusieurs reprises.function update () rollingGroundSphere.rotation.x + = rollingSpeed; heroSphere.rotation.x - = heroRollingSpeed; si (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 (); rendre(); requestAnimationFrame (update); // demande la prochaine mise à jour function render () renderer.render (scène, caméra); // drawDans
mettre à jour
, nous appelons lerendre
méthode, qui utilise lemoteur de rendu
dessiner la scène. Nous appelons ledoTreeLogic
méthode, qui vérifie la collision et supprime également les arbres une fois qu'ils sont hors de vue.La boule de neige et les sphères terrestres sont en rotation tandis que nous ajoutons également une logique de rebondissement aléatoire à la boule de neige. Les nouveaux arbres sont placés dans le chemin en appelant
addPathTree
après un temps prédéfini. Le temps est suivi en utilisant unTROIS heures
objet. Nous mettons également à jour leBut
sauf si une collision s'est produite.Placement des arbres
Un ensemble d’arbres est placé en dehors de la piste de roulement pour créer le monde à l’aide des
addWorldTrees
méthode. Tous les arbres sont ajoutés en tant qu'enfant durollingGroundSphere
afin qu'ils bougent aussi quand on tourne la sphère.fonction addWorldTrees () var numTrees = 36; écart var = 6,28 / 36; pour (var i = 0; iPour planter des arbres du monde, nous appelons le
addTree
méthode en passant des valeurs autour de la circonférence de notre sphère terrestre. lesphériqueHelper
utilité nous aide à trouver la position sur la surface d'une sphère.Pour planter des arbres sur le chemin, nous allons utiliser un pool d’arbres créés au démarrage en utilisant
createTreesPool
méthode. Nous avons également des valeurs d’angle prédéfinies pour chaque trajet de la sphère stockées danspathAngleValues
tableau.pathAngleValues = [1.52,1.57,1.62]; //… function createTreesPool () var maxTreesInPool = 10; var newTree; pour (var i = 0; i0,5) voie = Math.floor (Math.random () * 2); addTree (true, options [lane]); le
addPathTree
La méthode est appelée depuis la mise à jour quand il s'est écoulé suffisamment de temps après la plantation du dernier arbre. Il appelle à son tour leaddTree
méthode montrée précédemment avec un ensemble de paramètres différent où l’arbre est placé dans le chemin sélectionné. ledoTreeLogic
la méthode retournera l’arbre à la piscine une fois qu’elle disparaîtra.Interaction de l'utilisateur
Nous ajoutons un auditeur au document pour rechercher des événements de clavier pertinents. le
handleKeyDown
méthode définit lecurrentLane
valeur si les touches fléchées droite ou gauche sont enfoncées ou définit lebounceValue
valeur si la flèche vers le haut est enfoncée.document.onkeydown = handleKeyDown; //… function handleKeyDown (keyEvent) if (sautant) return; var validMove = true; if (keyEvent.keyCode === 37) // left si (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; sauter = vrai; validMove = false; if (validMove) jumping = true; valeur de rebond = 0,06;Dans
mettre à jour
, laX
la position de notre boule de neige s’incrémente lentement pour atteindre lacurrentLane
positionner en changeant de voie.Détection de collision
Il n'y a pas de physique réelle impliquée dans ce jeu particulier, bien que nous puissions utiliser divers cadres de physique pour notre objectif de détection de collision. Mais comme vous le savez bien, un moteur physique ajoute beaucoup de performance à notre jeu et nous devrions toujours essayer de voir si nous pouvons l'éviter..
Dans notre cas, nous calculons simplement la distance entre notre boule de neige et chaque arbre pour déclencher une collision s’ils sont très proches. Cela se passe dans le
doTreeLogic
méthode, appelée parmettre à jour
.function doTreeLogic () var oneTree; var treePos = new THREE.Vector3 (); treesInPath.forEach (fonction (élément, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); if (treePos.distanceTo (heroSphere.position)<=0.6) console.log("hit"); hasCollided=true; explode(); ); //…Comme vous avez pu le constater, tous les arbres actuellement présents sur notre chemin sont stockés dans le répertoire.
treesInPath
tableau. ledoTreeLogic
La méthode supprime également les arbres de l'affichage et dans la piscine une fois qu'ils sont hors de notre vue en utilisant le code ci-dessous.var treesToRemove = []; treesInPath.forEach (fonction (élément, index) oneTree = treesInPath [index]; treePos.setFromMatrixPosition (oneTree.matrixWorld); if (treePos.z> 6 && oneTree.visible) // est sorti de notre zone de vue treesToRemove.push (un arbre); ); var fromWhere; treesToRemove.forEach (fonction (élément, index) oneTree = treesToRemove [index]; fromWhere = treesInPath.indexOf (oneTree); treesInPath.splice (fromWhere, 1); treesPool.push (oneTree); onePree.Office = false; console; .log ("supprimer l'arbre"););Conclusion
La création d'un jeu en 3D est un processus compliqué si vous n'utilisez pas un outil visuel comme Unity. Cela peut sembler intimidant ou accablant, mais laissez-moi vous assurer qu'une fois que vous aurez compris, vous vous sentirez beaucoup plus puissant et créatif. J'aimerais que vous exploriez davantage en utilisant les divers cadres de physique ou systèmes de particules ou les exemples officiels.