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

11 January 2015 08:52 Slick2d - Leçons 10-19 Java, Jeux, Slick2d, Tutorial

Voici certainement le tuto qu'on m'as le plus demandé ! Nous allons faire des combats. Je vous vois venir vous espérez déjà des combats en temps réel comme dans un jeu d'Action - Aventure, mais ce n'est pas pour tous de suite. Nous allons commencé doucement par les combat au tour par tour dans l'esprit des classiques J-Rpg. En préambule, il est impératif d'avoir lu et bien compris les leçons 10 et 11 sur la séparation en objet et la précédente leçon sur les phases de jeux.

Préparation :: Mise en place des boucles.

Si vous avez bien compris la leçon précédente, vous en aurez déduit que l'écran de combat sera une phase de jeux supplémentaire qui sera lancé de manière aléatoire depuis l'écran de jeu (celui qui contiens la carte).

Qu'allons nous avoir besoin dans ce nouvel écran de jeu ? Une boucle de jeu contenant un joueur, un ou plusieurs ennemi, afficher un fond, et donner des ordres de combat.

  • La boucle de jeu nous avons déjà vu comment en faire une, ici je l’appellerai BattleStateGame, elle fait logiquement suite à MainScreenStateGame et MapGameState.
  • Pour le joueur c'est plus compliqué, le joueur n'est pas le même que celui que nous utilisons depuis le début. En effet la méthode d'affichage n'est absolument pas adapté à un écran de combat comme nous l'imaginons. Il va falloir le retravailler.
  • Afficher un fond on sais faire on l'as vu sur l'écran d'introduction dans la leçon précédente.
  • Afficher un monstre, c'est simplement afficher un sprite, comme on le fait depuis la leçon 3.
  • Pour recevoir les ordres, je vous propose pour l'instant une solution simple, l'appui d'une touche donnera l'ordre à exécuter. par exemple "A" pour Attaquer, "F" pour Fuir, "M" pour magie, etc... je préviens on ne verra pas tous aujourd'hui.

Code :: BattleGameState

Pour l'instant je me contente d'un nouvel écran qui ne fait strictement rien, c'est à dire il affichera un écran noir et à l'appui d'une touche nous retournerons à l’écran de carte. En gros c'est quelque chose d'assez proche de notre écran de titre que nous avons vu dans l'exercice précédent.

BattleGameStatepublic class BattleGameState extends BasicGameState {
  public static final int ID = 3;
  private StateBasedGame game;

  @Override
  public void init(GameContainer container, StateBasedGame game) throws SlickException {
    this.game = game;
  }

  @Override
  public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException { }

  @Override
  public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException { }

  @Override
  public void keyPressed(int key, char c) { 
    game.enterState(MapGameState.ID);
  }

  @Override
  public int getID() {
    return ID;
  }
}

Il faut qu'on puisse entrer dans notre nouvelle boucle, pour cela déclarons la dans la classe principale, celle qui hérite de StateBasedGame, avec les deux autres boucles que nous avons déjà :

StateGame@Override
public void initStatesList(GameContainer container) throws SlickException {
  addState(new MainScreenGameState());
  addState(new MapGameState());
  addState(new BattleGameState());
}

Pour rentrer dans cette boucle, allons nous introduire un peu d'aléatoire, c'est à dire que de temps en temps dans l'update() de la classe MapGameState, nous entrerons dans la boucle BattleGameState. Je vais choisir arbitrairement une chance de 1 pour 1000 à chaque itération, j'ajoute également qu'il faut que le joueur se déplace, c'est ce qu'on observe généralement dans ce genre de jeu.

MapGameState@Override
public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
  // mise à jour, joueur, camera et trigger (cf leçon <a title="Slick2d, leçon 10 :: Séparation en objet" href="http://www.shionn.org/slick2d-lecon-10-separation-en-objet">10 et <a title="Slick2d, leçon 11 :: Séparation en objets (suite)" href="http://www.shionn.org/slick2d-lecon-11-separation-en-objets-suite">11)
  if (Math.random() < 0.001 && player.isMoving()) {
    game.enterState(BattleGameState.ID);
  }
}

A ce niveau lançons le jeu pour voir ou on en est. Nous avons notre écran titre.

main screen

Appuyons sur une touche et nous arrivons à l'écran de la carte.

ballade

Déplaçons nous un peu, nous arrivons à l'écran de combat, qui pour l'instant est noir, si nous appuyons sur une touche nous revenons à l'écran de la carte.

black screen

Voyons maintenant comme il serai possible de remplir cet écran noir.

Préparation :: écran de combat

J'ai choisi de reprendre une image de fond semblable à celle de l'écran titre, que j'ai trouvé dans ce pack sur opengameart. J'ai mis cet image dans le dossier src/main/resources/background sous le nom battle.png.

background

J'ai cherché des monstres, mon choix s'est porté sur ce pack, j'ai extrais le gobelin et je l'ai retourné pour qu'il regarde à gauche. J'ai mis cet image dans le dossier src/main/resources/battle sous le nom gobelin.png.

