
Dans l'
article précédent, nous avons essayé de créer une scène de base dans A-Frame afin d'essayer les concepts de base du cadre dans la pratique. Dans cet article, je voudrais partager mon expérience dans la création d'un jeu sur A-Frame - Recycle! VR Le référentiel de projet est disponible sur le
lien suivant .
Recyclage!?
L'idée de créer un jeu est venue presque immédiatement dès que j'ai découvert le Web VR. Bien qu'en général, je pense que les jeux sur le web seront en tout cas inférieurs aux bons projets même pour les appareils mobiles, sans parler des ordinateurs personnels et des consoles. Mais il me semble que le jeu est le test le plus difficile. J'ai commencé à penser exactement à ce que je pouvais faire. J'ai regardé d'autres projets et j'ai été immédiatement frappé par l'opportunité de prendre quelque chose à l'aide du contrôleur. Et depuis que je suis connecté à une organisation qui s'occupe de la collecte séparée des déchets depuis un certain temps, la réponse est venue d'elle-même. Le recyclage. Nous prenons la poubelle et la jetons à la poubelle. Quoi de plus simple. Mais tout s'est avéré pas si simple, et cela, en fait, sera discuté plus loin.
Sélection du cadre
Lors du démarrage des travaux sur le jeu, je ne connaissais que 2 frameworks plus ou moins sérieux: React 360, A-Frame. De toute évidence, A-Frame était le plus approprié pour créer le jeu. Oui, maintenant je sais qu'il existe toujours un moteur de jeu PlayCanvas qui prend également en charge la VR, mais il est trop tard. De plus, il s'est avéré que A-Frame n'est pas non plus mauvais pour créer des jeux.
Par où commencer?
J'ai commencé par étudier des exemples de jeux officiels de développeurs A-Frame. L'avantage de tant de choses. A-Blast, A-Painter, Museum, Super Craft et maintenant aussi Gunters of Oasis. De tous les projets présentés, j'ai le plus aimé A-Blast - un jeu de tir dans lequel vous devez vous battre avec les créatures les plus mignonnes de l'univers. Par conséquent, je voulais prendre ce jeu comme modèle pour le mien. Mais ça n'a pas marché. Et la raison en était la structure du jeu. Il me semblait qu'elle était trop encombrée et non réfléchie. Il n'est peut-être pas nécessaire d'en faire plus, mais je voulais faire quelque chose de plus pratique et plus facile à comprendre.
La structure
La structure
A-Blast ne représente qu'un seul point d'entrée - le fichier index.html, qui contient une scène avec tous les actifs, les entités de jeu de base, les contrôles et tout en général.

Comme vous pouvez le voir sur la capture d'écran, en plus des composants et des systèmes nécessaires (A-Frame utilise le modèle Entity Component System), il y a aussi des balles et des ennemis - essentiellement les mêmes systèmes, mais pour une raison quelconque, ils ont leur propre wrapper. Dites généralement que ce code n'est pas facile à comprendre. J'ai donc décidé de réfléchir à la façon dont ce code peut être structuré. La première idée est de décomposer la scène en ses parties constituantes. Pourquoi un routeur et des modèles seraient-ils utiles, qui rendraient telle ou telle partie de la scène? Après avoir recherché la première et la seconde (non, pas cinq minutes), je n'ai rien trouvé. Même si je suis partisan de la règle, n'écrivez pas de vélos, mais cette fois j'ai dû écrire ma décision. Cependant, quelque part en 2-3 semaines, je suis tombé sur des modèles de Kevin Ngo. Mais c'était trop tard.
Routeur et modèles

Et donc,
un modèle de routeur de trame entre en scène. Que peut-il faire? Comme mentionné ci-dessus, sa tâche principale est de rendre les parties nécessaires du jeu, par exemple, l'écran de titre, le terrain de jeu, l'écran de fin du jeu, etc. Comment faire En principe, vous pouvez trouver tout ce dont vous avez besoin dans la documentation du module sur github, mais en bref, nous avons les éléments suivants:
<a-scene router> ... <a-route id="start-screen" template="start-screen"></a-route> <a-route id="game-field" template="game-field"></a-route> <a-route id="game-over" template="game-over"></a-route> <a-route id="how-to-play" template="how-to-play"></a-route> ... <a-template name="controls"></a-template> ... </a-scene>
- Nous ajoutons le composant routeur à la scène.
- Ajoutez un itinéraire pour chaque partie de l'application (images de la scène). Un itinéraire pour l'écran d'accueil, un autre pour le terrain de jeu, etc.
- Rendre les modèles directement via a-templates
- Si nécessaire, nous changeons d'itinéraire
this.el.systems.router.changeRoute('game-field');
Remarque : cet exemple fait référence au code de scène, nous pouvons donc appeler directement le système de routeur. - Nous définissons et connectons les modèles, quelque chose comme ceci:
AFRAME.registerTemplate('game-field', ` <a-sub-assets> <a-asset-item id="glass" src="/assets/models/glass_bottle.gltf"></a-asset-item> ... <audio id="fail" src="/assets/sounds/fail.wav" preload></audio> </a-sub-assets> <a-template name="button" options="text: EXIT; position: 0 1 4; rotation: 0 180 0; event: stop-game"></a-template> <a-entity id="indicator" indicator visible="false" position="0 1 -2" text="align: center; width: 4; color: #00A105; value: -1" ></a-entity> <a-entity game-field-manager></a-entity> `);
Remarque: les sous-actifs vous permettent de charger des actifs ainsi que des actifs, mais seulement avec la différence qu'il y a une vérification par défaut et si l'actif est déjà ajouté, il ne sera pas ajouté à nouveau lorsque l'itinéraire sera modifié.
Remarque 2: Normalement, vous ne pouvez utiliser des modèles qu'avec la chaîne de modèle ES6. Sinon, il peut se transformer en «chaîne» + var + «chaîne», pas cool. Kevin, par exemple, prend en charge les moteurs de modèle. Mais pourquoi le compliquer, non?
Ainsi, vous pouvez créer une structure d'application pratique qui contiendra les éléments suivants:
composants, systèmes, modèles, états, bibliothèques . Rien de plus et tout est sur les étagères.
Manipulation d'objets

