Slick2d, leçon 14 :: Déplacement Analogique

16 November 2014 08:58 Slick2d - Leçons 10-19 Java, Jeux, Joystick, Slick2d, Tutorial

Dans la leçon 12 nous avons vu comment utiliser une manette XBox360 pour se déplacer. Mais on était rester sur un déplacement à la croix et une émulation de la croix sur le joystick. Mais si ce que l'on veux c'est pouvoir se déplacer dans toutes les directions ce n'est pas suffisant.

Préparation

Un joystick analogique permet de lire son déplacement en suivant 2 coordonnée, X et Y. C'est deux coordonnée donne un vecteur, ce vecteur est la direction du mouvement que l'on souhaite donné. On représente généralement ce vecteur par deux variables dx et dy. Ce vecteur multiplié par le temps écoulé donnera le déplacement du joueur.

tuto-slick2d-076-analogique

Dans la leçon 12 nous avons utilisé une implémentation du listener ControllerListener pour traiter les touche de notre manette. Malheureusement, cela n'est pas possible de garder le même modèle pour un traitement analogique. En effet il n'y a pas d’évènement à proprement parler quand on déplace le joystick. Il faut en effet lire régulièrement l'état du joystick à la main.

Code :: Les variables dx et dy

Comme je l'ai dis en introduction nous n'allons plus représenter la direction d'un joueur pour 4 direction mais par un vecteur dx et dy. Cependant nous aurons toujours besoin de connaitre la direction approximative de 0 à 3 pour l'affichage du sprite par exemple. De fait c'est les variables dx et dy qui font fois et direction sera mise à jour du déplacement, cette direction ne servira que pour l'affichage. Le booléen moving n'est plus utile et peu être supprimé.

Classe :: Player
// supprimer la variable moving 
private float dx = 0, dy = 0;
private int direction = 2;

public void update(int delta) {
  if (this.isMoving()) {
    // ajouter la mise de la direction ici
    updateDirection();
    // suite de la mise à jour cf <a title="Slick2d, leçon 10 :: Séparation en objet" href="http://www.shionn.org/slick2d-lecon-10-separation-en-objet">leçon 10
  }
}
private void updateDirection() { 
  if (dx > 0 && dx >= Math.abs(dy)) { 
    direction = 3;
  } else if (dx < 0 && -dx >= Math.abs(dy)) {
    direction = 1;
  } else if (dy < 0) { 
    direction = 0;
  } else { 
    direction = 2;
  } 
} 

public void setDirection(int direction) { 
  this.direction = direction;
  switch (direction) {
  // ajouter la mise à jour de dx et dy en fonction de la direction
  case 0: dx =  0; dy = -1; break;
  case 1: dx = -1; dy =  0; break;
  case 2: dx =  0; dy =  1; break;
  case 3: dx =  1; dy =  0; break; 
  default: dx = 0; dy =  0; break;
  } 
}

// remplacer l'utilisation de la variable moving par un appel à cette méthode
public boolean isMoving() {
  return dx != 0 || dy != 0;
}

// supprimer la méthode setMoving. Supprimer tous les appels à setMoving(true) et remplacer les appels à setMoving(false) par cette méthode. 
public void stopMoving() {
  dx = 0; dy = 0;
}

Ajouter également des setters setDx() et setDy() qui seront utile pour la suite. Et normalement à ce stade le code compile et marche comme avant.

Bon c'est bien joli, mais les variables dx et dy ne sont pas utilisé pour le calcul du mouvement, de fait si on ne change pas nos méthodes de mouvement cela n'apporte rien. Pour y remédier il faut revoir les méthodes getFuturX et getFuturY vu dans les leçons 5 et 6.

Classe :: Player
private float getFuturX(int delta) {
  return this.x + .1f * delta * this.dx;
}

private float getFuturY(int delta) {
  float futurY = this.y + .15 * delta * this.dy;
  if (this.onStair) {
    futurY = futurY - .1f * delta * this.dx;
  }
  return futurY;
}

