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

22 mars 2015 09:07 Slick2d - Leçons 10-19 Java, Jeux, Slick2d, Tutorial

Suite et toujours pas la fin sur les combats au tour par tour. Dans l'article précédent nous avons vu comment ajouter un système de commande pour traiter les attaques et défense lors du combat. Mais plusieurs problèmes ce soulevaient alors et voici ceux qu'on va résoudre aujourd'hui :

  • Non réinitialisation des pv des monstres.
  • Le combat est très statique et manque d'animation

Réinitialiser les points de vie du monstre.

On m'as demandé dans les commentaires, et plusieurs fois par mail, comment rappeler la méthode init(). Même si on pourrait, il ne faut pas. Sans rentrer dans les détails, la méthode init() sert à initialiser les ressources (image, son, contrôleur, etc..) nécessaires à la boucle, et si on la rappelle, on recharge encore une fois les ressources. Parfois ce n'est pas gênant, mais bien souvent c'est le cas. Prenons le cas d'une image, elle est chargée avec un objet java mais également dans la ram de votre carte graphique, n'oublions pas que Slick2D utilise OpenGL. Si l'objet java n'est plus référencé, java va le supprimer de votre mémoire, mais java ne peu pas savoir que image est également dans la ram de la carte graphique et va y rester ! Bien sur qu'il est possible de libérer cette mémoire, mais il faut l'indiquer à Slick2D.

Alors comment est-ce que l'on fait donc hein ? On utilise la méthode enter() qui est définit dans l'interface GameState on peu donc la surcharger dans nos boucles. Cette méthode est appelée quand on entre dans une boucle de jeux.

Code :: enter()

Ajoutons la méthode enter à notre BattleGameState.

// init(), cf leçon 17
@Override
public void enter(GameContainer container, StateBasedGame game) throws SlickException {
  this.ennemy.reset();
}
// render(), update(), ..., cf leçon 17

Et ajoutons la méthode reset() à notre monstre.

// [...], cf leçon 17
public void reset() {
  this.pv = 12;
}
// [...], cf leçon 17

Et voila normalement à ce state nous avons les pv du monstre que se remette à jour à chaque combat. Grâce à cela on pourrai également changer de musique lors du combat mais j'en parlerais dans un autre article.

Ajouter des animations de combat

Maintenant essayons de rendre nos combat un peu plus vivant en faisant bouger nos sprites. Je ne vais pas faire une animation ou l'on vois notre héro brandir une épée, se jeter sur l’ennemi et le pourfendre, car cela a déjà été vu, je vous assure, c'est la leçon 3 ! Mais plutôt juste faire faire un déplacement au sprite comme on le vois souvent dans les vieux J-RPG. Mais je ne veux pas les faire se déplacer simplement d'avant en arrière, ou simplement trembler, beaucoup trop simpliste. Je veux qui suivent une courbe, un peu comme cela :

tuto-slick2d-096-mouvement-voulu

Ce genre de courbe peu facilement être obtenu à l'aide d'une courbe de Bézier ! Qu'est-ce qu'une courbe de Bézier ? il s'agit d'une courbe définit par une moyenne pondéré de point de contrôle. Ceux qui ont déjà utilisé des outils de dessin vectoriel comme inkspace savent très bien de quoi je parle. Pour les autres je vous invite à lire cette article sur wikipedia.

Slick permet la définition de courbe de Bézier, et pratiquement ce qu'il faut pour faire une animation avec celle-ci. Mais Slick ne permet que les courbes de Bézier qu'à 4 points de contrôle, hors je ne souhait pas être limité à ce niveau. Aussi j'ai ajouté cette fonctionnalité à la lib SlickUtil, que je vous ai présenté précédemment, ainsi qu'un outil pour faire des animation avec ces courbes. Je vous laisse voir sur la page dédié pour l'intégrer à votre projet.

PathAnimation

PathAnimation est une classe du projet SlickUtils, elle permet de définir un parcourt le long d'un chemin Path durant une certaine durée exprimé en milliseconde. Pour l'instant je n'ai crée qu'une seule implémentation de chemin, BezierPath, qui comme vous vous en doutez il s'agit d'un chemin s'appuyant sur une courbe de Bézier, d'autant de point que vous le souhaitez.

Code :: PathAnimation

Commençons par définir l'animation de notre héro.


// [...] les autres variables cf leçon 17
private PathAnimation animation;

/**
 * à l'initialisation on définit une animations du parcourt que l'on souhait
 * 
 * ici l'animation fait partir le personnage vers l'avant 
 * puis le fait revenir à sa place sur une durée de 2000 millisecondes. 
 */
public void init() throws SlickException {
  // [..] chargement de l'image, cf <a title="Slick2d, leçon 17 :: Les combats aux tour par tour, (2/X)" href="http://www.shionn.org/slick2d-lecon-17-les-combats-tour-par-tour-2x">leçon 17
  this.animation = new PathAnimation(new BezierPath(0, 0, 400, 1, -50, 20, 0, 0), 2000);
}

/**
 * à l'affichage on prend en compte la position de l'animation 
 * pour afficher le joueur
 */
public void render(GameContainer container, Graphics g) {
  Vector2f p = animation.currentLocation();
  hero.drawCentered(p.x + container.getWidth() * 1 / 4, p.y + container.getHeight() / 2);
  // [...] affichage des pvs, cf <a title="Slick2d, leçon 17 :: Les combats aux tour par tour, (2/X)" href="http://www.shionn.org/slick2d-lecon-17-les-combats-tour-par-tour-2x">leçon 17
}

