Slick2d, leçon 4 :: Une Camera

26 janvier 2014 09:00 Slick2d - Leçons 0-9 Java, Jeux, Slick2d, Tutorial

Suite de ma série de tutoriels sur Slick2D. Dans l'article précédent nous avons traiter le déplacement d'un personnage sur une carte. Mais celui-ci sortais de l'image car l'affichage ne suivais pas le personnage. Aujourd'hui on va y remédier en ajoutant une camera.

Théorie

A la différence de la carte il n'existe pas de fonctionnalité toute faite dans slick pour traiter une camera. Ce qui n'est pas vraiment un problème car ce n'est pas ce qu'il y a de plus complexe en 2D, ni en 3D d’ailleurs. Comme bien souvent notre instinct nous trompe. En effet une technique les plus simples n'est pas de déplacer un objet camera mais de déplacer l’ensemble des objets qu'on affiche de l'inverse des coordonnées de camera.

Pour cela nous allons utiliser des méthodes de transformation. Au nombre de trois, ces méthodes s'appliquent à tous ce qu'on affiche après. Ainsi si on fait une translation de ( 50, 30 ) et qu'on affiche un objet à ( 20 , 30 ) cela serait revenu au même que d'afficher l'objet à ( 70 , 60 ). Nous ne verrons pas les autres méthode de transformation dans cet article.

Code :: Caméra suiveuse

Dans ce premier exemple nous allons faire une caméra qui place le personnage pile poil au centre l'écran ou qu'il soit. En premier essai je vous propose ce code stupide que l'on va placer en début de la méthode render(), c'est à dire avant l'affichage de la carte et du personnage. Souvenez-vous, x et y sont les coordonnées du personnage. Essayons de voir se qu'il se passe si donne à la camera les même coordonné que le personnage :

@Override
public void render(GameContainer container, Graphics g) throws SlickException {
    g.translate(-this.x, -this.y);
    // [...]
}

Le résultat n'est pas convenable, en effet le personnage est affiché dans le supérieur gauche de l’écran.

tuto-slick2d-032-trouver-willy

Il faut prendre en compte la taille de l'affichage pour que le personnage soit centré. Le code devient ainsi :

@Override
public void render(GameContainer container, Graphics g) throws SlickException {
    g.translate(container.getWidth() / 2 - this.x, 
            container.getHeight() / 2 - this.y);
    // [...]
}

C'est déjà mieux, mais en vous déplaçant vous risquez d'avoir quelques artefacts à l'affichage. C'est du à l'approximation des flottants. je vous conseil de toujours systématiquement caster en entier les flottants avant de les passer à une méthode d'affichage. Cela vaut également pour l'affichage du personnage sinon vous aller avoir l'impression qu'il tremble quand il marche.

tuto-slick2d-033-camera-centretuto-slick2d-034-artefact

@Override
public void render(GameContainer container, Graphics g) throws SlickException {
    g.translate(container.getWidth() / 2 - (int) this.x, 
            container.getHeight() / 2 - (int) this.y);
    [...]
}

Bon c'est pas mal mais quand le personnage arrive prêt du bord de la carte on vois le vide... C'est pas top. Il suffit pour cela de donner des limites à notre algo en fonction de la taille de la carte mais je passe cela sous silence, vous trouverez tous seul comment faire :].

Code :: Caméra suiveuse flottante

Généralement dans les jeux la camera n'est pas pile poil sur le personnage mais le suit avec un certain flottement, c'est à dire tant que le personnage est dans un cadre au centre de l’écran on ne met pas à jour la caméra. Nous allons cette fois définir notre caméra par deux variables « xCamera, yCamera » représentant le point que regarde la camera, c'est à dire le point au centre de l'écran. Nous pouvons déjà adapter la méthode d'affichage en fonction et assigner les coordonnées du personnage à chaque mise à jour. Et normalement vous avez exactement le même comportement que précédemment.

