Lancer une explosion

pour Mathys (Rosny-sous-bois)

Les capacités spéciales permettent de donner au joueur un sentiment de puissance et peuvent lui permettre de surmonter les obstacles du jeu de façon créative. Si elles sont trop puissantes, les capacités spéciales peuvent nuire à l'expérience de jeu en réduisant sa part de challenge, tandis que lorsqu'elles sont rares, elles peuvent être une surprise mémorable pour le joueur. Pour mesurer l'usage d'une capacité, il est possible de l'associer à un objet à trouver dans le jeu, ou encore ralentir le temps de rechargement du pouvoir pour obliger le joueur à en mesurer l'usage.

Importer des effets sonores

Tu peux réaliser tes effets sonores toi-même, ou utiliser ceux-ci dessous (click droit -> enregistrer-sous).

Charger le son dans le programme

Importe ensuite ton son dans ton projet en la plaçant dans le bon dossier : assets/sounds/. Puis, ajoute cette ligne au début de ton script game.js.

// chargement des sons du jeu
loadSound( "hit"    , "assets/sounds/hit.mp3"    ) ;
loadSound( "portal" , "assets/sounds/portal.mp3" ) ;

loadSound( "mystic" , "assets/sounds/mystic.mp3" ) ; loadSound( "dune" , "assets/sounds/dune.mp3" ) ;

Créer un nouveau composant

Pour créer cette capacité, nous allons devoir créer un composant. Celui-ci comprend beaucoup de paramètres, tu peux les modifier si tu le souhaites, mais attention au résultat ! Rends-toi dans le script component.js et ajoutes-y cette fonction.

// composant permettant de tirer un laser function explosion( explosionKey = "space" ){ const sul = 1 ; // vitesse à laquelle l'énergie se vide const srl = 2 ; // vitesse à laquelle l'énergie se remplie const laa = 2 , las = 20 ; // variation cercle chargement amplitude , vitesse const lt = 0.8 ; // temps de chargement en secondes, vitesse de const lrl = 10; // vitesse de reiniyialisation du chargement const cr=60 , cm=30 , opl = 0.4 ; const rla = 10 , rls = 30 ; // pendant le lancement : amplitude, vitesse const camZoom = 0.25 ; // zoom de la caméra const dfls = 2 ; // vitesse de la deflagration const dfld = 300 ; // diamètre de la deflagration const shakeA = 6 ; // amplitude de la secousse const minE = 0.2 ; // seuil de déclanchement de l'effet // explosion const explosion = add([ circle(1), color(250,200,200), area(), pos(0, 0), origin("left"), opacity(), "explosion", ]); // lancement const loadingExplosion = add([ circle(1), color(250,200,200), pos(0, 0), origin("bot"), opacity(), ]); const fx1 = play("mystic", {volume: 0.2,}) ; const fx2 = play("dune", {volume: 0.2,}) ; let loading = 0 ; let loadingC = 0 ; let power = 0 ; let explosionRange = 0 ; let energy = 1 ; return { load(){ fx2.stop(); }, update(){ if ( isKeyDown( explosionKey ) ) this.loadExplosion(); else this.resetExplosion(); const radA = (loading/lt * (cr-cm) ) + cm ; const radS = Math.sin(time()/lt*las)*laa*loading/lt ; loadingExplosion.radius = radA + radS; loadingExplosion.opacity = loading/lt * opl ; loadingExplosion.pos = this.pos; explosion.pos = this.pos; camScale(1+loading*camZoom); }, launchExplosion(){ explosionRange = Math.min(explosionRange+dt()*dfls,power) ; let sinRange = Math.sin(explosionRange/power*Math.PI/2); explosion.radius = sinRange*dfld*power; explosion.opacity = 1-sinRange; shake((1-sinRange)*shakeA*power); fx2.play(); if(explosionRange==power){ power=0; explosionRange=0; fx2.stop(); } every("destructible", (e) => { const dx = Math.pow( e.pos.x-this.pos.x-e.width/2, 2 ) ; const dy = Math.pow( e.pos.y-this.pos.y-e.height/2 , 2 ) ; const d = Math.sqrt( dx + dy ) ; if (d<explosion.radius){ destroy(e); play("hit"); } }); }, resetExplosion(){ loading = Math.max(0, loading - dt() * lrl ) ; this.angle = 0 ; fx1.stop(); if(power>minE) this.launchExplosion(); energy = Math.min(1,energy+dt()*srl); loadingExplosion.pos = this.pos; explosion.pos = this.pos; }, loadExplosion(){ if (energy>0){ loading = Math.min(1, loading + dt() * sul ) ; } if (loading<1){ energy = Math.max(0, energy - dt() * sul ) ; } this.angle = Math.sin(time()/lt*rls)*rla*loading/lt; power = loading ; explosionRange = 0 ; explosion.radius = 1 ; explosion.opacity = 0 ; fx1.play(); fx2.stop(); }, } }

Ajouter le nouveau composant

Nous devons maintenant ajouter ce nouveau composant au joueur. Dans le script game.js, à l'intérieur de la scène game, trouve la variable player et ajoutes-y cette ligne.

// définition du joueur
const player = add([

  pos(0,0) ,
  area() ,
  body() ,
  controller() ,
  sprite( "bean" ) ,
  origin( "center" ) ,

  explosion() ,

  "player",

]);

Il ne te reste plus qu'à ajouter le label destructible à tous les objets qui peuvent être détruits par cette capacité.

facultatif : Afficher une barre d'énergie

Tu peux choisir d'afficher une barre pour que ton joueur puisse voir l'état de sa jauge d'énergie. Ce peut-être un moyen de lui signifier l'existence de cette capacité, ou de lui montrer l'état de sa jauge si l'utilisation de la capacité est limitée.

