Passer au contenu principal




Cet article est la deuxième partie de mon tutoriel pour faire un petit jeu avec Javascript. Créer votre premier jeu en JavaScript - Première partie.

Dans cette deuxième partie, nous allons commencer le fonctionnement de notre jeu, en lui donnant plus d'animation, de polyvalence et de mouvement. Sans plus dire, commençons par cette deuxième partie!

Rattrapons

Comme vous le savez peut-être déjà, nous avons beaucoup d'avance sur ce tutoriel. Suivez le lien ci-dessous et voir le code source complet de ce tutoriel . Vous obtiendrez absolument tout en suivant cette URL. Alors n'attendez plus et continuons notre tutoriel.

Créer notre incroyable vaisseau spatial

Maintenant que le fond est terminé, nous pouvons enfin commencer à assembler notre vaisseau spatial!

Créons un fichier Player.js et ajoutez-le à index.html en tant que script (comme nous l'avons fait avec CloudManager).

Voici un (beau) vaisseau spatial conçu en quelques minutes, en plus vous pouvez l'ajouter à votre dossier des actifs et dans le fonction chargeur de main.js:

Fondamentalement, nous voulons que ce vaisseau apparaisse près du bord gauche de l'écran. Nous avons également besoin que son ancre (point de position de l'objet) soit au centre du sprite, et comme il est peut-être trop grand, nous pouvons le redimensionner comme je vous le montre ci-dessous:

class Player {constructeur () {this.sprite = new PIXI.Sprite (PIXI.loader.resources ["assets / spaceship.png"]. texture); this.sprite.interactive = true; this.sprite.anchor.set (0,5, 0,5); this.sprite.position.set (renderer.width * 0.2, renderer.height * 0.4); this.sprite.scale.set (0.4, 0.4); stage.addChild (this.sprite); }}

Bien sûr, nous devons déclarer cet objet comme un variable globale à partir du fichier main.js, afin qu'il puisse être instancié dans le jeu:

var stage = nouveau PIXI.Container (); var cloudManager; joueur var; ...

… Et dans le fonction init à partir du même fichier (juste après CloudManager):

joueur = nouveau joueur ();

Vous devriez maintenant voir le vaisseau spatial brillant naviguer.

Cependant, si vous attendez quelques secondes, vous devriez avoir quelques problèmes:

Le vaisseau spatial vole derrière les nuages!

Oui, car en fait le dernier objet créé se déplace derrière le précédent. Les nuages apparaissent après l'instanciation du vaisseau spatial, nous avons donc besoin du gestionnaire de nuages pour générer des nuages en bas de la liste d'objets. Pour ce faire, il suffit de mettre à jour le addChild de la classe CloudManager:

stage.addChildAt (this.cloud, 0);

addChildAt nous permet de passer un deuxième paramètre, qui est la position dans la liste des objets sur la scène. Plus vous êtes éloigné, plus le sprite sera éloigné à l'écran.

"0" signifie le premier index de la liste d'objets, donc chaque fois que nous créons un nouveau nuage, nous l'ajoutons en haut de la liste, en nous assurant qu'il apparaîtra derrière le vaisseau spatial.

Maintenant que c'est fait, nous pouvons démarrer les contrôles du vaisseau spatial.

Déplacez le vaisseau spatial

Revenons à la classe de Player.js et ajoutons quelques lignes dans le constructeur:

window.addEventListener ('keydown', this.onKeyDown.bind (this)); window.addEventListener ('keyup', this.onKeyUp.bind (this));

Cela permet au jeu de détecter les événements du clavier (lorsqu'une touche est enfoncée et relâchée). Lorsqu'un de ces événements se produit, la méthode est exécutée à la fin de chaque ligne.

Remarque: En ajoutant ".bind (this)" à la fin de la fonction, nous lui passons le contexte de l'objet, ce qui signifie que nous pouvons toujours accéder à ses propriétés en appelant "this". Si nous ne l'écrivons pas de cette façon, la fonction de rappel ne peut pas déterminer avec quel objet nous travaillons. N'oubliez pas que si l'une de vos fonctions de rappel reçoit une erreur "indéfini this", vous devrez peut-être lier votre objet de cette façon.

Nous allons définir 2 méthodes vides dans la même classe (nous les remplirons un peu):

onKeyDown (clé) = {}; onKeyUp (clé) = {};

Nous avons également besoin de 3 variables pour déplacer le vaisseau spatial: ses directions horizontale et verticale (X et Y) et la vitesse (toutes dans le constructeur):

this.directionX = 0; this.directionY = 0; this.speed = 8;
this.keyCodes = {37: -1, 38: -1, 39: 1, 40: 1};

Cet objet est essentiellement une association de codes clés et de valeurs d'adresse:

  • 37 est la touche fléchée gauche, pour déplacer notre vaisseau spatial vers la gauche, nous devons diminuer sa position en X.
  • 38 est la touche fléchée vers le haut, pour déplacer notre vaisseau spatial vers le haut, nous devons diminuer sa position en Y.
  • 39 est la touche fléchée droite, pour déplacer notre vaisseau spatial vers la droite, nous devons augmenter sa position dans X.
  • 40 est la touche fléchée vers le bas, pour déplacer notre vaisseau spatial à l'arrière-plan, nous devons augmenter sa position dans Y.

A chaque pression sur une touche, l'adresse correspondante est rappelée:

onKeyDown (key) {if (key.keyCode == 37 || key.keyCode == 39) this.directionX = this.keyCodes [key.keyCode]; else if (key.keyCode == 38 || key.keyCode == 40) this.directionY = this.keyCodes [key.keyCode]; }

Cela semble assez étrange, mais expliquons cela: si le code de la clé est 37 ou 39 (ce qui signifie la touche fléchée gauche ou droite), alors nous définissons simplement la direction du X.Si c'est 38 ou 40, vous pouvez deviner ce qui se passe (oui, c'est en fait la direction verticale). C'est tout!

