Fermer

mars 20, 2019

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


Dans Partie 1 Alvin a expliqué les bases de la conception d'un modèle de réalité virtuelle. Dans Part 2 il a montré comment implémenter la logique centrale du jeu. Dans cette dernière partie de son didacticiel, les touches finales seront ajoutées, telles que les menus «Démarrer» et «Jeu ​​terminé», ainsi qu'une synchronisation des états de jeu entre clients mobiles et ordinateurs de bureau. Cela ouvre la voie aux concepts de construction de jeux multijoueurs.

Et ainsi continue notre chemin. Dans la dernière partie de ma série sur la création d’un jeu de course à la roulette sans fin, je vais vous montrer comment synchroniser l’état du jeu entre deux appareils, ce qui vous rapproche encore davantage de la création d’un jeu multijoueur. Je vais vous présenter en particulier MirrorVR, responsable de la gestion du serveur de médiation dans la communication client à client.

Note : Ce jeu peut être joué avec ou sans casque VR. Vous pouvez visionner une démonstration du produit final à l'adresse suivante: ergo-3.glitch.me .

Pour commencer, vous devez disposer des éléments suivants:

  • Un accès Internet (plus précisément à la position ). );
  • Un projet Glitch achevé à partir de partie 2 de ce tutoriel. Vous pouvez commencer à partir de la partie 2 du produit fini en allant sur https://glitch.com/edit/#!/ergo-2 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: Afficher le score

Le jeu tel quel est très réduit au minimum. Le joueur doit relever un défi: éviter les obstacles. Cependant, en dehors des collisions d’objets, le jeu ne donne aucune information au joueur sur les progrès accomplis. Pour remédier à cela, vous implémenterez l'affichage du score à cette étape. La partition sera un objet texte volumineux placé dans notre monde de réalité virtuelle, par opposition à une interface collée au champ de vision de l'utilisateur.

Dans la réalité virtuelle, l'interface utilisateur est mieux intégrée dans le monde que collée à l'utilisateur. head.

 Affichage du score
Affichage du score ( Grand aperçu )

Commencez par ajouter l'objet à index.html . Ajoutez un mixin text qui sera réutilisé pour d'autres éléments textuels:


   ...
  
   ...

Ajoutez ensuite un élément text à la plate-forme, juste avant le joueur:





 ...

Ceci ajoute une entité de texte à la scène de réalité virtuelle. Le texte n'est pas visible actuellement, car sa valeur est définie sur vide. Cependant, vous allez maintenant renseigner l'entité de texte de manière dynamique, à l'aide de JavaScript. Accédez à assets / ergo.js . Après la section sur les collisions ajoutez une section et définissez un certain nombre de variables globales:

  • score : le score actuel du jeu.
  • countedTrees : ID de tous les arbres inclus dans le score. (En effet, les tests de collision peuvent déclencher plusieurs fois le même arbre.)
  • scoreDisplay : référence à l'objet DOM, correspondant à un objet texte dans le monde de la réalité virtuelle.
 / ** *******
 * BUT *
 ********* /

score var;
var countedTrees;
var scoreDisplay;

Ensuite, définissez une fonction de configuration pour initialiser nos variables globales. Dans le même esprit, définissez une fonction de démontage .

 ...
var scoreDisplay;

fonction setupScore () {
  score = 0;
  countedTrees = new Set ();
  scoreDisplay = document.getElementById ('score');
}

fonction teardownScore () {
  scoreDisplay.setAttribute ('valeur', '');
}

Dans la section Game mettez à jour gameOver startGame et window.onload pour inclure la configuration et le démontage des partitions. ] / ********
 * JEU *
 ******** /

fonction gameOver () {
    …
    teardownScore ();
}

fonction startGame () {
    …
    setupScore ();
    addTreesRandomlyLoop ();
}

window.onload = function () {
    setupScore ();
    …
}