La toute première tâche à résoudre était la manipulation d'objets. J'avais besoin d 'un lancer fonctionnel. Au début, j'ai commencé à réfléchir à la façon de créer un tel composant à partir de zéro. Purement au niveau philistin, une telle réflexion est admissible: nous avons un contrôleur (dans le cas d'un bureau c'est un curseur), il a une position. Nous avons aussi certains objets, par exemple des cubes, ils ont aussi une position. En changeant la position du contrôleur, nous devons changer la position de l'objet. C'est simple? Donc, en fait, oui, mais ça ne marchera pas. Je mentionnerai quelques points d'une très longue liste pour vous en convaincre:
- Le curseur dans A-Frame est un descendant d'une caméra et a des coordonnées relatives;
- La position du contrôleur ne suffit pas, vous devez toujours tenir compte de l'orientation, de la distance à l'objet, de la position de la caméra (joueur);
- Pour les objets avec un corps physique, cela ne fonctionnera pas du tout, car les coordonnées de la géométrie sont liées aux coordonnées du corps.
C'est bien que le bon M. Wil Murphy et ses amis aient fait
un super-cadre . En substance, cette bibliothèque contient tous les composants nécessaires:
- hoverable . Orientation. Pointez le contrôleur ou le curseur sur la zone de collision de l'objet (généralement l'objet entier)
- saisissable : capture. Saisissez un objet à l'aide du bouton approprié et faites-le glisser
- extensible : saisir avec les deux mains et étirer \ presser
- draggable \ dropable : Essentiellement nécessaire pour déterminer l'événement «l'élément a été lancé à un emplacement spécifique»
Vous pouvez trouver tout ce dont vous avez besoin pour configurer et connecter des super-mains dans le référentiel mentionné ci-dessus. Je veux juste attirer l'attention sur un certain nombre de nuances:
- Créez des mixins séparés pour la main droite et la main gauche. Séparez les composants par type de périphériques pris en charge. Par exemple, la main droite, en plus des commandes oculus-touch, vive-controls, windows-motion-control, il peut également y avoir des commandes oculus-go et des commandes gear-vr. La main gauche doit être cachée pour les casques mobiles BP. Chaque contrôleur doit contenir à la fois des mains mixin et un composant super-mains. Un exemple ;
- Si vous avez spécifié des objets: .clsname pour reycaster, n'oubliez pas de l'ajouter à chaque élément qui peut être pris à l'aide du contrôleur, sinon aucun événement pour les super-mains échouera. Bien sûr, si colliderEvent: raycaster-intersection ;
- Faire glisser avec la souris projette les coordonnées 2D dans le monde 3D, il est donc préférable d'utiliser le curseur pour le bureau.
Ajouter de la physique
Ajouter de la physique à un cadre est en fait très simple. Il existe un
système spécial pour cela. Il est ajouté à la scène et le tour est joué, la physique est déjà dans votre poche.
<a-scene physics="debug: false"> <a-box dynamic-body position="0 1 -2"></a-box> <a-box id="floor" static-body></a-box> </a-scene>
Mark :
debug: true permet de visualiser les corps physiques liés à la géométrie. Il est pratique lorsque vous devez «contourner» un objet.
En fait, c'est un wrapper pour
cannon.js qui fait tout le sale boulot de comparer la géométrie et les corps physiques pour vous. Encore une fois, sur le fonctionnement de ce système, vous pouvez le trouver dans la description du référentiel. Et je voudrais m'attarder sur un seul point important pour mon jeu.
Je devais m'assurer qu'en appuyant sur le bouton de la corbeille, une certaine force était définie (plus vous maintenez le bouton enfoncé, plus la force est grande). Il s'est avéré que cette tâche n'est pas aussi simple qu'il y paraît à première vue. Eh bien, qu'est-ce qui est si compliqué? - vous dites, nous
appliquons Impluse et le tour est joué. Pas vraiment ... Il définit la rotation d'un objet le long d'un vecteur appliqué au centre du corps. En utilisant cette méthode, nous ne pouvons qu'émuler un yule. Cependant, si vous définissez un vecteur avec l'angle correct par rapport au plan, vous pouvez obtenir quelque chose de similaire à une poussée. Mais ce n'est pas ce dont j'avais besoin.
Il s'est avéré que j'avais besoin de vitesse lors du réglage de ce paramètre, l'objet commence son mouvement dans une direction donnée. Cette direction est spécifiée par le vecteur. Et ici, le plaisir commence. Comment trouver ce vecteur? J'ai trouvé deux options:
- Obtenez le quaternion du contrôleur (ou de la caméra pour le bureau), qui décrit son orientation dans l'espace. Créez un vecteur V1 = <1,1,1>, multipliez-le par la force de projection et appliquez l'orientation à tout cela.
const velocityVector = new THREE.Vector3(1,1,1); velocityVector.multiplyScalar(this.force); velocityVector.applyQuaternion(controllerQuaternion); this.grabbed.body.velocity.set(velocityVector.x, velocityVector.y, velocityVector.z);
- Trouvez la position du contrôleur (curseur) et la position de l'objet lancé. Calculez le vecteur de direction pour deux points. Normalisez le vecteur. Et multipliez-le par la force.
const directionX = (trashPosition.x - zeroPosition.x); const directionZ = (trashPosition.z - zeroPosition.z); const vectorsLength = Math.sqrt(Math.pow(directionX, 2) + Math.pow(directionZ, 2)); const x = (directionX / vectorsLength) * this.force; const y = this.force; const z = (directionZ / vectorsLength) * this.force; this.grabbed.body.velocity.set(x , y, z );
J'ai choisi la deuxième option car je ne peux y compter que x et z. Et placez-vous vous-même, car j'avais besoin d'un lancer le long de l'arc pour que les déchets déversés tombent dans le panier, malgré que l'utilisateur tienne le contrôleur.
Quelques mots sur le modèle