Ajoutons la méthode de mise à jour du lecteur (comme le Gestionnaire de cloud, souviens toi?):

update () {this.sprite.position.x + = this.directionX * this.speed; this.sprite.position.y + = this.directionY * this.speed; }

... et bien sûr, n'oubliez pas de l'appeler dans la fonction loop de main.js

player.update ();

Si vous sauvegardez et rechargez le jeu, cela (devrait) fonctionner correctement, sauf que le vaisseau continue de bouger lorsque nous relâchons les touches fléchées. C'est évident car nous n'avons pas terminé la fonction onKeyUp, qui capture la clé qui vient d'être relâchée. Ainsi, nous pouvons l'arrêter en définissant directionX ou directionY sur 0.

Il n'y a qu'un petit problème: que se passe-t-il si nous mettons l'adresse à 0 parce que nous avons relâché la touche gauche, mais que la touche droite est toujours enfoncée?

Oui, le vaisseau spatial va s'arrêter, et il faudrait à nouveau appuyer sur la touche droite pour se déplacer, ce qui n'est pas très logique.

Alors pourquoi ne pas vérifier si la touche droite ou gauche est toujours enfoncée, afin que nous puissions réajuster l'adresse sur cette touche précédemment pressée?

C'est ce que nous allons faire. Et pour cela, nous devons stocker l'état de chaque touche dans un booléen, enfoncé ou relâché (vrai ou faux). Mettons tous ces états dans une variable objet dans le constructeur du lecteur:

this.keyState = {37: faux, 38: faux, 39: faux, 40: faux};

Si une touche (identifiée par son propre code) est enfoncée, alors nous changeons simplement son état en vrai, et si elle est relâchée, nous la mettons à faux. Facile non?

Ajoutons cette ligne au début de onKeyDown:

this.keyState [key.keyCode] = true;

... et cela dans onKeyUp:

this.keyState [key.keyCode] = false;

Maintenant, nous pouvons continuer avec la logique onKeyUp: si l'une des touches horizontales ou verticales est relâchée mais que l'opposé continue d'être enfoncée, on change la direction vers la dernière. Si les deux sont libérés, nous arrêterons le navire:

if (! this.keyState [37] && this.keyState [39]) this.directionX = this.keyCodes [39]; else if (this.keyState [37] &&! this.keyState [39]) this.directionX = this.keyCodes [37]; sinon this.directionX = 0; if (! this.keyState [38] && this.keyState [40]) this.directionY = this.keyCodes [40]; else if (this.keyState [38] &&! this.keyState [40]) this.directionY = this.keyCodes [38]; sinon this.directionY = 0;

Noter: Il existe plusieurs façons de gérer le mouvement du vaisseau spatial, ce n'est certainement pas la plus propre, mais je pense que c'est l'une des plus simples à comprendre.

Nous sommes prêts, enregistrez et rechargez et profitez-en!

Actualisation: Le vaisseau spatial peut quitter l'écran! Essayez de vérifier votre position avant de la mettre à jour. Vous pouvez vérifier le résultat ici.

Lancement de fusée

Maintenant que nous pouvons contrôler le navire, nous voulons qu'il tire des roquettes.

Commençons par ajouter le sprite dans le dossier assets:

PIXI.loader.add (["assets / cloud_1.png", "assets / cloud_2.png", "assets / spaceship.png", "assets / rocket.png"]). Load (init);

Créez un nouveau fichier appelé Rocket.js dans la dossier src et ajoutez-le index.html avec les autres types:



<script src="../src/lib/pixi.min.js"></script>
<script src="../src/Player.js"></script>
<script src="../src/CloudManager.js"></script>
<script src="../src/Rocket.js"></script>
<script src=“../src/main.js"></script>