Définissez une fonction qui incrémente le score d'un arbre particulier. Cette fonction vérifie par rapport à countedTrees pour s'assurer que l'arbre n'est pas compté en double.

 function addScoreForTree (tree_id) {
  if (countedTrees.has (tree_id)) return;
  score + = 1;
  countedTrees.add (tree_id);
}

Ajoutez également un utilitaire pour mettre à jour l'affichage du score à l'aide de la variable globale.

 function updateScoreDisplay () {
  scoreDisplay.setAttribute ('valeur', score);
}

Mettez à jour le test de collision en conséquence afin d'invoquer cette fonction d'incrémentation du score lorsqu'un obstacle est passé devant le joueur. Toujours dans assets / ergo.js accédez à la section Collisions . Ajoutez la vérification et la mise à jour suivantes.

 AFRAME.registerComponent ('player', {
  tick: fonction () {
    document.querySelectorAll ('. tree'). forEach (function (tree) {
      ...
      if (position.z> POSITION_Z_LINE_END) {
        addScoreForTree (tree_id);
        updateScoreDisplay ();
      }
    })
  }
})

Enfin, mettez à jour l'affichage de la partition dès que le jeu commence. Accédez à la section Jeu et ajoutez updateScoreDisplay (); à startGame :

 function startGame () {
  ...
  setupScore ();
  updateScoreDisplay ();
  ...
}

Assurez-vous que assets / ergo.js et index.html correspondent aux fichiers de code source correspondants . Ensuite, accédez à votre aperçu. Vous devriez voir ce qui suit:

 Affichage de la partition
Affichage de la partition ( Grand aperçu )

Ceci termine l’affichage de la partition. Ensuite, nous ajouterons le bon départ et les menus Game Over afin que le joueur puisse rejouer le jeu à sa guise.

Maintenant que l'utilisateur peut suivre l'évolution, vous ajouterez la touche finale pour terminer. l'expérience de jeu. Dans cette étape, vous allez ajouter un menu Start et un menu Game Over permettant à l'utilisateur de démarrer et de redémarrer les jeux.

Commençons par le Start . menu où le joueur clique sur un bouton "Démarrer" pour commencer le jeu. Pour la seconde moitié de cette étape, vous allez ajouter un menu Game Over avec un bouton «Redémarrer»:

 Démarrer les menus et la partie terminée
les menus Démarrer et la partie terminée ( Large aperçu )

Accédez au index.html dans votre éditeur. Ensuite, trouvez la section Mixins . Ici, annexez le titre mixin, qui définit les styles pour un texte particulièrement volumineux. Nous utilisons la même police qu'auparavant, alignons le texte au centre et définissons une taille adaptée au type de texte. (Notez ci-dessous que ancre est l'endroit où un objet de texte est ancré à sa position.)


   ...
    

Ensuite, ajoutez un deuxième mixin pour les vedettes secondaires. Ce texte est légèrement plus petit mais est par ailleurs identique au titre.


   ...
    

Pour le troisième et dernier mixin, définissez les propriétés du texte descriptif – encore plus petites que les titres secondaires.


   ...
    

Une fois tous les styles de texte définis, vous allez maintenant définir les objets de texte présents dans le monde. Ajouter une nouvelle section Menus sous la section Score avec un conteneur vide pour le menu Start :


 ...



  
  

Dans le conteneur du menu Démarrer, définissez le titre et un conteneur pour tout texte autre que le titre:

 ...
  
    
    
     
  

Dans le conteneur pour le texte autre que le titre, ajoutez des instructions pour jouer au jeu:


    

Pour compléter le menu Démarrer ajoutez un bouton indiquant "Démarrer":


   ...
   
   

Vérifiez à nouveau que votre code HTML de menu Start correspond aux éléments suivants:



   
    
       
       
       
    
     
  

Accédez à votre aperçu et vous verrez le menu Démarrer :

 Image du menu Démarrer
Menu Démarrer ( Grand aperçu )

Toujours en Dans la section Menus (directement sous le menu Début ), ajoutez le menu Game-over en utilisant les mêmes mixins:



   ...
  
     
     
    
       
       
    
     
  