Créer un nouveau composant

Ce composant va nous permettre d'afficher la jauge. Tu vas pouvoir l'utiliser pour cette capacité mais aussi pour d'autres si tu le souhaites. Pour cela, ajoute cette fonction dans le script component.js.

function UImeter( x = 14 , y = 14 , w = 160 , h = 30 , op = 4){ let value = 0 ; const bgRect = add([ rect(w,h,{radius:6}), color(255,255,255), opacity(0.2), outline(op), fixed(), pos(x, y), ]); const valueRect = add([ rect(w-op,h-op,{radius:4}), color(250,200,200), fixed(), pos(x+2, y+2), ]); return { update(){ valueRect.width = value * (w-op) ; }, setValue(v){ value = v ; } } }

Ajouter le nouveau composant

Nous allons maintenant ajouter ce nouveau composant au joueur. Retourne dans le script game.js, à l'intérieur de la scène game, puis ajoute cette ligne dans la variable player.

// définition du joueur
const player = add([

  pos(0,0) ,
  area() ,
  body() ,
  controller() ,
  sprite( "bean" ) ,
  origin( "center" ) ,
  explosion() ,

  UImeter() ,

  "player",

]);

La barre est maintenant visible mais ne représente pas encore l'état de l'effet.

Mettre à jour la valeur

Pour que cette jauge affiche la bonne valeur, rends-toi à nouveau dans le script component.js et ajoute cette simple ligne dans le composant explosion.

update(){
      
  if (isKeyDown("space")) this.loadExplosion();
  else this.resetExplosion();
  
  const radA = (loading/lt * (cr-cm) ) + cm  ;
  const radS = Math.sin(time()/lt*las)*laa*loading/lt ;
  
  loadingExplosion.radius  = radA + radS;
  loadingExplosion.opacity = loading/lt * opl ;
  loadingExplosion.pos     = this.pos;
  explosion.pos            = this.pos;
   
  camScale(1+loading*camZoom);
  
  this.setValue(energy);
  
},

BONUS : Créer des blocs de sable

Il peut être intéressant que cette nouvelle aptitude du joueur ait un impact sur l'environnement du jeu. Le hack ci-dessous te propose de créer un bloc destructible avec un composant qui le rend sensible à la gravité, comme dans l'exemple en haut de la page.

Importer une image

Pour commencer, il va te falloir une image. Réalises-en une si tu ne l'as pas déjà fait, ou utilise celle ci-dessous (click droit -> enregistrer-sous).

Charger l'image dans le programme

Importe ensuite ton image dans ton projet en la plaçant dans le bon dossier : assets/sprites/. Puis, ajoute cette ligne au début de ton script game.js, comme suit.

// chargement des images du jeu
loadSprite( "bean"   , "assets/sprites/bean.png"   ) ;
loadSprite( "grass"  , "assets/sprites/grass.png"  ) ;
loadSprite( "portal" , "assets/sprites/portal.png" ) ;

loadSprite( "sand", "assets/sprites/sand.png"      ) ;

Déclarer un symbole

Nous allons maintenant déclarer un nouveau type d'objet que tu vas pouvoir placer dans ton jeu. Dans cet exemple nous avons choisi le caractère S. Dans le script level.js, trouve la variable LEVEL_CONFIG et ajoutes-y le code ci-dessous, comme suit.

// défini ce à quoi correspond chaque symbole dans le niveau
const LEVEL_CONFIG = {
  
  // taille en pixel de chaque case
  width: 64,
  height: 64,

  // sol
  "=": () => [
    sprite("grass"), 
    area(), 
    solid(), 
    origin("bot")
  ],
  
// sable "S": () => [ sprite("sand"), area(), solid(), body(), origin("bot"), "destructible", ],
} ;

Placer les blocs

Tu vas maintenant pouvoir choisir l'emplacement de tes blocs destructibles dans ton niveau. Toujours dans le script level.js, trouve le tableau LEVELS et places des caractères S là où tu le souhaites.

// plan du niveau
const LEVELS = [
  
  [
    "        S            " ,
    "    S   S       S    " ,
    "    S   S   S   S    " ,
    "    S   S   S   S    " ,
    "    S   S   S   S    " ,
    " #  S   S   S   S  @ " ,
    "=====================" ,
  ],
  
];

Tout a déjà l'air de fonctionner, à un détail près. Le blocs peuvent tomber sur la tête de personnage et y rester, l'empêchant ainsi de sauter. Cela peut être amusant, mais peut aussi être gênant pour la suite de ton jeu. Cette dernière étape va permettre aux blocs qui tombent sur ton joueur de se détruire automatiquement.

Créer un nouveau composant

Pour cela, une fois de plus, il va nous falloir un nouveau composant. Rends-toi une dernière fois dans le script component.js et ajoutes-y cette fonction.

// composant permettant au sable de disparaître s'il tombe sur le joueur function sand() { return { require: [ "pos" , "area", "body" , "solid" ], add() { this.on("collide", (obj, col) => { if (obj.is("player") && col.isBottom()) { destroy(this); play("hit"); } }) }, } }

Ajouter le nouveau composant

Il ne nous reste plus qu'à ajouter ce nouveau composant à notre nouvel objet. Retourne dans le script level.js, et retrouve dans la variable LEVEL_CONFIG le passage où sont déclarés nos blocs, puis ajoutes-y ces lignes, comme suit.

 // sable
"S": () => [
  sprite("sand"), 
  area(), 
  solid(), 
  body(),
  origin("bot"),

  sand(),

  "destructible",
],