tuto-slick2d-083-logo

Slick2d, leçon 21 :: Les combats aux tour par tour (6/6)

Après une longue pause voici enfin le 6ieme et dernier article sur les combats aux tour par tour. Dans celui-ci nous verrons comment changer la musique au début et en fin de combat. Cette méthode permet également de déclencher des « choses » quand on change d’état. Comme cela fait un moment que je n’ai pas écris de tuto, il se peut que le style change un peu, les fautes d’orthographes resterons.

Actuellement la musique se charge au lancement du jeux et ne change plus tout au long du jeux. Nous allons voir comment changer cela.

Préparation

J’ai trouvé quelques musiques sur OpenGameArt, celle-ci pour le combat et celle-ci pour la victoire. Comme précédemment il faut convertir les musiques en OGG.

Code :: enter() // leave()

Comme nous avons vu dans la leçon 7, pour charger et lire une musique de fond il nous suffisait de faire cela dans une des méthodes init :

@Override
public void init() {
  Music background = new Music("sound/lost-in-the-meadows.ogg");
  background.loop();
}

Pour que la musique ne se lance plus au démarrage du jeu, il nous suffit d’enlevé l’appel à la méthode loop(). Ensuite il nous suffit de faire appel à cette méthode quand on « entre » dans un state et appeler la méthode stop() quand on le quite, mais comment ?
Dans les GameStates il est possible de surcharger les méthodes enter() et leave() qui sont chacune respectivement appelées quand on entre dans un state et qu’on le quite.

Nous pouvons ainsi modifier notre MapGameState comme ceci :

classe :: MapGameState
public class MapGameState extends BasicGameState {
  // ...
  private Music background;

  @Override
  public void init() {
    // ...
    this.background = new Music("sound/lost-in-the-meadows.ogg");
    // ...
  }

  @Override
  public void enter() {
    this.background.loop();
  }

  @Override
  public void leave() {
    this.background.stop();
  }