Accédez à votre fichier JavaScript, assets / ergo.js . Créez une nouvelle section Menus avant la section Game . De plus, définissez trois fonctions vides: setupAllMenus hideAllMenus et showGameOverMenu .

 / *********.
 * MENU *
 ******** /

fonction setupAllMenus () {
}

function hideAllMenus () {
}

fonction showGameOverMenu () {
}

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

Ensuite, mettez à jour la section Game à trois endroits. Dans gameOver affichez le menu Game Over :

 function gameOver () {
    ...
    showGameOverMenu ();
}
`` `

Dans `startGame`, masquez tous les menus:

`` `
fonction startGame () {
    ...
    hideAllMenus ();
}

Ensuite, dans window.onload supprimez l'invocation directe de startGame et appelez setupAllMenus . Mettez à jour votre écouteur afin qu'il corresponde à ce qui suit:

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

Revenez à la section Menu . Enregistrez les références à divers objets DOM:

 / ********
 * MENU *
 ******** /

var menuStart;
var menuGameOver;
var menuContainer;
var isGameRunning = false;
var startButton;
var restartButton;

fonction setupAllMenus () {
  menuStart = document.getElementById ('start-menu');
  menuGameOver = document.getElementById ("jeu terminé");
  menuContainer = document.getElementById ('menu-container');
  startButton = document.getElementById ('bouton de démarrage');
  restartButton = document.getElementById ('restart-button');
}

Liez ensuite les boutons “Démarrer” et “Redémarrer” à startGame :

 de la fonction setupAllMenus () {
  ...
  startButton.addEventListener ('click', startGame);
  restartButton.addEventListener ('click', startGame);
}

Définissez showStartMenu et appelez-le à partir de setupAllMenus :

 function setupAllMenus () {
  ...
  showStartMenu ();
}

function hideAllMenus () {
}

fonction showGameOverMenu () {
}

fonction showStartMenu () {
}

Pour remplir les trois fonctions vides, vous aurez besoin de quelques fonctions d'assistance. Définissez les deux fonctions suivantes, qui acceptent un élément DOM représentant une entité VR A-Frame et la montrent ou la masquent. Définissez les deux fonctions ci-dessus . Affichez tous les menus :

 ...
var restartButton;

fonction hideEntity (el) {
  el.setAttribute ('visible', false);
}

fonction showEntity (el) {
  el.setAttribute ('visible', true);
}

