À propos de l'auteur
Doctorat en intelligence artificielle chez UC Berkeley, axé sur les petits réseaux de neurones en perception, pour véhicules autonomes. Grand amateur de cheesecake, corgis, et…
Pour en savoir plus sur Alvin …
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.
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:
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»:
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 :
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:
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.
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.
Source link