Fermer

mars 13, 2019

Comment construire un jeu de coureur sans fin en réalité virtuelle (2e partie)


Si vous vous êtes déjà demandé comment sont construits des jeux avec prise en charge de casques sans clavier pour clavier, ce tutoriel explique ce que vous recherchez. Voici comment vous pouvez aussi donner vie à un jeu de réalité virtuelle de base fonctionnel.

Dans Partie 1 de cette série, nous avons vu comment créer un modèle de réalité virtuelle avec des effets d’éclairage et d’animation. Dans cette partie, nous allons implémenter la logique centrale du jeu et utiliser des manipulations plus avancées de l’environnement A-Frame pour construire la partie "jeu" de cette application. À la fin, vous aurez un jeu de réalité virtuelle opérationnel avec un véritable défi.

Ce didacticiel comprend un certain nombre d’étapes, dont (sans y être limité) la détection de collision et davantage de concepts A-Frame tels que mixins.

Conditions préalables

Comme dans le didacticiel précédent, vous aurez besoin des éléments suivants:

  • accès à Internet (plus précisément à glitch.com );
  • Un projet Glitch complété à partir de partie 1 . (Vous pouvez continuer à partir du produit fini en allant à https://glitch.com/edit/#!/ergo-1 et en cliquant sur «Remix pour éditer»;
  • Un casque de réalité virtuelle (facultatif). (recommandé) (J'utilise Google Cardboard, qui est proposé à 15 dollars pièce.)

Étape 1: Conception des obstacles

Dans cette étape, vous concevez les arbres que nous utiliserons comme obstacles. ajoutez une animation simple qui déplace les arbres vers le joueur, comme suit:

 Arbres modèles se dirigeant vers le joueur
Arbres modèles se dirigeant vers le joueur ( Grand aperçu )

Ces arbres serviront comme les modèles pour les obstacles que vous avez générés pendant le jeu. Pour la dernière partie de cette étape, nous allons ensuite supprimer ces «arbres de modèle».

Pour commencer, ajoutez un certain nombre de différentes A-Frame mixins . sont des ensembles de propriétés de composant couramment utilisés. Dans notre cas, tous nos arbres auront la même couleur, hauteur, largeur, profondeur, etc. En d'autres termes, tous vos arbres se ressembleront et utiliseront donc quelques mixins partagés.

Note : Dans notre didacticiel, vos seuls actifs sont des mixins. Visitez la page A-Frame Mixins pour en savoir plus.

Dans votre éditeur, accédez à index.html . Juste après votre ciel et devant vos lumières, ajoutez une nouvelle entité A-Frame pour conserver vos actifs:








 ...

Dans votre nouvelle entité a-assets commencez par ajouter un mixin pour votre feuillage. Ce mixins définit les propriétés communes pour le feuillage de l’arborescence du modèle. En bref, il s’agit d’une pyramide blanche et ombrée, pour un effet low poly.


    

Juste en dessous de votre mélange de feuillage, ajoutez un mélange pour le tronc. Ce coffre sera un petit prisme rectangulaire blanc.


   ...
   

Ajoutez ensuite les objets d'arborescence de modèles qui utiliseront ces mixins. Toujours dans index.html faites défiler jusqu'à la section des plates-formes. Juste avant la section joueur, ajoutez une nouvelle section arborescente, avec trois entités vides:



  
  
  
  

  
   ...

Ensuite, repositionnez, redimensionnez et ajoutez des ombres aux entités arborescentes.





Maintenant, remplissez les entités arborescentes avec un tronc et un feuillage, à l'aide des mixins définis précédemment.



  
  


  
  


  
  

Accédez à votre aperçu. Maintenant, voyez les arbres modèles suivants.

 Arbres modèles pour les obstacles
Arbres modèles pour les obstacles ( Grand aperçu )

Maintenant, animez les arbres depuis un emplacement éloigné sur la plate-forme en direction de l'utilisateur. Comme auparavant, utilisez le a-animation tag:



   ...
  


   ...
  


   ...
  

Assurez-vous que votre code correspond à ce qui suit:





  
  
  



  
  
  



  
  
  



 ...

Naviguez vers votre aperçu et vous verrez maintenant les arbres se déplacer vers vous.

 Arbres modèles se dirigeant vers le joueur
Arbres modèles se dirigeant vers le joueurTemple des arbres se dirigeant vers le joueur ( Grand aperçu ) [19659015] Revenez à votre éditeur. Cette fois-ci, sélectionnez assets / ergo.js . Dans la section des jeux, configurez les arbres après le chargement de la fenêtre.

 / ********
 * JEU *
 ******** /

...

window.onload = function () {
  setupTrees ();
}

Sous les commandes, mais avant la section Game, ajoutez une nouvelle section TREES . Dans cette section, définissez une nouvelle fonction setupTrees .

 / ************
 * LES CONTRÔLES *
 ************ /

...

/ *********
 * DES ARBRES *
 ********* /

fonction setupTrees () {
}

/ ********
 * JEU *
 ******** /

...

Dans la nouvelle fonction setupTrees obtenez des références aux objets DOM de l'arbre de gabarit et rendez ces références disponibles au niveau mondial.

 / *********.
 * DES ARBRES *
 ********* /

var templateTreeLeft;
var templateTreeCenter;
var templateTreeRight;

fonction setupTrees () {
  templateTreeLeft = document.getElementById ('template-tree-left');
  templateTreeCenter = document.getElementById ('template-tree-center');
  templateTreeRight = document.getElementById ('template-tree-right');
}

Ensuite, définissez un nouvel utilitaire removeTree . Avec cet utilitaire, vous pouvez ensuite supprimer les arborescences de modèles de la scène. Sous la fonction setupTrees définissez votre nouvel utilitaire.

 function setupTrees () {
    ...
}

fonction removeTree (arbre) {
  tree.parentNode.removeChild (arbre);
}

De retour dans setupTrees utilisez le nouvel utilitaire pour supprimer les arborescences de modèles.

 function setupTrees () {
  ...

  removeTree (templateTreeLeft);
  removeTree (templateTreeRight);
  removeTree (templateTreeCenter);
}

Assurez-vous que votre arbre et vos sections de jeu correspondent aux éléments suivants:

 / *********
 * DES ARBRES *
 ********* /

var templateTreeLeft;
var templateTreeCenter;
var templateTreeRight;

fonction setupTrees () {
  templateTreeLeft = document.getElementById ('template-tree-left');
  templateTreeCenter = document.getElementById ('template-tree-center');
  templateTreeRight = document.getElementById ('template-tree-right');

  removeTree (templateTreeLeft);
  removeTree (templateTreeRight);
  removeTree (templateTreeCenter);
}

fonction removeTree (arbre) {
  tree.parentNode.removeChild (arbre);
}

/ ********
 * JEU *
 ******** /

setupControls (); // TODO: AFRAME.registerComponent doit avoir lieu avant window.onload?

window.onload = function () {
  setupTrees ();
}

Rouvrez votre aperçu, et vos arbres devraient maintenant être absents. L'aperçu doit correspondre à notre jeu au début de ce didacticiel.

 Produit fini de la partie 1
Produit fini de la pièce 1 ( Grand aperçu )

Ceci termine la conception de l'arborescence du modèle.

Dans cette étape, nous avons couvert et utilisé les mixins A-Frame, ce qui nous permet de simplifier le code en définissant des propriétés communes. De plus, nous avons tiré parti de l'intégration de A-Frame avec le DOM pour supprimer des objets de la scène VR de l'A-Frame.

À l'étape suivante, nous allons générer de multiples obstacles et concevoir un algorithme simple permettant de répartir les arbres entre différentes voies.

Étape 2: Obstacles lors de la ponte

Dans un jeu sans fin de coureurs, notre objectif est d’éviter que des obstacles ne nous volent. Dans cette implémentation particulière du jeu, nous utilisons trois voies comme cela est le plus courant.

Contrairement à la plupart des jeux de coureurs sans fin, ce jeu ne prend en charge que les mouvements à gauche et à droite . Cela impose une contrainte à notre algorithme pour générer des obstacles: nous ne pouvons pas avoir trois obstacles dans les trois voies, en même temps, qui volent vers nous. Si cela se produit, le joueur n'aura aucune chance de survie. En conséquence, notre algorithme de génération doit prendre en compte cette contrainte.

À cette étape, toutes les modifications de code seront effectuées dans assets / ergo.js . Le fichier HTML restera le même. Accédez à la section TREES de assets / ergo.js .

Pour commencer, nous allons ajouter des utilitaires pour générer des arbres. Chaque arbre aura besoin d'un identifiant unique, que nous définirons naïvement comme étant le nombre d'arbres existant lors de la création de l'arbre. Commencez par suivre le nombre d'arbres dans une variable globale.

 / *********
 * DES ARBRES *
 ********* /

...
var numberOfTrees = 0;

fonction setupTrees () {
...

Ensuite, nous allons initialiser une référence à l'élément DOM du conteneur d'arborescence, à laquelle notre fonction spawn ajoutera des arborescences. Toujours dans la section TREES ajoutez une variable globale, puis indiquez la référence.

 ...
var treeContainer;
var numberOfTrees ...

fonction setupTrees () {
    ...
    templateTreeRight = ...
    treeContainer = document.getElementById ('tree-container');
    
    removeTree (...);
    ...
}

En utilisant à la fois le nombre d'arbres et le conteneur d'arbres, écrivez une nouvelle fonction qui génère des arbres.

 function removeTree (tree) {
    ...
}

fonction addTree (el) {
  numberOfTrees + = 1;
  el.id = 'tree-' + numberOfTrees;
  treeContainer.appendChild (el);
}

...

Pour plus de facilité d'utilisation, vous allez créer une deuxième fonction qui ajoute le bon arbre à la bonne voie. Pour commencer, définissez un nouveau tableau templates dans la section TREES .

 var templates;
var treeContainer;
...

fonction setupTrees () {
    ...
    modèles = [templateTreeLeft, templateTreeCenter, templateTreeRight];

    removeTree (...);
    ...
}

À l'aide de ce tableau de modèles, ajoutez un utilitaire qui génère des arbres dans une voie spécifique, à l'aide d'un identifiant représentant la gauche, le milieu ou la droite.

 function fonction addTree (el) {
    ...
}

fonction addTreeTo (position_index) {
  var template = templates [position_index];
  addTree (template.cloneNode (true));
}

Accédez à votre aperçu et ouvrez votre console de développeur. Dans votre console de développeur, appelez la fonction globale addTreeTo .

> addTreeTo (0); Nombre d'arbres apparaissant dans la voie de gauche
 Invoquer addTreeTo manuellement
Invoke addTreeTo manuellement ( Grand aperçu )

Vous allez maintenant écrire un algorithme qui fait apparaître des arbres au hasard:

  1. Choisissez une piste au hasard (cela n'a pas encore été choisi, pour ce pas de temps);
  2. Apparaît un arbre avec une certaine probabilité;
  3. Si le nombre maximum d'arbres a été créé pour ce pas de temps, arrêtez. Sinon, répétez l'étape 1.

Pour appliquer cet algorithme, nous allons mélanger la liste des modèles et en traiter un par un. Commencez par définir une nouvelle fonction, addTreesRandomly qui accepte un certain nombre d'arguments de mots clés différents.

 fonction addTreeTo (position_index) {
    ...
}

/ **
 * Ajouter n'importe quel nombre d'arbres sur différentes voies, au hasard.
 ** /
fonction addTreesRandomly (
  {
    probTreeLeft = 0,5,
    probTreeCenter = 0,5,
    probTreeRight = 0,5,
    maxNumberTrees = 2
  } = {}) {
}

Dans votre nouvelle fonction addTreesRandomly définissez une liste d'arbres modèles et de la liste aléatoire.

 fonction addTreesRandomly (...) {
  arbres var = [
    {probability: probTreeLeft,   position_index: 0},
    {probability: probTreeCenter, position_index: 1},
    {probability: probTreeRight,  position_index: 2},
  ]
  mélanger (arbres);
}

Faites défiler l'écran jusqu'au bas du fichier et créez une nouvelle section d'utilitaires, ainsi qu'un nouvel utilitaire shuffle . Cet utilitaire va mélanger un tableau en place.

 / ********
 * JEU *
 ******** /

...

/ **************
 * UTILITAIRES *
 ************* /

/ **
 * Mélange tableau en place.
 * @param {Array} un éléments Un tableau contenant les éléments.
 * /
fonction shuffle (a) {
   var j, x, i;
   pour (i = une.longueur - 1; i> 0; i--) {
       j = Math.floor (Math.random () * (i + 1));
       x = a [i];
       a [i] = a [j];
       a [j] = x;
   }
   retourne un;
}

Revenez à la fonction addTreesRandomly dans votre section Arbres. Ajoutez une nouvelle variable numberOfTreesAdded et parcourez la liste des arbres définie ci-dessus.

 function addTreesRandomly (...) {
  ...
  var numberOfTreesAdded = 0;
  trees.forEach (fonction (arbre) {
  });
}

Dans l'itération au-dessus des arbres, n'engendrer un arbre qu'avec une certaine probabilité et uniquement si le nombre d'arbres ajoutés ne dépasse pas 2 . Mettez à jour la boucle for comme suit.

 function addTreesRandomly (...) {
  ...
  trees.forEach (fonction (arbre) {
    if (Math.random () 

Pour conclure la fonction, retourne le nombre d'arbres ajoutés.

 function addTreesRandomly (...) {
  ...
  numéro de retour des arbres ajoutés;
}

Vérifiez que votre fonction addTreesRandomly correspond à la suivante:

 / **
 * Ajouter n'importe quel nombre d'arbres sur différentes voies, au hasard.
 ** /
fonction addTreesRandomly (
  {
    probTreeLeft = 0,5,
    probTreeCenter = 0,5,
    probTreeRight = 0,5,
    maxNumberTrees = 2
  } = {}) {

  arbres var = [
    {probability: probTreeLeft,   position_index: 0},
    {probability: probTreeCenter, position_index: 1},
    {probability: probTreeRight,  position_index: 2},
  ]
  mélanger (arbres);

  var numberOfTreesAdded = 0;
  trees.forEach (fonction (arbre) {
    if (Math.random () 

Enfin, pour générer automatiquement des arbres, configurez une minuterie qui s'exécute à intervalles réguliers. Définissez la minuterie globalement et ajoutez une nouvelle fonction de démontage pour cette minuterie.

 / * ********
 * DES ARBRES *
 ********* /
...
var treeTimer;

fonction setupTrees () {
...
}

fonction teardownTrees () {
  clearInterval (treeTimer);
}

Ensuite, définissez une nouvelle fonction qui initialise le chronomètre et le sauvegarde dans la variable globale définie précédemment. Le temporisateur ci-dessous est exécuté toutes les demi-secondes.

 function addTreesRandomlyLoop ({intervalLength = 500} = {}) {
  treeTimer = setInterval (addTreesRandomly, intervalLength);
}

Enfin, démarrez le chronomètre une fois la fenêtre chargée, à partir de la section Jeu.

 / ********
 * JEU *
 ******** /
...
window.onload = function () {
  ...
  addTreesRandomlyLoop ();
}

Accédez à votre aperçu et les arbres apparaîtront au hasard. Notez qu'il n'y a jamais trois arbres à la fois.

 Arbre frayant au hasard
Arbre frayant au hasard ( Grand aperçu )

Ceci conclut l'étape des obstacles. Nous avons réussi à prendre plusieurs arbres de modèles et à générer un nombre infini d’obstacles à partir des modèles. Notre algorithme de génération respecte également les contraintes naturelles du jeu pour le rendre jouable.

Ajoutons ensuite le test de collision à la prochaine étape

Étape 3: Test de collision

Dans cette section, nous allons implémenter les tests de collision. entre les obstacles et le joueur. Ces tests de collision sont plus simples que dans la plupart des autres jeux. Cependant, le joueur ne se déplace que sur l’axe des x. Par conséquent, chaque fois qu’un arbre passe sur l’axe des x, vérifiez si la voie de l’arbre est identique à celle du joueur. Nous allons implémenter cette vérification simple pour ce jeu.

Accédez à index.html jusqu’à la section TREES . Ici, nous allons ajouter des informations sur les voies à chacun des arbres. Pour chacun des arbres, ajoutez data-tree-position-index = comme suit. Ajoutez également class = "tree" afin que nous puissions facilement sélectionner tous les arbres le long de la ligne:

  





Accédez à assets / ergo.js et appelez une nouvelle fonction setupCollisions dans la section GAME . De plus, définissez une nouvelle variable globale isGameRunning qui indique si un jeu existant est déjà en cours d'exécution.

 / ********
 * JEU *
 ******** /

var isGameRunning = false;

setupControls ();
setupCollision ();

window.onload = function () {
...

Définissez une nouvelle section COLLISIONS juste après la section TREES mais avant la section Game. Dans cette section, définissez la fonction setupCollisions.

 / *********
 * DES ARBRES *
 ********* /

...

/ ***************
 * COLLISIONS *
 ************** /

const POSITION_Z_OUT_OF_SIGHT = 1;
const POSITION_Z_LINE_START = 0.6;
const POSITION_Z_LINE_END = 0,7;

fonction setupCollision () {
}

/ ********
 * JEU *
 ******** /

Comme précédemment, nous allons enregistrer un composant AFRAME et utiliser l'écouteur d'événement tick pour exécuter le code à chaque fois. Dans ce cas, nous allons enregistrer un composant avec le joueur et exécuter des vérifications sur tous les arbres de cet écouteur:

 function setupCollisions () {
  AFRAME.registerComponent ('player', {
    tick: fonction () {
      document.querySelectorAll ('. tree'). forEach (function (tree) {
      }
    }
  }
}

Dans la boucle pour commencez par obtenir les informations pertinentes de l’arbre:

 document.querySelectorAll ('. Tree'). ForEach (function (tree) {
  position = tree.getAttribute ('position');
  tree_position_index = tree.getAttribute ('data-tree-position-index');
  tree_id = tree.getAttribute ('id');
}

Ensuite, toujours dans la boucle pour supprimez l'arbre s'il est invisible, juste après l'extraction des propriétés de l'arbre:

 document.querySelectorAll ('.arbor'). ForEach (fonction (arbre) {
  ...
  if (position.z> POSITION_Z_OUT_OF_SIGHT) {
    removeTree (arbre);
  }
}

Ensuite, s'il n'y a pas de partie en cours, ne vérifiez pas s'il y a collision.

 document.querySelectorAll ('. tree'). forEach (function (tree) {
  si (! isGameRunning) retourne;
}

Enfin (toujours dans la boucle pour ), vérifiez si l'arbre partage la même position en même temps que le joueur. Si tel est le cas, appelez une fonction gameOver à définir:

 document.querySelectorAll ('. Tree'). ForEach (function (tree) {
  ...
  if (POSITION_Z_LINE_START 

Vérifiez que votre fonction setupCollisions correspond à ce qui suit:

 function setupCollisions () {
  AFRAME.registerComponent ('player', {
    tick: fonction () {
      document.querySelectorAll ('. tree'). forEach (function (tree) {
        position = tree.getAttribute ('position');
        tree_position_index = tree.getAttribute ('data-tree-position-index');
        tree_id = tree.getAttribute ('id');

        if (position.z> POSITION_Z_OUT_OF_SIGHT) {
          removeTree (arbre);
        }

        si (! isGameRunning) retourne;

        if (POSITION_Z_LINE_START 

Ceci termine la configuration de la collision. Nous allons maintenant ajouter quelques subtilités pour extraire les séquences startGame et gameOver . Accédez au GAME . ] Mettez à jour le bloc de window.onload pour le remplacer par le suivant, en remplaçant la fonction addTreesRandomlyLoop par une fenêtre startGame . .onload = function () {
  setupTrees ();
  démarrer jeu();
}

Sous les invocations de la fonction de configuration, créez une nouvelle fonction startGame . Cette fonction initialisera la variable isGameRunning en conséquence et empêchera les appels redondants.

 window.onload = function () {
    ...
}

fonction startGame () {
  if (isGameRunning) retourne;
  isGameRunning = true;

  addTreesRandomlyLoop ();
}

Enfin, définissez gameOver qui alertera pour l'instant un message «Game Over!».

 function startGame () {
    ...
}

fonction gameOver () {
  isGameRunning = false;

  alerte ('Game Over!');
  arrachement des arbres ();
}

Ceci termine la section de test de collision du jeu sans fin de coureurs.

À cette étape, nous avons de nouveau utilisé des composants A-Frame et un certain nombre d'autres utilitaires que nous avions ajoutés précédemment. Nous avons en outre réorganisé et correctement résumé les fonctions du jeu; nous augmenterons ensuite ces fonctions de jeu pour obtenir une expérience de jeu plus complète.

Conclusion

Dans 1ère partie nous avons ajouté des commandes adaptées aux casques d'écoute virtuelle: regardez à gauche pour aller à gauche et à droite. déplacer vers la droite. Dans cette deuxième partie de la série, je vous ai montré combien il est facile de créer un jeu de réalité virtuelle de base fonctionnel. Nous avons ajouté une logique de jeu afin que le coureur sans fin corresponde à vos attentes: courez pour toujours et laissez une série infinie d'obstacles dangereux voler au joueur. Jusqu'à présent, vous avez créé un jeu fonctionnel avec une prise en charge sans clavier pour les casques de réalité virtuelle.

Voici d'autres ressources pour différents contrôles et casques VR:

Dans la partie suivante, nous allons ajouter quelques touches de finition et synchroniser. Les états de jeu qui nous rapprochent des jeux multijoueurs.

Restez à l'écoute pour la partie 3!

 Smashing Editorial (rb, ra, yk, il)




Source link