private float x = 300, y = 300;
private float xCamera = x, yCamera = y;
// [...]
@Override
public void render(GameContainer container, Graphics g) throws SlickException {
    g.translate(container.getWidth() / 2 - (int) this.xCamera, 
            container.getHeight() / 2 - (int) this.yCamera);
    // [...]
}
// [...]
@Override
public void update(GameContainer container, int delta) throws SlickException {
    // [...]
    // bien faire la mise à jour des coordonnées du personnage avant
    this.xCamera = this.x;
    this.yCamera = this.y;
}

Je souhaiterai que la camera ne se déplace que quand le personnage sort d'une zone que je définirais comme étant la moitié de l'affichage. C'est à dire un rectangle de 400x300 pour un affichage de 800x600. C'est assez simple il suffit de calculer des bornes à chaque mise à jour et changer de position à ce moment la.

@Override
public void update(GameContainer container, int delta) throws SlickException {
    // [...]
    int w = container.getWidth() / 4;
    if (this.x > this.xCamera + w) this.xCamera = this.x - w;
    if (this.x < this.xCamera - w) this.xCamera = this.x + w;
    int h = container.getHeight() / 4;
    if (this.y > this.yCamera + h) this.yCamera = this.y - h;
    if (this.y < this.yCamera - h) this.yCamera = this.y + h;
}

tuto-slick2d-035-camera-suiveuse

Pour aller plus loin

On peu tout imaginer comme type de camera. On peu lui donner une vitesse et une accélération qui lui serait propre. Faire une camera dans l'esprit de The Legend of Zelda ou lui permettre de faire des rotations... etc. mais c'est une autre histoire. La prochaine fois nous verrons les collisions si je n'ai pas d'autre idée.

Ressource.