/**
 * Il faut également ajouter une méthode de mise à jour 
 * pour mettre à jour l'animation.
 */
public void update(int delta) {
  this.animation.update(delta);
}

/**
 * et une méthode pour lancer l'attaque d'un joueur
 */
public void startAttack() {
  this.animation.start();
}
// [...]
Maintenant que cela est fait, ajoutons les appels à la méthode startAttack dans la classe BattleController.
Classe :: BattleController
// [...]
private void attack() {
  this.player.startAttack();
  // [...] le reste ne bouge pas, cf <a title="Slick2d, leçon 17 :: Les combats aux tour par tour, (2/X)" href="http://www.shionn.org/slick2d-lecon-17-les-combats-tour-par-tour-2x">leçon 17
}

private void defend() {
  // [...] attaque du monstre, cf <a title="Slick2d, leçon 17 :: Les combats aux tour par tour, (2/X)" href="http://www.shionn.org/slick2d-lecon-17-les-combats-tour-par-tour-2x">leçon 17
  } else {
    this.player.startAttack();
    // [...] contre attaque, cf <a title="Slick2d, leçon 17 :: Les combats aux tour par tour, (2/X)" href="http://www.shionn.org/slick2d-lecon-17-les-combats-tour-par-tour-2x">leçon 17
  }
}
// [...]

Enfin n'oublions pas d’appeler la méthode de mise à jour dans notre BattleGameState.

Classe :: BattleController
// [...]
@Override
public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
  this.player.update(delta);
}
// [...]

Et normalement vous obtenez cela :

Animation du monstre

Il suffit de faire exactement le même code mais en adaptant la courbe de Bézier pour que le monstre parte dans l'autre sens.  Comme c'est exactement le même code, je vous passe les détails. Vous pouvez toujours avoir le contenu complet du code sur le repository git.

Pour aller plus loin

Voila deux chose de faite, mais ce n'est pas fini en effet, le monstre et le joueur attaque en même temps ce qui ne correspond pas au code ! Et en plus on n'attend pas la fin de l’animation pour quitter le combat ! Autant de problèmes qui trouverons solution dans le prochain tuto, grâce à un mécanisme de callback.

Ressources

par Shionn, dernière modification le 09 avril 2017 18:17
7 réflexions au sujet de « Slick2d, leçon 18 :: Les combats aux tour par tour (3/6) »
  • Alecks 09 juin 2015 20:11

    Bonjour,

    J'ai un problème avec Slick, j'ai un basicGame que je lance une fois et les images s'affiche correctement.

    Quand je fais un exit, que je retourne dans mon main et que je recréé une autre partie, les images identiques à l'ancienne partie sont remplacés par du blanc.

    Je suppose peut-être à tord qu'il y a peut être un conflit avec celles chargées dans la ram précédement? Si c'est le cas comment faire pour vider les images de la ram?

  • Shionn 12 juin 2015 21:18

    Tu fais fausse route je pense. Deux programme java ne peuvent pas se gêner niveau mémoire. Envoie moi ton code j'y jetterai un oeil.

    Sinon tes drivers sont à jour ? accessoirement tu utilises qu'elle implémentation d'OpenGL ?

  • Louloutre 15 octobre 2015 08:16

    Et si l'on veut appliquer cette pathanimation sur notre personnage, hors combat, dans le cas d'un rpg plus type "tLoZelda" ou "sword of mana",est-ce faisable avec notre tableau d'animation ? Par là j'entend sur une frame de l'animation enregistrée sur le mapplayer

  • Shionn 18 octobre 2015 08:20

    C'est à dire associer l'animation en frame et cette animation ? oui bien sur c'est possible. Il te suffit de bien prévoir les temps d'animation. Je voulais le faire pour cette article mais je manque de temps.

  • Louloutre 20 octobre 2015 08:56

    J'ai essayé de l'appliquer aux coordonnées du joueur (je ne fais pas de combat au tour par tour, je parle donc du joueur "Map") sans succès en créant une classe Vector2fPlusPlayer qui hérite de Vector2f mais avec un attribut player.

    C'est la première fois que je fais ça, je ne connais pas grand chose au vectoriel non plus mais il me semble qu'il suffirait d'indiquer à la courbe que son point de départ est la coordonnée du personnage, mais je n'arrive qu'à recopier le personnage sur lui même. Pourtant la modification me semble simple sur le papier, pourrais-tu m'aiguiller sur la façon de faire ?

  • Shionn 25 octobre 2015 07:50

    Voici une solution plus simple, garde les x/y du joueur comme ils sont dans le tuto sur le déplacement. Ensuite garde les vecteur comme ils sont dans mon truc d'animation.

    Par contre à l'affichage du joueur, au lieu de l'affiché au x,y comme on le fait des les tuto sur les déplacement tu l'affiche en x+vector.x et y+vector.y ^^

    Comme cela tu ajoute le vecteur d'animation à ton personnage.

    Il faut pour cela que ton vecteur d'animation parte de 0,0

  • Louloutre 26 octobre 2015 15:08

    Merci, ça marche :D Pour que l'animation se fasse dans toute les directions, j'ai fais un tableau de 4 vecteurs et j'ai ajouté le vecteur concerné à la coordonnée du joueur (x+vecteur[direction]) et ça marche très bien :D Prochaine étape : les dommages entre les joueurs. Je ne sais pas trop comment procéder mais j'imagine que je trouverais réponse au près des listener de la leçon 19 avec quelques modifications.

    Merci merci :3

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.