Ceci est la deuxième partie de notre quête pour créer un jeu de tir spatial 3D. Dans la première partie, nous avons examiné la configuration d’un jeu PlayCanvas de base, avec physique et collision, nos propres modèles et une caméra..
Pour référence, voici à nouveau une démonstration en direct de notre résultat final.
Dans cette partie, nous allons nous concentrer sur la création dynamique d’entités avec des scripts (pour générer des puces et des astéroïdes), ainsi que sur la façon d’ajouter des éléments comme un compteur FPS et du texte dans le jeu. Si vous avez déjà suivi la partie précédente et êtes satisfait de ce que vous avez, vous pouvez commencer à construire à partir de là et ignorer la section de configuration minimale suivante. Sinon, si vous devez redémarrer à partir de zéro:
var Fly = pc.createScript ('fly'); Fly.attributes.add ('speed', type: 'nombre', valeur par défaut: 50); // initialise le code appelé une fois par entité Fly.prototype.initialize = function () ; // code de mise à jour appelé chaque image Fly.prototype.update = fonction (dt) // Appuyez sur Z pour pousser si (this.app.keyboard.isPressed (pc.KEY_Z)) // déplacez-vous dans le sens opposé à sa force var = this.entity.forward.clone (). scale (this.speed); this.entity.rigidbody.applyForce (force); // Rotation haut / bas / gauche / droite if (this.app.keyboard.isPressed (pc.KEY_UP)) var force_up = this.entity.right.clone (). Scale (1); this.entity.rigidbody.applyTorque (force_up); if (this.app.keyboard.isPressed (pc.KEY_DOWN)) var force_down = this.entity.right.clone (). scale (-1); this.entity.rigidbody.applyTorque (force_down); if (this.app.keyboard.isPressed (pc.KEY_RIGHT)) // Tourner à droite var force_right = this.entity.up.clone (). scale (-1); this.entity.rigidbody.applyTorque (force_right); if (this.app.keyboard.isPressed (pc.KEY_LEFT)) var force_left = this.entity.up.clone (). scale (1); this.entity.rigidbody.applyTorque (force_left); ; // méthode d'échange appelée pour le rechargement à chaud du script // hériter de votre état de script ici Fly.prototype.swap = function (old) ; // pour en savoir plus sur l'anatomie du script, veuillez lire: // http://developer.playcanvas.com/fr/user-manual/scripting/
Testez que tout a fonctionné. Vous devriez pouvoir voler avec Z pour pousser et les touches fléchées pour faire pivoter!
La création dynamique d’objets est cruciale pour presque tous les types de jeux. Dans la démo que j'ai créée, je crée deux types d'astéroïdes. Le premier type flotte et agit comme un obstacle passif. Ils réapparaissent quand ils s’éloignent trop pour créer un champ d’astéroïdes toujours dense autour du joueur. Le second type apparaît de plus loin et va vers le joueur (pour créer un sentiment de danger même si le joueur ne bouge pas).
Nous avons besoin de trois choses pour engendrer nos astéroïdes:
Créez une nouvelle entité à partir d'un modèle de votre choix. Cela pourrait être quelque chose hors du magasin PlayCanvas, ou quelque chose de BlendSwap, ou juste une forme de base. (Si vous utilisez vos propres modèles, il est conseillé de commencer par ouvrir Blender pour vérifier le nombre de faces utilisées et l'optimiser si nécessaire.)
Donnez-lui une forme de collision appropriée et un composant de corps rigide (assurez-vous que c'est dynamique). Une fois que vous êtes satisfait, décochez la case Activée boîte:
Lorsque vous désactivez un objet comme celui-ci, cela revient à le supprimer du monde en ce qui concerne le lecteur. Ceci est utile pour supprimer temporairement des objets, ou dans notre cas, pour conserver un objet avec toutes ses propriétés mais ne pas le voir apparaître dans le jeu..
Créez un nouveau script appelé AsteroidSpawner.js et attachez-le au Racine objet dans la hiérarchie. (Notez que la racine est simplement un objet normal auquel peuvent être attachés des composants, tout comme la caméra.)
Maintenant, ouvrez le script que vous venez de créer.
La manière générale de cloner une entité et de l'ajouter au monde via un script ressemble à ceci:
// Crée le clone var newEntity = oldEntity.clone (); // Ajoutez-le à l'objet racine this.app.root.addChild (newEntity); // Donnez-lui un nouveau nom, sinon le nom de oldEntity sera aussi newEntity.name = "ClonedEntity"; // Activez-le, en supposant que oldEntity soit défini sur désactivé newEntity.enabled = true;
Voici comment cloner un objet si vous aviez déjà un objet "oldEntity". Cela laisse une question sans réponse: Comment accédons-nous au modèle Asteroid que nous avons créé?
Il y a deux façons de faire ça. La méthode la plus flexible consiste à créer un attribut de script contenant l’entité à cloner. Vous pourrez ainsi échanger facilement des modèles sans toucher au script. (C’est exactement ce que nous avons fait avec le script lookAt de la caméra à l’étape 7).
L'autre façon consiste à utiliser la fonction findByName. Vous pouvez appeler cette méthode sur n’importe quelle entité pour rechercher l’un de ses enfants. Nous pouvons donc l'appeler sur l'objet racine:
var oldEntity = this.app.root.findByName ("AsteroidModel");
Et ceci complétera notre code par le haut. Le script complet AsteroidSpawner ressemble maintenant à ceci:
var AsteroidSpawner = pc.createScript ('asteroidSpawner'); // initialise le code appelé une fois par entité AsteroidSpawner.prototype.initialize = function () var oldEntity = this.app.root.findByName ("AsteroidModel"); // Crée le clone var newEntity = oldEntity.clone (); // Ajoutez-le à l'objet racine this.app.root.addChild (newEntity); // Donnez-lui un nouveau nom, sinon le nom de oldEntity sera aussi newEntity.name = "ClonedEntity"; // Activez-le, en supposant que oldEntity soit défini sur désactivé newEntity.enabled = true; // Définit sa position newEntity.rigidbody.teleport (new pc.Vec3 (0,0,1)); ; // code de mise à jour appelé chaque image AsteroidSpawner.prototype.update = function (dt) ;
Testez cela en lançant et en regardant si votre modèle d'astéroïde existe.
Note: j'ai utilisé newEntity.rigidbody.teleport
au lieu de newEntity.setPosition
. Si une entité a un corps rigide, celui-ci aura priorité sur la position et la rotation de l'entité. N'oubliez donc pas de définir ces propriétés sur le corps rigide et non sur l'entité elle-même..
Avant de continuer, essayez de faire apparaître au moins dix astéroïdes autour du lecteur, de manière aléatoire ou de manière systématique (peut-être même en cercle?). Il serait utile de mettre tout votre code en cours de génération dans une fonction afin qu'elle ressemble à ceci:
AsteroidSpawner.prototype.initialize = function () this.spawn (0,0,0); cet.spawn (1,0,0); cet.spawn (1,1,0); // etc… ; AsteroidSpawner.prototype.spawn = fonction (x, y, z) // Code de reproduction ici…
Vous devriez être à l'aise d'ajouter de nouveaux scripts maintenant. Créez un nouveau script (appelé Asteroid.js) et attachez-le à AsteroidModel. Puisque tous nos astéroïdes engendrés sont des clones, ils auront tous le même script qui leur est associé..
Si nous créons beaucoup d’astéroïdes, il serait judicieux de s’assurer qu’ils sont détruits quand nous n’en avons plus besoin ou quand ils sont suffisamment éloignés. Voici une façon de procéder:
Asteroid.prototype.update = function (dt) // Récupère le joueur var player = this.app.root.findByName ("Ship"); // Remplace "Expédier" par le nom de votre joueur // Clonez la position de l'astéroïde var distance = this.entity.getPosition (). Clone (); // Soustrayez la position du joueur de la position de cet astéroïde distance.sub (player.getPosition ()); // Récupère la longueur de ce vecteur if (distance.length ()> 10) // Certains seuils arbitraires this.entity.destroy (); ;
Conseil de débogage: Si vous souhaitez imprimer quoi que ce soit, vous pouvez toujours utiliser la console du navigateur comme s'il s'agissait d'une application JavaScript normale. Pour que vous puissiez faire quelque chose comme console.log (distance.toString ());
pour imprimer le vecteur de distance, et il apparaîtra dans la console.
Avant de continuer, vérifiez que l'astéroïde disparaît lorsque vous vous en éloignez.
Les balles de frai auront à peu près la même idée que les astéroïdes de frai, avec un nouveau concept: Nous voulons détecter quand la balle frappe quelque chose et l'enlever. Pour créer notre système de bullet, nous avons besoin de:
Vous pouvez utiliser n'importe quelle forme pour votre balle. J'ai utilisé une capsule juste pour avoir une idée de la direction à laquelle la balle faisait face. Comme avant, créez votre entité, réduisez-la et donnez-lui un corps rigide dynamique et une zone de collision appropriée. Donnez-lui le nom "Bullet" pour qu'il soit facile à trouver.
Une fois que vous avez terminé, assurez-vous de le désactiver (avec la case à cocher Activé).
Créez un nouveau script et attachez-le à votre vaisseau. Cette fois, nous allons utiliser un attribut pour obtenir une référence à notre entité bullet:
Shoot.attributes.add ('bullet', type: 'entity');
Retournez dans l'éditeur et cliquez sur "analyser" pour que le nouvel attribut apparaisse, puis sélectionnez l'entité balle que vous avez créée..
Maintenant, dans la fonction de mise à jour, nous voulons:
Vous avez déjà été initié à tous ces concepts. Vous avez vu comment cloner des astéroïdes, appliquer une force dans une direction pour faire bouger le navire et positionner des objets. Je laisserai la mise en œuvre de cette partie comme un défi. (Mais si vous êtes bloqué, vous pouvez toujours aller voir comment j'ai implémenté mon propre script Shoot.js dans mon projet).
Voici quelques astuces qui pourraient vous éviter des maux de tête:
keyboard.wasPressed
au lieu de keyboard.isPressed
. Lorsqu’on détecte que la touche X est enfoncée pour tirer un coup, la première solution est un moyen pratique de la déclencher uniquement lorsque vous appuyez sur, par opposition à un tir tant que le bouton est maintenu enfoncé..rotationLocal
au lieu de définir une rotation absolue. Pour que la balle apparaisse toujours parallèlement au navire, il était difficile de calculer correctement les angles. Une méthode beaucoup plus simple consiste simplement à définir la rotation de la balle sur la rotation du navire, puis à faire pivoter la balle dans son espace local de 90 degrés sur l'axe X.. À ce stade, vos balles devraient apparaître, frapper les astéroïdes et ricocher dans un espace vide. Le nombre de balles peut vite devenir écrasant, et savoir détecter une collision est utile pour toutes sortes de choses.. (Par exemple, vous avez peut-être remarqué que vous pouvez créer des objets comportant uniquement un composant de collision mais pas de corps rigide. Ceux-ci agiraient en tant que déclencheurs mais ne réagiraient pas physiquement.)
Créez un nouveau script appelé Bullet et attachez-le à votre modèle Bullet qui est cloné. PlayCanvas propose trois types d’événements de contact. Nous allons écouter collisionend, qui se déclenche lorsque les objets se séparent (sinon la balle serait détruite avant que l'astéroïde ne puisse réagir).
Pour écouter un événement de contact, tapez ceci dans votre fonction init:
this.entity.collision.on ('collisionend', this.onCollisionEnd, this);
Et créez ensuite l'auditeur lui-même:
Bullet.prototype.onCollisionEnd = function (result) // Détruit la puce si elle frappe un astéroïde if (result.name == "Asteroid") this.entity.destroy (); ;
C'est ici que le nom que vous avez donné à vos astéroïdes lorsque vous les avez engendrés devient pertinent. Nous voulons que la balle ne soit détruite que quand elle entre en collision avec un astéroïde. résultat
est l'entité avec laquelle il a fini d'entrer en collision.
Sinon, vous pouvez supprimer ce contrôle et le faire détruire en cas de collision avec quoi que ce soit..
Il est vrai qu’il n’ya pas d’autre objet au monde avec lequel entrer en collision, mais j’ai eu quelques problèmes avec le joueur qui a déclenché la collision de la balle pour une image et qui a disparu avant son lancement. Si vos besoins en matière de collision sont plus complexes, PlayCanvas prend en charge les groupes et les masques de collision, mais cela n’est pas très bien documenté au moment de la rédaction..
Nous en avons pratiquement terminé avec le jeu lui-même. Bien sûr, j'ai ajouté beaucoup de petites choses polonaises à la démo finale, mais vous ne pouvez rien y faire avec ce que vous avez appris jusqu'à présent..
Je voulais vous montrer comment créer un compteur FPS (même si PlayCanvas a déjà un profileur; vous pouvez survoler le bouton de lecture et cocher la case du profileur) car c'est un bon exemple d'ajout d'un élément DOM en dehors du moteur PlayCanvas..
Nous allons utiliser cette bibliothèque FPSMeter. La première chose à faire est d'aller sur le site de la bibliothèque et de télécharger la version de production abrégée.
Retournez dans votre éditeur PlayCanvas, créez un nouveau script et copiez le code fpsMeter.min.js. Attachez ce script à l'objet racine.
Maintenant que la bibliothèque a été chargée, créez un nouveau script qui initialisera et utilisera la bibliothèque. Appelez-le meter.js, et à partir de l'exemple d'utilisation sur le site Web de la bibliothèque, nous avons:
var Meter = pc.createScript ('meter'); Meter.prototype.initialize = function () this.meter = new FPSMeter (document.body, graph: 1, heat: 1); ; Meter.prototype.update = function (dt) this.meter.tick (); ;
Ajoutez également le script de compteur à l'objet racine et lancez-le. Vous devriez voir le compteur FPS dans le coin supérieur gauche de l'écran!
Enfin, ajoutons du texte dans notre monde. Celui-ci est un peu impliqué car il existe différentes façons de le faire. Si vous souhaitez simplement ajouter une interface utilisateur statique, vous pouvez travailler directement avec le DOM, en superposant vos éléments d'interface utilisateur au-dessus de l'élément de toile de PlayCanvas. Une autre méthode consiste à utiliser des SVG. Cet article décrit certaines de ces différentes manières.
Comme ce sont tous des moyens standard de gérer le texte sur le Web, j'ai plutôt choisi de regarder comment créer du texte existant dans l'espace du monde du jeu. Alors, réfléchissez-y comme un texte qui irait sur un signe dans l'environnement ou sur un objet dans le jeu..
Pour ce faire, nous créons un Matériel pour chaque morceau de texte que nous voulons rendre. Nous créons ensuite un toile invisible que nous rendons le texte en utilisant la méthode fillText de canvas habituelle. Enfin nous rendre la toile sur le matériau l'avoir dans le jeu.
Notez que cette méthode peut être utilisée pour plus que du texte. Vous pouvez dessiner dynamiquement des textures ou faire tout ce qu'une toile peut faire.
Créez un nouveau matériau et appelez-le quelque chose comme "TextMaterial". Définissez sa couleur diffuse sur le noir car notre texte sera blanc.
Créer une entité plane et y attacher ce matériau.
Vous pouvez trouver le script text.js complet dans cet élément essentiel:
https://gist.github.com/OmarShehata/e016dc219da36726e65cedb4ab9084bd
Vous pouvez voir comment la texture est configurée pour utiliser le canevas comme source, plus précisément à la ligne: this.texture.setSource (this.canvas);
Créez ce script et attachez-le à votre avion. Notez comment cela crée deux attributs: texte et taille de police. De cette façon, vous pouvez utiliser le même script pour n'importe quel objet texte de votre jeu..
Lancez la simulation et vous devriez voir le gros texte "Hello World" quelque part. Si vous ne le voyez pas, assurez-vous qu’il a une source de lumière à proximité et que b) vous regardez du bon côté. Le texte ne sera pas rendu si vous le regardez de derrière. (Il est également utile de placer un objet physique près de l'avion, juste pour le localiser au début.)
Une fois que vous avez assemblé votre superbe prototype, vous pouvez cliquer sur l’icône PlayCanvas dans le coin supérieur gauche de l’écran et sélectionner "Publication". C'est ici que vous pouvez publier de nouvelles versions hébergées sur PlayCanvas et les partager avec le monde entier.!
C'est tout pour cette démo. Il y a beaucoup plus à explorer dans PlayCanvas, mais j'espère que cet aperçu vous familiarisera suffisamment avec les bases pour commencer à créer vos propres jeux. C'est un très bon moteur que je pense que plus de gens devraient utiliser. Une grande partie de ce qui a été créé avec ce sont des démonstrations techniques plutôt que des jeux complets, mais rien ne nous empêche de construire et de publier quelque chose de génial avec cela.
Une fonctionnalité dont je n'ai pas vraiment parlé mais que j'aurais pu voir est que l'éditeur de PlayCanvas vous permet de mettre à jour votre jeu en temps réel. Ceci est vrai pour le design, dans la mesure où vous pouvez déplacer des éléments dans l'éditeur et les mettre à jour dans la fenêtre de lancement si vous l'avez ouvert, ainsi que pour le code, avec son rechargement à chaud..
Enfin, bien que l'éditeur soit vraiment pratique, tout ce que vous pouvez faire avec cela peut être fait avec du code pur. Donc, si vous devez utiliser PlayCanvas sur un budget, le dossier des exemples sur GitHub est une bonne référence. (Un bon point de départ serait cet exemple simple de cube en rotation.)
Si quelque chose est déroutant, faites-le-moi savoir dans les commentaires! Ou juste si vous avez construit quelque chose de cool et que vous voulez partager, ou avez trouvé un moyen plus facile de faire quelque chose, j'aimerais voir!