Revenons à la classe Players. Nous voulons que vous puissiez tirer une fusée si vous appuyez sur le bouton de la barre d'espace. La fonction onKeyDown Il détecte déjà ces événements, il suffit donc de créer une condition avec le code clé souhaité (barre d'espace dans cet exemple):

onKeyDown (clé) {this.keyState [key.keyCode] = true; if (key.keyCode == 32) {let rocket = new Rocket (this.sprite.position.x, this.sprite.position.y); } if (key.keyCode == 37 || key.keyCode == 39) this.directionX = this.keyCodes [key.keyCode]; else if (key.keyCode == 38 || key.keyCode == 40) this.directionY = this.keyCodes [key.keyCode]; }

Nous donnons la position du vaisseau spatial dans le constructeur de la fusée, donc à l'intérieur, nous fixons simplement sa position à celle du vaisseau spatial (avec un décalage pour qu'il apparaisse devant le vaisseau plutôt qu'au centre).

Alors viens, initialise Rocket.js avec le constructeur:

class Rocket {constructeur (x, y) {this.sprite = new PIXI.Sprite (PIXI.loader.resources ["assets / rocket.png"]. texture); this.sprite.anchor.set (0,5, 0,5); this.sprite.position.set (x + 40, y); stage.addChild (this.sprite); }}

D'accord, si vous enregistrez et rechargez, vous pouvez tirer des roquettes (statiques) en appuyant sur la barre d'espace!

Pour qu'elles se déplacent, nous devons créer une variable de tableau contenant toutes les fusées existantes dans le jeu (nous ne savons pas combien d'entre elles sont sur la scène):

let _list = new Array (); class Rocket {statique get list () {return _list; } liste de jeux statiques (valeur) {_list = valeur; } ...

La variable _liste il est en dehors de la classe car il est statique, ce qui signifie que sa valeur est unique et qu'il ne s'agit pas seulement de la propriété d'un objet (par opposition à cela). Cependant, nous pouvons l'obtenir et le configurer comme nous le voulons (avec les deux premières lignes de la classe).

Nous pouvons pousser l'objet courant dans cette liste (à l'intérieur du constructeur) et déclarer la variable de vitesse en même temps:

this.speed = 20; Rocket.list.push (ceci)

…. et ajoutez également une méthode de mise à jour:

update () {this.sprite.position.x + = this.speed; if (this.sprite.position.x> renderer.width * 1.1) {this.sprite.destroy (); Rocket.list.splice (Rocket.list.indexOf (ceci), 1); }}

C'est fondamentalement le même qu'avant, nous mettons à jour la position X de la fusée (et non le y car il ne bouge pas verticalement) et tout comme les nuages on le supprime quand il sort des limites de l'écran, sauf que cette fois c'est le bord droit.

Après ça, dans la boucle main.js, il suffit d'analyser la liste des fusées et d'appeler la fonction de mise à jour pour chaque élément:

function loop () {cloudManager.update (); player.update (); Rocket.list.map ((élément) => {élément.update ();}); requestAnimationFrame (boucle); renderer.render (étape); }

Maintenant, enregistrez-le, rechargez-le et testez-le!

Il tire, mais ce n'est pas automatique. Vous devez appuyer sur la touche de chaque fusée et lorsque vous ne bougez pas, c'est un peu bizarre parce qu'elle tire si vite. Ce que nous voulons, c'est pouvoir tirer automatiquement lorsque la touche est maintenue enfoncée, avec une vitesse réglable.

Nous allons définir deux nouvelles variables dans le constructeur Player: la cadence de tir (qui peut être modifiée) et le Cooldown, qui sera la valeur du timer:

this.fireSpeed = 10; this.fireCooldown = 0;

Nous devons également mettre à jour keyState pour ajouter la touche espace, car nous voulons savoir si elle est enfoncée ou non:

this.keyState = {32: faux, 37: faux, 38: faux, 39: faux, 40: faux};

C'est la fonction que nous utilisons pour déclencher (nous devons l'appeler dans la mise à jour du lecteur):

updateFire () {if (this.fireCooldown <this.fireSpeed) this.fireCooldown ++; if (this.keyState [32] && this.fireCooldown> = this.fireSpeed) {let rocket = new Rocket (this.sprite.position.x, this.sprite.position.y); this.fireCooldown = 0; }}

C'est assez simple: nous augmentons simplement la minuterie de 0 à la cadence de tir que nous avons définie et si la touche est enfoncée et que la minuterie a atteint la valeur, nous engendrons une fusée et remettons la minuterie à 0.

Cette fonction s'exécute en permanence dans la boucle de mise à jour du lecteur:

update () {this.sprite.position.x + = this.directionX * this.speed; this.sprite.position.y + = this.directionY * this.speed; this.updateFire (); }

Travail effectué! Si vous le souhaitez plus rapidement, diminuez simplement la vitesse de prise de vue (et augmentez-la pour une vitesse plus lente).