fonction showAllMenus () {
...

Première population hideAllMenus . Vous retirerez les objets de la vue, puis les écouteurs de clic des deux menus:

 function hideAllMenus () {
  hideEntity (menuContainer);
  startButton.classList.remove ('cliquable');
  restartButton.classList.remove ('cliquable');
}

Deuxièmement, peupler showGameOverMenu . Restaurez ici le conteneur des deux menus, ainsi que le menu Game Over et l’auditeur de clic du bouton "Redémarrer". Toutefois, supprimez l’auditeur de clic du bouton "Démarrer" et masquez le menu "Démarrer".

 function showGameOverMenu () {
  showEntity (menuContainer);
  hideEntity (menuStart);
  showEntity (menuGameOver);
  startButton.classList.remove ('cliquable');
  restartButton.classList.add ('cliquable');
}

Troisièmement, peupler showStartMenu . Ici, annulez toutes les modifications effectuées par showGameOverMenu .

 function showStartMenu () {
  showEntity (menuContainer);
  hideEntity (menuGameOver);
  showEntity (menuStart);
  startButton.classList.add ('cliquable');
  restartButton.classList.remove ('cliquable');
}

Vérifiez à nouveau que votre code correspond aux fichiers source correspondants . Ensuite, accédez à votre aperçu et vous observerez le comportement suivant:

 Menus Démarrer et jeu supérieur
Menus Début et Jeu fermé ( Grand aperçu )

Ceci conclut le Démarrer les menus et Game Over .

Félicitations! Vous avez maintenant un jeu entièrement fonctionnel avec un bon début et une bonne fin. Cependant, il reste encore une étape dans ce didacticiel: nous devons synchroniser l’état du jeu entre différents appareils. Cela nous rapprochera encore plus des jeux multijoueurs.

Étape 3: Synchronisation de l’état du jeu avec MirrorVR

Dans un tutoriel précédent vous avez appris à envoyer des informations en temps réel sur des sockets communication à sens unique entre un serveur et un client. Dans cette étape, vous allez créer un produit à part entière de ce didacticiel, MirrorVR, qui gère le serveur de médiation dans une communication client à client.

Note : Pour en savoir plus sur MirrorVR ici .

Naviguez vers index.html . Ici, nous allons charger MirrorVR et ajouter un composant à la caméra, en indiquant qu’il doit refléter la vue du périphérique mobile, le cas échéant. Importez la dépendance socket.io et MirrorVR 0.2.3.

    

Ajoutez ensuite un composant, auditeur de caméra à la caméra:

  

Accédez à assets / ergo.js . Dans cette étape, le périphérique mobile envoie des commandes et le périphérique de bureau ne fait que refléter le périphérique mobile.

Pour faciliter cela, vous avez besoin d'un utilitaire permettant de faire la distinction entre les périphériques de bureau et les périphériques mobiles. À la fin de votre fichier, ajoutez une fonction mobileCheck après de lecture aléatoire :

 / **
 * Vérifie les plates-formes mobiles et tablettes.
 * /
fonction mobileCheck () {
  var check = false;
  (fonction (a) {if (/ (android | bb  d + | meego). + mobile | avantgo | bada  / | blackberry | blazer | compal | elaine | fennec | hiptop | iemobile | ip (hone | od) | iris | kindle | lge | maemo | midp | mmp | mobile. + firefox | netfront | opera m (ob | in) i | palm (os)? | téléphone | p (ixi | re)  / | plucker | pocket | psp | série (4 | 6) 0 | symbian | treo | up . (navigateur | lien) | vodafone | wap | windows ce | xda | xiino | android | ipad | playbook | silk / i.test (a) || / 1207 | 6310 | 6590 | 3gso | 4thp | 50 [1-6] i | 770s | 802s | a wa | abac | ac (er | oo | s  -) ​​| ai (ko | rn) | al (av | ca | co) | amoi | an (ex | ny | yw) | aptu | ar (ch | go) | as (te | us) | attw | au (di |  -m | r | s) | avan | be (ck | ll | nq) | bi (lb | rd) | bl (ac | az) | br (e | v) w | bumb | bw  - (n | u) | c55  / | capi | ccwa | cdm  - | cell | chtm | cldc | cmd  - | co (mp | nd) | craw | da (it | ll | ng) | dbte | dc  -s | devi | dica | dmob | do (c | p) o | ds ( 12 |  -d) | el (49 | ai) | em (l2 | ul) | er (ic | k0) | esl8 | ez ([4-7] 0 | os | wa | ze) | fetc | fly ( - | _) | g1 u | g560 | gène | gf  -5 | g  -mo | go (. w | od) | gr (ad | un) | haie | hcit | hd  - (m | p | t ) | hei  - | hi (pt | ta) | hp (i | ip) | hs  -c | ht (c ( - | | _ | a | g | p | t | t) | tp) | hu (aw | tc) | i  - (20 | go | ma) | i230 | iac (|  - |  /) | ibro | idea | ig01 | ikom | im1k | inno | ipaq | iris | ja (t | v ) a | jbro | jemu | jigs | kddi | keji | kgt (|  /) | klon | kpt | kwc  - | kyo (c | k) | le (no | xi) | lg (g |  / (k | l | u) | 50 | 54 |  - [a-w]) | libw | lynx | m1  -w | m3ga | m50  / | ma (te | ui | xo) | mc (01 | 21 | ca) | m  -cr | me (rc | ri) | mi (o8 | oa | ts) | mmef | mo (01 | 02 | bi | de | do | t ( - | | o | v) | zz) | mt (50 | p1 | v) | mwbp | mywa | n10 [0-2] | n20 [2-3] | n30 (0 | 2) | n50 (0 | 2 | 5) | n7 ( 0 (0 | 1) | 10) | ne ((c | m)  - | sur | tf | wf | wg | wt) | nok (6 | i) | nzph | o2im | op (ti | wv) | oran | owg1 | p800 | pan (a | d | t) | pdxg | pg (13 |  - ([1-8] | c)) | phil | pire | pl (ay | uc) | pn  -2 | po (ck | rt | se) | prox | psio | pt  -g | qa  -a | qc (07 | 12 | 21 | 32 | 60 |  - [2-7] | i  -) ​​| qtek | r380 | r600 | raks | rim9 | ro (ve | zo) | s55  / | sa (ge | ma | mm | ms | ny | va) | sc (01 | h  - | oo | p  -) ​​| sdk  / | se ( c ( - | 0 | 1) | 47 | mc | nd | ri) | sgh  - | shar | sie ( - | m) | sk  -0 | sl (45 | id) | sm (al | ar | b3 | it | t5) | so (ft | ny) | sp (01 | h  - | v  - | v) | sy (01 | mb) | t2 (18 | 50) | t6 (00 | 10 | 10 | 10 | 18) | ta (gt | lk) | tcl  - | tdg  - | tel (i | m) | tim  - | t  -mo | à (pl | sh) | ts (70 | m  - | m3 | m5) | tx  -9 | up (. b | g1 | si) | utst | v400 | v750 | veri | vi (rg | te) | vk (40 | 5 [0-3] |  -v) | vm40 | voda | vulc | vx (52 | 53 | 60 | 61 | 70 | 80 | 81 | 83 | 85 | 98) | w3c ( - |) | webc | whit | wi (g | nc | nw) | wmlb | wonu | x700 | yas  - | votre | zeto | zte  - / i.test (a.substr (0,4))) check = true;}) (navigator.userAgent || navigator.vendor || window.opera )
  chèque de retour;
};

Nous allons d'abord synchroniser le début de la partie. Dans startGame de la section Game ajoutez une notification mirrorVR à la fin.

 function startGame () {
  ...
  si (mobileCheck ()) {
    mirrorVR.notify ('startGame', {})
  }
}

Le client mobile envoie maintenant des notifications sur le démarrage d'un jeu. Vous allez maintenant implémenter la réponse du bureau.

Dans l’écouteur de chargement de fenêtre, appelez une fonction setupMirrorVR :

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

Définissez une nouvelle section au-dessus de la section Game pour l'installation de MirrorVR:

 / *************
 * MirrorVR *
 ************ /

fonction setupMirrorVR () {
  mirrorVR.init ();
}

Ensuite, ajoutez des arguments de mots clés à la fonction d'initialisation pour mirrorVR. Plus précisément, nous allons définir le gestionnaire pour les notifications de démarrage du jeu. Nous spécifierons en outre un identifiant de salle; Cela garantit que toute personne chargeant votre application est immédiatement synchronisée.

 function setupMirrorVR () {
  mirrorVR.init ({
    roomId: 'ergo',
    Etat: {
      démarrer jeu: {
        onNotify: fonction (données) {
          hideAllMenus ();
          setupScore ();
          updateScoreDisplay ();
        }
      },
    }
  });
}

Répétez le même processus de synchronisation pour Game Over . Dans gameOver dans la section Game ajoutez une coche pour les appareils mobiles et envoyez une notification en conséquence:

 function gameOver () {
  ...
  si (mobileCheck ()) {
    mirrorVR.notify ('gameOver', {});
  }
}

Accédez à la section MirrorVR et mettez à jour les arguments de mots clés avec un écouteur gameOver :

 setupMirrorVR () {
  mirrorVR.init ({
    Etat: {
      démarrer jeu: {...
      },
      jeu terminé: {
        onNotify: fonction (données) {
          jeu terminé();
        }
      },
    }
  })
}

Répétez ensuite le même processus de synchronisation pour l'ajout d'arbres. Accédez à addTreesRandomly dans la section Arbres . Gardez une trace des voies qui reçoivent de nouveaux arbres. Puis, directement avant la directive retournez et envoyez une notification en conséquence:

 function addTreesRandomly (...) {
  ...
  var numberOfTreesAdded ...
  var position_indices = [];
  trees.forEach (fonction (arbre) {
    si (...) {
      ...
      position_indices.push (tree.position_index);
    }
  });

  si (mobileCheck ()) {
    mirrorVR.notify ('addTrees', position_indices);
  }
  revenir ...
}

Accédez à la section MirrorVR et mettez à jour les arguments de mots clés avec mirrorVR.init avec un nouvel écouteur pour les arbres:

 function setupMirrorVR () {
  mirrorVR.init ({
    Etat: {
      ...
      jeu terminé: {...
      },
      addTrees: {
        onNotify: function (position_indices) {
          position_indices.forEach (addTreeTo)
        }
      },
    }
  })
}

Enfin, nous synchronisons le score du jeu. Dans updateScoreDisplay de la section Score envoyez une notification, le cas échéant:

 function updateScoreDisplay () {
  ...
  si (mobileCheck ()) {
    mirrorVR.notify ('score', score);
  }
}

Mettez à jour l'initialisation de miroirVR pour la dernière fois, avec un écouteur pour les modifications de partition:

 function setupMirrorVR () {
  mirrorVR.init ({
    Etat: {
      addTrees: {
      },
      But: {
        onNotify: fonction (données) {
          score = données;
          updateScoreDisplay ();
        }
      }
    }
  });
}

Vérifiez à nouveau que votre code correspond aux fichiers de code source appropriés pour cette étape . Ensuite, accédez à l'aperçu de votre bureau. De plus, ouvrez la même URL sur votre appareil mobile. Dès que votre appareil mobile charge la page Web, votre ordinateur doit immédiatement commencer à refléter le jeu de celui-ci.

Voici une démonstration. Notez que le curseur du bureau ne bouge pas, indiquant que le périphérique mobile contrôle l'aperçu du bureau.

 Final Endless Runner Game avec synchronisation d'état du jeu MirrorVR
Résultat final du jeu Endless Runner avec synchronisation de l'état du jeu MirrorVR ( Grand aperçu )

Ceci conclut votre projet augmenté avec mirrorVR.

Cette troisième étape introduit quelques étapes de base pour la synchronisation des états de jeu; Pour rendre cela plus robuste, vous pouvez ajouter plus de contrôles de cohérence et plus de points de synchronisation.

Conclusion

Dans ce tutoriel, vous avez apporté la touche finale à votre jeu de coureurs sans fin et mis en œuvre la synchronisation en temps réel d'un client de bureau avec client mobile, reflétant efficacement l'écran de l'appareil mobile sur votre bureau. Ceci conclut la série sur la construction d’un jeu de coureur sans fin en réalité virtuelle. Outre les techniques de VR A-Frame, vous avez opté pour la modélisation 3D, la communication client à client et d'autres concepts largement applicables.

Les étapes suivantes peuvent inclure:

  • Modélisation plus avancée
    signifie des modèles 3D plus réalistes, potentiellement créés dans un logiciel tiers et importés. Par exemple, ( MagicaVoxel ) simplifie la création d'art voxel et ( Blender ) est une solution complète de modélisation 3D.
  • Plus complexe
    Jeux plus complexes, comme un jeu de stratégie en temps réel, pourrait s’appuyer sur un moteur tiers pour une efficacité accrue. Cela peut signifier éviter complètement A-Frame et webVR, en publiant plutôt un jeu compilé ( Unity3d ).

Le support multijoueur et des graphiques plus riches sont également proposés. Avec la conclusion de cette série de didacticiels, vous avez maintenant un cadre à explorer davantage.

 Smashing Editorial (rb, ra, il)




Source link