Dès le début, j'ai décidé de faire un jeu de style
low-poly . Bien que WebGL soit capable de rendre des scènes relativement complexes aujourd'hui, ses performances sont toujours inférieures aux bibliothèques avancées telles que DirectX, Vulkan, Mantle, etc. Tout dépend également des performances de l'appareil de l'utilisateur. Comme je voudrais me concentrer sur les casques mobiles BP plus abordables (Oculus Go, Gear VR), je pense que le low-poly est l'une des rares solutions pour créer une application ou un jeu VR. Bien sûr, tout dépend du volume.
D'accord, le low-poly est tellement low-poly, mais comment faire tout cela? Tout est très simple, il existe un bon outil open source -
Blender . Croyez-moi, il est capable de beaucoup, mais pour des tâches simples, il ne convient pas tout à fait. Il existe de nombreux supports de formation liés à la modélisation dans Blender et il ne sera pas difficile de les trouver. Je voulais juste concentrer votre attention sur un certain nombre de points liés au développement web:
- L'exportateur Three-js est obsolète. Besoin de trouver et de fournir un exportateur GLTF . GLTF est un format spécial conçu pour le Web. Et oui, c'est JSON.
- GLTF ne prend pas en charge Cycles Renderer, vous devez donc utiliser Blender Renderer. Et cela signifie qu'il n'y aura pas de nœuds frais, de transformations de couleurs, de reflets métalliques (cela peut être fait différemment).
- Vous devez exporter uniquement l'élément sélectionné. Vous n'avez pas besoin de caméras et de lumières supplémentaires? Fichier> Exporter> gltf 2.0. Dans le menu de gauche, Exporter GLTF 2.0> Exporter uniquement sélectionné.
- Nous commençons à exporter à partir de la position <0, 0, 0> dans Blender. Il est préférable de mettre à l'échelle au même endroit, afin que plus tard vous n'utilisiez pas le composant d'échelle dans un cadre.
- Si vous dessinez un espace ouvert comme dans Recycle! VR, vous devez ajouter des objets uniquement à l'endroit où le joueur peut théoriquement regarder. Derrière, derrière les maisons, dans le Recycle! il y a quelques arbres et seulement à l'endroit où l'utilisateur peut les voir. Il n’est pas nécessaire de surcharger la scène.
- Si vous devez changer le matériau du modèle, vous devez attendre qu'il se charge, obtenir le modèle lui-même, en extraire tous les nœuds (GLTF contient des informations non seulement sur les maillages)
e.detail.model.traverse((node) => { if (node.isMesh) { node.material.color = new THREE.Color(someColor); } });
En conclusion
Merci à tous pour votre attention! Je vous rappelle encore une fois que le référentiel du projet est disponible sur le
lien suivant . Quiconque veut apporter quelque chose de nouveau à ce jeu - bienvenue.