par Shionn, dernière modification le 09 avril 2017 08:58
21 réflexions au sujet de « Slick2d, leçon 4 :: Une Camera »
  • Antonin 26 janvier 2014 12:40

    Salut et merci pour tes tutos sur Slick ! J'attend la suite avec impatience !

  • Shionn 26 janvier 2014 16:10

    Merci.

    Auriez-vous une préférence pour le prochain article ? Collision ? Musique ? Interface ? Ou autre chose ?

  • Antonin 26 janvier 2014 17:10

    Je suis en train de travailler sur la collision, et bien que le coté algorithmique ne me gène pas du tout, je ne parviens pas à récupérer la propriété bloquante d'une tile, votre aide à ce propos me serait donc d'une grande aide !

  • Shionn 26 janvier 2014 17:28

    Trés bien cela sera l'article de dimanche prochain. C'est très simpliste. Si vous ne pouvez pas attendre vous avez déjà un début de réponse dans la présentation que j'ai fait à [HumanTalk[(/ht-geneve-slick2d).

  • Cliuura 14 mars 2015 14:50

    Bonjour,

    Je site " Bon c’est pas mal mais quand le personnage arrive prêt du bord de la carte on vois le vide… C’est pas top. Il suffit pour cela de donner des limites à notre algo en fonction de la taille de la carte mais je passe cela sous silence, vous trouverez tous seul comment faire :]. "

    Si l'on à toujours pas trouvé ? Peut tu nous éclairer sur ce point ?

    Je te remercie d'avance :)

  • Shionn 14 mars 2015 21:43

    Je peu essayé, le but n'ai pas de vous donner du copier/coller car cela ne sert à rien.

    Il faut ajouter des minimal et maximal au coordonnée de votre caméra. Les minimales étant 0 et les maximal, étant la taille de la carte soustraite de la résolution de l'écran.

  • Cliuura 15 mars 2015 00:13

    Exactement, le copier/coller ne sert pas on est d'accord, pour les minimales et maximales c'est ce que je pensais aussi, je vais regarder ça de plus près.

    Merci :)

  • Shionn 15 mars 2015 09:03

    Apres si tu trouve pas je peu soit te donner d'autre indice, soit t'aider pour des point de calcul que tu ne trouverai pas, soit de donner l'algo, n'hésite pas à demander :]

  • Cliuura 15 mars 2015 13:38

    Bonjour,

    Mon problème en fait est de savoir ou rentrer l'algo, je pensait le rentrer dans la méthode render, la ou l'on à déja " g.translate(container.getWidth() / 2 - (int) this.xCamera, container.getHeight() / 2 - (int) this.yCamera); "

    Mais je ne suis pas sur..

  • Shionn 15 mars 2015 15:26

    Je pense qu'il est mieux de le faire dans la méthode update. Tu peu avoir la taille de l'écran avec l'objet container, comme on je vois dans la méthode d'affichage. Et dans l'objet map tu peu avoir la taille de la carte ;)

  • Cliuura 15 mars 2015 16:27

    Je pensais rajouter quelque chose du genre

    int w = container.getWidth() / 4;
    if (this.x > this.xCamera + w) this.xCamera = this.x - w;
    if (this.x  ...);
    if (this.x  this.yCamera + h) this.yCamera = this.y - h;
    if (this.y  ...);
    if (this.y < ...);
    

  • Cliuura 15 mars 2015 16:29

    Désolé dans ce message, l'affichage ne c'est pas fait correctement..

    Je voulais donc ajouter quelque chose du genre if (this.x > ... ); if (this < ... );

  • Shionn 15 mars 2015 17:24

    Non tu n'y est pas, c'est beaucoup plus simple que ca ^^. les coordonnée du joueur ne sont plus nécessaire pour cet ajout.

    Pour XCamera, le minimum c'est containeur.getWidth()/2. t'es ok avec cela ? Et le maximum c'est taille de la carte - container.getWidth()/2.

    Tu vois ce que je veux dire ?

  • Cliuura 15 mars 2015 17:50

    Je pense avoir compris pour le minimum, pour le maximum un peu moins, mais je vais regarder tout cela en essayant différentes choses :D

  • Cliuura 16 mars 2015 18:43

    Bonjour, je vois à peu près ce que tu me dis par rapport au minimum et au maximum, mais je comprend pas vraiment ou rentrer cet algo, je dois le mettre en if(this...) dans l'update ?

  • Shionn 16 mars 2015 19:25

    Cette partie sert à calculer les coordonnées idéales. :

    int w = container.getWidth() / 4;
    if (this.x > this.xCamera + w) this.xCamera = this.x - w;
    if (this.x < this.xCamera - w) this.xCamera = this.x + w;
    int h = container.getHeight() / 4;
    if (this.y > this.yCamera + h) this.yCamera = this.y - h;
    if (this.y < this.yCamera - h) this.yCamera = this.y + h;
    

    Après cette partie tu ajoutes ton algo pour ajouter une contrainte au coordonnée de la caméra.

  • Cliuura 18 mars 2015 20:45

    Bonsoir, toujours bloqué :P

    Après certains essais j'ai finalement réussi... à bloquer la caméra sur le coté gauche, mais ... le personnage est lui aussi bloqué sur la partie gauche et on ne le vois qu'a moitié.. enfin bref, voila mon pitable avancement :P

    Bonne soirée Shion.

  • Shionn 19 mars 2015 13:31

    @Cliurra :

    int min = container.getWidth() / 2;
    int max = this.map.getWidth() * this.map.getTileWidth() -  container.getWidth() / 2;
    if (this.xCamera < min) {
      this.xCamera = min;
    } else if (this.xCamera > max) {
      this.xCamera = max;
    }
    min = container.getHeight() / 2;
    max = this.map.getHeight() * this.map.getTileHeight() - container.getHeight() / 2;
    if (this.yCamera < min) {
      this.yCamera = min;
    } else if (this.yCamera > max) {
      this.yCamera = max;
    }
    

  • Cliuura 19 mars 2015 18:31

    Je te remercie pour ce code, maintenant je cherche a le comprendre comme il le faut, en gros :

    if (this.xCamera < min) {
    		      this.xCamera = min;
    

    c'est cette partie qui stop la caméra sur le coté gauche des x en disant que si la caméra dépassait la taille du container /2, cela stoperais la caméra ?

    et de même pour les autres axes ?

  • Shionn 19 mars 2015 19:26

    C'est exactement ça. Et pourquoi c'est divisé par deux ? Parce la coordonne de la caméra est au centre de l'affichage et pas dans le coin supérieur gauche.

  • Cliuura 19 mars 2015 20:39

    Je te remercie pour tes explications Shionn :)

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.