ennemies gobelin

J'ai cherché un moment une image pour représenter le héro, et j'ai choisi le lancier de ce pack. Je l'ai extrait, retourné pour qu'il regarde à droite et enlevé le fond. J'ai mis cet image dans le dossier src/main/resources/battle sous le nom hero.png.

heroes hero

Mettons tous ca en place dans la boucle de des combats

Code :: BattleGameState

Dans la méthode init(), il nous faut charger les images comme nous l'avons déjà vu à plusieurs reprise.

Classe :: BattleGameState@Override
public void init(GameContainer container, StateBasedGame game) throws SlickException {
  this.game = game;
  this.background = new Image("background/battle.png"png");
  this.ennemy = new Image("battle/gobelin.png"png");
  this.hero = new Image("battle/hero.png");
}

Puis dans la méthode render() il nous faut afficher tous cela. L'idée est de mettre le joueur à gauche au quart de l'écran et l'ennemi à droite, c'est à dire à trois quart en partant de la gauche.

Classe :: BattleGameState@Override
public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
  background.draw(0, 0, container.getWidth(), container.getHeight());
  hero.drawCentered(container.getWidth() * 1 / 4, container.getHeight() / 2);
  ennemy.drawCentered(container.getWidth() * 3 / 4, container.getHeight() / 2);
}

Et voila le résultat :

tuto-slick2d-092-battle-screen Bon les personnages sont un peu petit. La solution la plus simple et d'utiliser une copie deux fois plus grande des images en 2 fois plus grande grâce à la méthode getScaledCopy().

// Classe :: BattleGameState@Override
public void init(GameContainer container, StateBasedGame game) throws SlickException {
  this.game = game;
  this.background = new Image("background/battle.png"png");
  this.ennemy = new Image("battle/gobelin.png"png").getScaledCopy(2);
  this.hero = new Image("battle/hero.png").getScaledCopy(2);
}

Et voici le rendu.

tuto-slick2d-093-battle-screen-big

Pour aller plus loin

Ce sera tous pour aujourd'hui, nous verrons dans le prochain article comment gérer les points de vie de notre gobelin et de notre héro. Et ainsi pour faire une première attaque !

Pour l'instant on peu toujours chercher à améliorer l’algorithme aléatoire du déclenchement du combat. On peu par exemple introduire une notion de temps, c'est à dire faire en sorte que plus le temps passe sans qu'il y ai de combat, plus la chance d'entrée en combat est grande.

Ressources

par Shionn, dernière modification le 09 April 2017 17:41
7 réflexions au sujet de « Slick2d, leçon 16 :: Les combats aux tour par tour (1/6) »
  • Oukem 13 February 2015 03:22

    Salut ton blog est vraiment sympathique, j'avais entendu parlé de slick2D mais je n'avais pas trouvé de tuto expliquant pas à pas comment s'y prendre comme tu le fait ici. J'ai commencé à m'exercer y a quelques heures mais j'adore déjà ! Je vais suivre ton blog régulièrement.

    Je te remercie de mettre ton savoir à disposition et j’espère que tu va continuer à faire des tutos sur les différents aspects de jeu. En te souhaitant du courage et une bonne continuation.

  • Shionn 13 February 2015 06:51

    Je te souhaite alors une bonne lecture. Soit indulgent avec mes fautes de francais ;)

    Il va y avoir encore pas mal de tuto.

  • Leo 24 May 2016 14:09

    Bonjour,

    J'essaye de changer le background de l'écran de battle selon la map mais je n'y parviens pas.

    J'ai rajouté une propriété "nom" sur mes maps mais je ne parviens pas à faire passer ma map(ou au minimum son nom) entre les différentes states.

    Merci :D

  • Shionn 25 May 2016 10:30

    Salut leo,

    Dans la méthode init d'un BasicGameState tu as un paramètre de type StateBasedGame. Sur cet objet tu peu faire un getState avec un identifiant pour obtenir le state de combat depuis ton state de map.

  • Leo 25 May 2016 18:48

    Oui mais je ne parviens pas à récupérer les attributs de la State en utilisant le getState

  • Shionn 25 May 2016 18:54

    Bien évidement le type retourné est forcement "BasicGameState".

    Mais rien ne t’empêche de le caster dans le type que tu connais. Dans mon cas je ferai un :

    ((BattleGameState)game.getState(BattleGameState.ID)).setMap("nomDeLaCarte");
    //ou :
    ((BattleGameState)game.getState(BattleGameState.ID)).setBackgroundMap("nomDuFond");
    
  • Leo 25 May 2016 19:23

    Effectivement ça fonctionne

    Merci énormément, j'espère que tu reprendras bientot les tutos :D

Laissez un commentaire

Vous pouvez utilisez du markdown pour la mise en forme

Votre adresse de messagerie ne sera pas publiée.

Temporairement, pour lutter contre les bots, il n'est pas permis de mettre http:// dans le commentaire.