  // ...

Lire une musique une seule fois.

Nous pouvons utiliser exactement la même méthode pour la musique de combat. Mais on tombe sur un os si on souhaite lire une musique particulière une seule fois à la fin du combat. En effet si sur le « leave » du BattleGameState on joue la musique on aura l’impression que celle-ci se joue sur l’écran de la carte.
Nous pourrions ajouter un nouveau game state ou on afficherait nos gain etc … mais je vais plutôt vous montrer comment lire une musique une seule fois et ensuite changer de state.

Code

Pour cela nous allons modifier notre classe BattleController, qui je vous le rappel se charge entre autre de nous faire retourner à la carte en fin de combat.
Actuellement pour quitter le combat nous faisont appel à ce code :

// permet de rtourner à la "carte"
this.game.enterState(MapGameState.ID);
// permet de désactiver les futures commande
this.mode = BattleCommand.NONE;

Nous allons remplacer ce code par ceci qui va nous permettre d’ajouter une pause de 5 secondes à la fin du combat :

Classe :: BattleGameState
public void quitBattle(final int nextState) {
  // création d'une pause dans un autre thread pour ne pas bloquer l'affichage
  new Thread(new Runnable() {
    @Override
    public void run() {
      try {
        // pause de 5 secondes
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
      this.mode = BattleCommand.NONE;
      this.game.enterState(nextState);
    }
  }).start();
}

Pour lancer une musique une seule fois il faut utiliser la méthode play() sur la musique que l’on souhaite. Chargeons cette musique quand nous initialisons le controller :

Classe :: BattleGameState
private Music victory;

public BattleController(BattlePlayer player, BattleEnnemy ennemy, StateBasedGame game) throws SlickException {
  // ...
  this.victory = new Music("sound/lively-meadow-victory-fanfare.ogg");
}

Et jouer la musique quand on gagne le combat :

Classe :: BattleGameState
private void endPlayerAttack() {
  if (ennemy.getPv() <= 0) {
    hud.addLog("Vous avez gagné !");
    victory.play();
    quitBattle(MapGameState.ID);
  } else {
    // ...
}

Pour aller plus loin

Comme toujours on peu faire mieux, ajouter des bruitages pendant le combat ou ajouter une musique en cas de défaite par exemple.

Je ne sais pas ce que je vous proposerai dans le prochain tuto ni quand est-ce qu’il arrivera. Je pense aborder les « entités » c’est un concept assez vague mais qui permet de mieux concevoir notre jeux.

Ressource :

Magic-The-Gathering-Tactics-6151

MtgDb – Fait peau neuve

Bonjour à tous,

Promis je ne suis pas mort, mais je travail sur d’autre projet en ce moment. Dernièrement j’ai entièrement refait ma base de donnée sur le jeu de carte Magic the Gathering.

  • Interface un peu plus simple
  • Possibilité d’ouvrir un compte et d’enregistrer ces Decks.
  • Plus d’éditions
  • Moteur de recherche toujours plus complet.

Je doit encore nettoyer un peu le code avant de le poussé sur github mais il le sera bientôt.

http://mtg.shionn.org

Il n’est pas encore fini, je pense qu’il manque des choses comme la gestion des limitations par format.

Slick2d, leçon 20 :: Les combats aux tour par tour (5/6)

Aujourd’hui on va voir comment ajouter un hud à notre combat par l’intermédiaire de bouton cliquable et une fenêtre de texte qui contiendra les logs du combat.

Préparation :: un bouton.

Comme pour la leçon 13 j’ai utilisé la ressource Golden UI sur OpenGameArt pour faire un bouton que voici :

tuto-slick2d-099-buttons

Code :: BattleHud

Comme pour la leçon 13 créons une classe dédiée à l’affichage du hud BattleHud.

Classe :: BattleHud
public class BattleHud { 

  public void init(GameContainer container) throws SlickException { }
  
  public void render(GameContainer container, Graphics g) { }

}

Un bouton dans Slick c’est une « zone clickable » avec une image à l’intérieur, ces zones sont représentés avec la classe MouseOverArea. On va utiliser le constructeur avec les arguments :

  • GameContainer : container du jeux
  • Image : image du bouton
  • int x, int y : coin supérieur gauche
  • ComponentListener : écouteur pour les évènements

Construisons nos boutons,  dans la méthode init() et comme d’habitude affichons les dans la méthode render(). Il y a 3 boutons qui correspondent au 3 actions possible dans notre combat.

Classe :: BattleHud
// espace entre les éléments de l'interface
private static final int SPACE = 5;
// pour le centrage du text dans les boutons
private static final int Y_PADDING = 3; 
private static final int X_PADDING = 13;

private MouseOverArea attackButton;
private MouseOverArea fleeButton;
private MouseOverArea defendButton;

public void init(GameContainer container) throws SlickException {
  Image buttonImage = new Image("hud/button.png");
  attackButton = new MouseOverArea(container, buttonImage, SPACE, container.getHeight() - (buttonImage.getHeight() + SPACE) * 3, this);
  defendButton = new MouseOverArea(container, buttonImage, SPACE, container.getHeight() - (buttonImage.getHeight() + SPACE) * 2, this);
  fleeButton = new MouseOverArea(container, buttonImage, SPACE, container.getHeight() - (buttonImage.getHeight() + SPACE) * 1, this);
}

public void render(GameContainer container, Graphics g) { 
  g.setColor(Color.black);
  attackButton.render(container, g);
  g.drawString("Attaquer", attackButton.getX() + X_PADDING, attackButton.getY() + Y_PADDING);
  defendButton.render(container, g);
  g.drawString("Defendre", defendButton.getX() + X_PADDING, defendButton.getY() + Y_PADDING);
  fleeButton.render(container, g);
  g.drawString("Fuire", fleeButton.getX() + X_PADDING, fleeButton.getY() + Y_PADDING);
}

Les calculs sont fait pour que les trois boutons soit placés l’un en dessous de l’autre dans le coin inférieur gauche. Pour que notre Hud puisse capter les évènements de notre souris il doit implémenter l’interface ComponentListener.

Classe :: BattleHud
public class BattleHud implements ComponentListener {
  // [...]

  @Override
  public void componentActivated(AbstractComponent source) {
  }
}

La méthode en argument la source, c’est à dire le bouton, qui est activé quand on clic dessus. Il faut encore compléter cette méthode en appelant notre BattleController pour activer les actions de combat qui correspondent au bouton.

Classe :: BattleHud
// [...]
private BattleController controller;

public BattleHud(BattleController controller) {
  this.controller = controller;
}
// [...]

@Override
public void componentActivated(AbstractComponent source) {
  if (source == attackButton) {
    controller.controlPressed(BattleCommand.ATTACK);
  } else if (source == defendButton) {
    controller.controlPressed(BattleCommand.DEFEND);
  } else if (source == fleeButton) {
    controller.controlPressed(BattleCommand.FLEE);
  }
}
// [...]

Il faut intégrer tous cela dans notre la boucle de combat, cela commence à devenir routinier.

Classe :: BattleGameState
// [...]
private BattleHud hud;

@Override
public void init(GameContainer container, StateBasedGame game) throws SlickException {
  // initialisation, vu dans les leçons précédentes
  BattleController controller = new BattleController(player, ennemy, game);
  provider.addListener(controller);
  this.hud = new BattleHud(controller);
  this.hud.init(container);
}

@Override
public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
  // affichage, vu dans les leçons précédentes
  this.hud.render(container, g);
}
// [...]

Et voici le résultat :

tuto-slick2d-100-battle-buttons

Un log de combat

Pour la suite il vous faut importer SlickUtils dans sa version 1.3, pour utiliser ma dernière version du TextArea, qui support des polices par défaut. Ajoutons notre log à l’initialisation et au rendu :

Classe :: BattleHud
// [...]
private TextArea log;

public void init(GameContainer container) throws SlickException {
  log = new TextArea(SPACE + attackButton.getWidth() + SPACE, attackButton.getY(), container.getWidth() - attackButton.getWidth() - SPACE * 3, buttonImage.getHeight() * 3 + SPACE * 2);
  // affichage de bas en haut
  log.setBottomUp(true); 
  log.setDefaultFont(container.getDefaultFont());
}
// [...]

public void render(GameContainer container, Graphics g) { 
  // [...]
  g.drawString("Fuire", fleeButton.getX() + X_PADDING, fleeButton.getY() + Y_PADDING);
  log.render();
}
// [...]

public void addLog(String text) {
  log.addFirstText(text, VerticalAlignement.LEFT);
}
// [...]

Maintenant il faut passer notre instance du hud au contrôleur pour que celui-ci puisse écrire dans le log.

Classe :: BattleHud
  // [...]
  public BattleHud(BattleController controller) {
    this.controller = controller;
    this.controller.setHud(this);
  }
  // [...]

Et dans le contrôleur :

Classe :: BattleControllerpublic class BattleController implements InputProviderListener {
  // [...]
  private BattleHud hud;

  // [...]

  public void setHud(BattleHud hud) {
    this.hud = hud;
  }
  // [...]

Pour finir il reste à appeler la méthode addLog de notre hud depuis la classe BattleController, pour afficher les messages qu’on souhaite lors des différentes étape du combat. C’est trivial aussi je passe cette partie sous silence. Mais voici le résultat :

Pour aller plus loin

Le combat avance bien, la prochaine fois on verra comment changer la musique et comment ajouter une pause a la fin du combat avant de retourner à la carte.

Ressources