Créer un jeu de physique 3D simple en utilisant Three.js et Physijs

Ce que vous allez créer

Dans ce didacticiel, vous allez apprendre à utiliser Physi.js pour ajouter de la physique de jeu à une scène 3D créée à l'aide de Three.js. Nous allons créer un jeu simple dans lequel nous conduirons un chariot pour collecter des objets, en utilisant des formes physiques de base et des contraintes physiques..

Ce tutoriel s'appuiera sur les concepts partagés dans mon précédent tutoriel Three.js. Je vous demanderais de le lire si vous êtes nouveau dans Three.js et dans la création de scènes 3D. 

En raison d'une limitation technique liée à l'hébergement de solutions Web worker sur JSFiddle, nous ne sommes pas en mesure d'intégrer le jeu interactif dans cette page de didacticiel. Veuillez utiliser le code source fourni pour consulter l'exemple de travail sur tout IDE basé sur un nuage ou en auto-hébergement..

1. Physique 3D sur le Web

Il existe actuellement de nombreux frameworks et moteurs pouvant être utilisés pour créer du contenu 3D pour le Web avec la physique. Parmi ceux qui méritent d’être mentionnés, citons Turbulenz, BabylonJS, PlayCanvas et l’apparente version de Unity WebGL. Mais lorsqu'il est question de popularité et de facilité d'utilisation, la plupart des gens préfèrent utiliser Three.js pour leurs expériences. Three.js étant un moteur de rendu et n'ayant pas de physique intégrée, nous devons explorer de nouveaux cadres pour ajouter la capacité physique..

Ammo.js est une solution de physique JavaScript très populaire, qui est un portage direct de la physique Bullet. Bien qu’il soit possible d’utiliser directement Ammo.js, il n’est pas très convivial pour les débutants et comporte beaucoup de codes standard pour chaque aspect. De plus, comme il n’est pas écrit manuellement, mais porté avec Emscripten, le code n’est pas facile à comprendre.. 

Une autre solution consiste à utiliser Cannon.js ou Physijs. Ce qui est intéressant avec Physijs, c’est que l’objectif est toujours de faciliter les choses, ce qui en fait le choix idéal pour les débutants. Il est basé sur Ammo.js et possède même une branche Cannon.js. Ce tutoriel utilise Physijs et Three.js pour construire un prototype de jeu fonctionnel doté de fonctionnalités physiques.. 

Une autre option, bien que simplifiée à l'excès, consisterait à utiliser le cadre Whitestorm, qui est un cadre à base de composants basé sur Three.js et Physijs.. 

2. Mise en place de Physijs

Nous avons besoin des fichiers ammo.js, physi.js, physijs_worker.js et three.js dans notre structure de dossiers ou notre environnement de codage pour pouvoir utiliser Physijs. Physijs utilise un outil Web pour utiliser différents threads pour les calculs physiques. La première étape du processus d’intégration consiste donc à configurer l’agent Web comme indiqué ci-dessous..

Physijs.scripts.worker = 'lib / physijs_worker.js'; Physijs.scripts.ammo = 'ammo.js';

À ce stade, la configuration est terminée et nous pouvons commencer à utiliser le cadre physique. Physijs s’est assuré qu’il suivait le style de codage de Three.js, et la plupart des concepts sont de simples remplacements du concept Three.js correspondant..

Étapes de base

Au lieu de TROIS.Scene, nous devons utiliser Physijs.Scene.

Physijs propose plusieurs maillages qui doivent être utilisés à la place de TROIS.Mesh. Les options disponibles sont PlaneMesh, BoxMesh, SphereMesh, CylinderMesh, ConeMesh, CapsuleMesh, ConvexMesh, ConcaveMesh, et HeighfieldMesh.

Nous devons appeler le scène.simuler méthode pour faire les calculs de physique soit dans le rendre méthode ou dans des intervalles fréquents. Permettez-moi de vous rappeler que les calculs physiques se déroulent dans un autre thread et ne seront pas synchronisés ou aussi rapides que la boucle de rendu de la scène.. 