Code :: Déplacement en diagonale

On va enfin pouvoir permettre un déplacement en diagonal, il était temps ! Pour cela aux lieux de changer la direction à l’appui des directions nous allons modifier directement les valeurs dx et dy.

Classe :: PlayerController
@Override
public void keyPressed(int key, char c) {
  switch (key) {
  case Input.KEY_UP:    player.setDy(-1); break;
  case Input.KEY_LEFT:  player.setDx(-1); break;
  case Input.KEY_DOWN:  player.setDy(1);  break;
  case Input.KEY_RIGHT: player.setDx(1);  break;
  }
}

@Override
public void keyReleased(int key, char c) {
  switch (key) {
  case Input.KEY_UP:
  case Input.KEY_DOWN:  player.setDy(0); break;
  case Input.KEY_LEFT:
  case Input.KEY_RIGHT: player.setDx(0); break;
  }
}

Maintenant on peu se déplacer en diagonale mais uniquement avec le clavier. Passons à l'analogique maintenant.

Code :: Déplacement  analogique

Qu'entendons nous par analogique ? Il s'agit d'un déplacement dont la vitesse est promotionnel au mouvement du joystick. Comme je l'ai dis en introduction, dans la leçon 12 nous avons utilisé une implémentation de l'interface ControllerListener, nous n'en avons plus besoin (enfin pour l'instant) nous pouvons supprimer le code associé :

public class PlayerController implements KeyListener {
  private Player player;
  // Supprimer les méthodes qui sont devenue superflue.

}
Comme je l'ai déjà dis, il n'y a pas d’évènement pour la gestion du joystick, il faut lire régulièrement l'état du joystick et mettre à jour le vecteur de déplacement du joueur. Pour cela nous avons besoin d'un objet de type Input dans notre contrôleur, c'est à cela que sert la méthode setInput.
Classe :: PlayerController
public void update() {
  if (input.getControllerCount() > 0) {
    player.setDx(input.getAxisValue(0, 1));
    player.setDy(input.getAxisValue(0, 2));
  }
}

@Override
public void setInput(Input input) {
  this.input = input;
}

Il nous reste plus qu'à intégrer ce nouveau code à la classe principal et tous devrait marcher.

Classe :: AnalogGame
@Override
public void init(GameContainer container) throws SlickException {
  // initialisation des autres composants (leçons précédentes)
  this.controller.setInput(container.getInput());
  container.getInput().addKeyListener(this.controller);
}

@Override
public void update(GameContainer container, int delta) throws SlickException {
  this.controller.update();
  // mise à jour des autres composants (leçons précédentes)
}

tuto-slick2d-077-analogique-sc  

Pour aller plus loin

Tous n'est pas parfait, on ne peu pas mélanger les commandes clavier et joystick mais les base de la technique sont ici. Je vous laisse vous faire votre propre idée et faire comme vous le sentez.

On peu corriger facilement le tremblement du personnage quand le joystick est au centre. Et mélanger les deux modes de contrôle n'est pas très difficile :]

A ce stade les méthodes setDirection et getDirection de la classe Player sont devenut inutiles et peuvent être supprimé. La variable direction ne servant plus qu'à conserver la direction du personnage quand il s’arrête.

Ressources

par Shionn, dernière modification le 09 April 2017 17:05
10 réflexions au sujet de « Slick2d, leçon 14 :: Déplacement Analogique »
  • Anon 21 November 2014 10:26

    Je poste juste un petit commentaire pour te féliciter et te remercier pour ce tuto.

    Très bon travail... C'est simple et clair. En le suivant, on apprend de très bonnes bases afin de pouvoir travailler sur un projet de plus grande ampleur. J'ai eu beaucoup de mal à trouver quelque chose qui répondait à mes attentes en matière de développement de jeux vidéos en java.

    J'ai longuement hésité à me remettre au C++, mais étant un jeune développeur java, je pense que c'est mieux de continuer à me faire la main sur ce langage étant donné qu'une grande partie des postes de développement d'applications de gestion sont pour des profils de développeurs java.

    Un grand merci :)

  • TheTormentor 27 March 2015 21:39

    Coucou me revoilou, encore un petit soucis quand j'éxécute ce code:

      if (input.getControllerCount() > 0) {
        player.setDx(input.getAxisValue(0, 1));
        player.setDy(input.getAxisValue(0, 2));
      }
    

    j'obtient un beau java.lang.ArrayIndexOutOfBoundsException;

    Et il s'avère après de la recherche que 1! je n'ai pas d'axe en 1 (ni en 2) mais qu'en plus sans brancher de manettes le programme détect déjà 3 controller X.X

  • TheTormentor 27 March 2015 21:45

    Quelques essais plus tard si je fais

    (input.getControllerCount() > 3) {
      player.setDx(input.getAxisValue(2, 1));
      player.setDy(input.getAxisValue(2, 0));
    }
    

    Cela fonctionne... J'avoue que j'aimerais bien comprendre...

  • Shionn 28 March 2015 17:43

    La je peu pas t'aider cela viens de la configuration de ton pc, ou de ton système d'exploitation, qui apparemment à des contrôleurs virtuel ou je ne sais quoi. Genre un volant ? un truc de vibration ? un écran tactile ? un trackpad ?

  • TheTormentor 28 March 2015 17:52

    Rien de tout sa, c'est un Asus n76v, pas d'écran tactile, a part une souris sans fil j'ai rien de brancher en USB, rien en bluetooth... Je vois vraiment pas ce que sa peut etre... Mais c'est problématique si je veut partager mon jeu :/ Y'a t'il un moyen de ne détecter que le controller de type manette?

  • TheTormentor 28 March 2015 18:16

    Tiens une erreur d'exportation m'a donner les 4 controller totalement par hasard:

    • Souris USB
    • Micr
    • Mannette Windows
    • Micr
  • TheTormentor 28 March 2015 18:23

    Voici la ligne en question:

    Loading: net.java.games.input.DirectAndRawInputEnvironmentPlugin
    Sat Mar 28 18:19:50 CET 2015 INFO:Found 4 controllers
    Sat Mar 28 18:19:50 CET 2015 INFO:0 : Souris USB Microsoft Hardware
    Sat Mar 28 18:19:50 CET 2015 INFO:1 : Micr
    Sat Mar 28 18:19:50 CET 2015 INFO:2 : Controller (XBOX 360 For Windows)
    Sat Mar 28 18:19:50 CET 2015 INFO:3 : Micr
    
  • Shionn 28 March 2015 20:50

    Idéalement il faut passé par un fichier de configuration ou un menu de configuration.

    Sinon tu peu essayer de détecter la manette idéale, à l'aide des méthodes :

    • input.getControllerCount
    • input.getAxisCount
    • input.getAxisName

    Je me rend compte que c'est important, je vais faire une mise à jour de cet article prochainement.

  • TheTormentor 28 March 2015 21:40

    Merci, du coup me suis débrouiller comme sa :

    public void setInput(Input input) {
      // TODO Auto-generated method stub
      this.input = input;
      for (int i = 0; i < input.getControllerCount(); i++){
        for(int j = 0; j  3) {
          if (Math.abs(input.getAxisValue(manette, axeX))> 0.2f || Math.abs(input.getAxisValue(manette, axeY)) > 0.2f){
            sorcier.setDx(input.getAxisValue(manette, axeX));
            sorcier.setDy(input.getAxisValue(manette, axeY));
          } else {
            sorcier.setDx(0);
            sorcier.setDy(0);
          }
        }
      }
    
  • Shionn 28 March 2015 22:28

    C'est à peut près l'idée. Je vais essayer de mettre à jour l'article le plus vite possible.

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.