Même le prochain appel à scène.simuler peut se produire pendant que les calculs précédents sont encore en cours d'exécution. Afin de le rendre correctement synchronisé avec les calculs de la physique, nous pourrions utiliser la scène de Physijs mettre à jour un événement.

scene.addEventListener ('update', function () // votre code. les calculs physiques ont terminé la mise à jour);

Afin d’enregistrer une collision sur un objet maillé Physijs nommé arbitrairement cube, nous pouvons écouter le collision un événement.

cube.addEventListener ('collision', fonction (objCollidedWith, linearVelOfCollision, angularVelOfCollision) );

Dans la méthode ci-dessus, ce se référera à cube, tandis que objCollidedWith est l'objet cube est entré en collision avec.

3. Exemple de prototype de jeu

Pour ce tutoriel, nous allons créer un jeu très simple basé sur la physique dans lequel nous utiliserons des contraintes physiques pour créer un véhicule. Le joueur peut utiliser les touches fléchées pour conduire le véhicule et collecter une balle qui rebondit qui apparaît au hasard dans l'aire de jeu.. 

Fait intéressant, Physijs a déjà une spéciale véhicule fonctionnalité qui peut être directement utilisée pour créer des véhicules, mais nous ne l'utilisons pas.

Le monde du jeu

Notre monde de jeu est un vaste terrain avec des murs sur ses quatre côtés comme ci-dessous.

Nous utilisons Physijs.BoxMesh pour le sol et les quatre murs comme indiqué dans le code ci-dessous.

ground_material = Physijs.createMaterial (nouveau THREE.MeshStandardMaterial (color: 0x00ff00), friction, .9 // faible restitution); // Ground Ground = new Physijs.BoxMesh (new THREE.BoxGeometry (150, 1, 150), ground_material, 0 // mass); ground.receiveShadow = true; scene.add (sol);

Notez l'utilisation de Physijs.createMaterial créer les matériaux de physique nécessaires en passant une valeur de frottement et une valeur de restitution. La valeur de frottement détermine l’adhérence au sol et la valeur de restitution, l’élasticité. Une chose importante à noter est que lorsque nous fournissons une valeur de masse de 0, nous créons un objet maillé stationnaire.

Le véhicule

Nous allons créer un véhicule spécial qui a deux parties connectées. La partie avant, qui a trois roues, fait office de moteur, et la partie arrière, qui a deux roues, fera office de chariot. La partie chariot est reliée à la partie moteur à l’aide d’une articulation articulée Physijs.HingeContraint

Les roues utilisent Physijs.DOFConstraint, qui est une contrainte de degré de liberté à fixer à la carrosserie du véhicule tout en maintenant la capacité de tourner de manière indépendante. Je vous invite à lire la documentation officielle sur les différentes contraintes disponibles dans Physijs.

Le corps du moteur et le corps du chariot sont simples BoxMesh des objets comme le sol ci-dessus, mais avec une valeur de masse définie. Ils sont connectés les uns aux autres à l'aide d'une articulation à charnière, comme indiqué dans le code suivant. Une articulation limite le mouvement de l'objet connecté comme celui d'une porte normale.

car.carriage_constraint = new Physijs.HingeConstraint (car.carriage, // premier objet à contraindre car.body, // contraint à ce nouveau THREE.Vector3 (6, 0, 0), // à ce stade nouveau THREE.Vector3 (0, 1, 0) // suivant cet axe); scene.addConstraint (car.carriage_constraint); car.carriage_constraint.setLimits (-Math.PI / 3, // angle de mouvement minimal, en radians Math.PI / 3, // angle de mouvement maximal, en radians 0, // appliqué en tant que facteur d'erreur de contrainte 0 / / contrôle le rebond à la limite (0,0 = pas de rebond));

La deuxième partie du code applique des limites à la rotation de la charnière, qui est dans ce cas entre -Math.PI / 3 et Math.PI / 3

Les roues utilisent une contrainte de degré de liberté qui peut être utilisée pour fixer des limites à la fois au mouvement linéaire et au mouvement angulaire dans les trois axes. Une méthode addWheel est créé pour l'ajout de roues, qui prend plusieurs paramètres. 

Les paramètres sont la position de la roue, le poids de la roue, que la roue soit grande ou petite et l'objet de référence de la roue. La méthode retourne une nouvelle création DOFConstraint, qui est utilisé pour conduire le véhicule.

fonction addWheel (wheel, pos, isBig, weight) var geometry = wheel_geometry; if (isBig) geometry = big_wheel_geometry;  wheel = new Physijs.CylinderMesh (géométrie, matériau de la roue, poids); wheel.name = "panier"; wheel.rotation.x = Math.PI / 2; wheel.position.set (pos.x, pos.y, pos.z); wheel.castShadow = true; scene.add (roue); wheel.setDamping (0, amortissement); var wheelConstraint = new Physijs.DOFConstraint (roue, voiture, corps, pos); if (isBig) wheelConstraint = new Physijs.DOFConstraint (wheel, car.carriage, pos);  scene.addConstraint (wheelConstraint); wheelConstraint.setAngularLowerLimit (x: 0, y: 0, z: 0); wheelConstraint.setAngularUpperLimit (x: 0, y: 0, z: 0); retour wheelConstraint; 

Les grosses roues doivent être attachées au chariot et les petites roues sont attachées au moteur. Une valeur d'amortissement est attribuée aux roues afin que leur vitesse angulaire soit réduite lorsqu'aucune force externe n'est appliquée. Cela garantit que le véhicule ralentit lorsque nous relâchons l'accélérateur. 

Nous explorerons la logique de conduite dans une section ultérieure. Le nom de chaque roue de maille avec les mailles de la voiture est attribué à Chariot à des fins d'identification lors du rappel de collision. Chaque contrainte de roue a différentes limites angulaires, qui sont définies indépendamment une fois qu'elles sont créées. Par exemple, voici le code de la roue avant centrale du moteur, car.wheel_fm, et la contrainte correspondante, car.wheel_fm_constraint.

car.wheel_fm_constraint = addWheel (car.wheel_fm, nouveau THREE.Vector3 (-7,5, 6,5, 0), false, 300); car.wheel_fm_constraint.setAngularLowerLimit (x: 0, y: -Math.PI / 8, z: 1); car.wheel_fm_constraint.setAngularUpperLimit (x: 0, y: Math.PI / 8, z: 0);

Le ballon

La balle est un Physijs.SphereMesh objet avec une valeur de masse inférieure à 20. Nous utilisons le releaseBall méthode pour positionner la balle au hasard dans notre zone de jeu à chaque fois qu'elle est collectée. 

function addBall () var ball_material = Physijs.createMaterial (nouveau THREE.MeshStandardMaterial (color: 0x0000ff, shading: THREE.FlatShading), frottement, .9 // bonne restitution); var ball_geometry = new THREE.SphereGeometry (2,16,16); ball = new Physijs.SphereMesh (ball_geometry, ball_material, 20); ball.castShadow = true; releaseBall (); scene.add (balle); ball.setDamping (0,0,9); ball.addEventListener ('collision', onCollision);  function releaseBall () var plage = 10 + Math.random () * 30; ball.position.y = 16; ball.position.x = ((2 * Math.floor (Math.random () * 2)) - 1) * plage; ball.position.z = ((2 * Math.floor (Math.random () * 2)) - 1) * plage; ball .__ dirtyPosition = true; // force la nouvelle position // Vous devez également annuler la vélocité de l'objet ball.setLinearVelocity (new THREE.Vector3 (0, 0, 0)); ball.setAngularVelocity (nouveau THREE.Vector3 (0, 0, 0)); 

Une chose à noter est le fait que nous devons outrepasser les valeurs de position définies par la simulation physique afin de repositionner notre balle. Pour cela, nous utilisons le __dirtyPosition drapeau, qui garantit que le nouveau poste est utilisé pour la simulation physique ultérieure.

Le ballon est récupéré lorsqu'il heurte une partie du véhicule qui se passe dans le onCollision méthode d'écoute.

function onCollision (other_object, linear_velocity, angular_velocity) if (other_object.name === "cart") score ++; releaseBall (); scoreText.innerHTML = score.toString (); 

Conduire le véhicule

Nous ajoutons des écouteurs d’événement pour le onkeydown et onkeyup événements du document, où nous déterminons la code clé définir les valeurs des contraintes de roue correspondantes. La théorie est que la roue avant unique du moteur contrôle la rotation de notre véhicule et que les deux roues à l'arrière du moteur contrôlent l'accélération et la décélération. Les roues du chariot ne jouent aucun rôle dans la conduite.

document.onkeydown = handleKeyDown; document.onkeyup = handleKeyUp; function handleKeyDown (keyEvent) switch (keyEvent.keyCode) cas 37: // car.wheel_fm_constraint.configureAngularMotor (1, -Math.PI / 3, Math.PI / 3, 1, 200); car.wheel_fm_constraint.enableAngularMotor (1); Pause; case 39: // car.wheel_fm_constraint.configureAngularMotor (1, -Math.PI / 3, Math.PI / 3, -1, 200); car.wheel_fm_constraint.enableAngularMotor (1); Pause; case 38: // Up car.wheel_bl_constraint.configureAngularMotor (2, 1, 0, 6, 2000); car.wheel_br_constraint.configureAngularMotor (2, 1, 0, 6, 2000); car.wheel_bl_constraint.enableAngularMotor (2); car.wheel_br_constraint.enableAngularMotor (2); Pause; cas 40: // Down car.wheel_bl_constraint.configureAngularMotor (2, 1, 0, -6, 2000); car.wheel_br_constraint.configureAngularMotor (2, 1, 0, -6, 2000); car.wheel_bl_constraint.enableAngularMotor (2); car.wheel_br_constraint.enableAngularMotor (2); Pause;  function handleKeyUp (keyEvent) switch (keyEvent.keyCode) cas 37: // car carww_fm_constraint.disableAngularMotor (1); Pause; case 39: // car carwheel_fm_constraint.disableAngularMotor (1); Pause; case 38: // Up car.wheel_bl_constraint.disableAngularMotor (2); car.wheel_br_constraint.disableAngularMotor (2); Pause; case 40: // down car.wheel_bl_constraint.disableAngularMotor (2); car.wheel_br_constraint.disableAngularMotor (2); Pause; 

le DOFConstraint utilise le enableAngularMotor méthode pour appliquer la vitesse angulaire sur la roue qui tourne la roue en fonction de la valeur de l'axe fournie en tant que paramètre. Fondamentalement, nous ne faisons que tourner les roues et le mouvement du véhicule se produit en raison de la réaction de frottement du sol, comme dans le monde réel..

Nous ne pouvons pas intégrer le jeu de travail dans cette page comme mentionné au début du didacticiel. Veuillez parcourir la source complète pour comprendre comment tout est câblé.

Conclusion

Il s’agit d’une introduction très simple à la physique dans votre monde 3D Three.js. La documentation de Physijs fait cruellement défaut, mais il existe déjà de nombreux exemples qui méritent d’être examinés. Physijs est un framework très convivial pour les débutants, comme Three.js, lorsque l’on considère la complexité présente sous le capot.. 

JavaScript est clairement populaire pour le développement de jeux ainsi que le développement Web. Ce n’est pas sans ses courbes d’apprentissage, et il existe de nombreux cadres et bibliothèques pour vous tenir occupé. Si vous recherchez des ressources supplémentaires à étudier ou à utiliser dans votre travail, consultez ce que nous avons à votre disposition sur le marché Envato..

J'espère que ce tutoriel vous aidera à explorer le monde intéressant de la physique des jeux Web 3D.