Slick2d, leçon 4 :: Une Camera

26 January 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 April 2017 08:58
26 réflexions au sujet de « Slick2d, leçon 4 :: Une Camera »
  • Antonin 26 January 2014 12:40

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

  • Shionn 26 January 2014 16:10

    Merci.

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

  • Antonin 26 January 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 January 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 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 March 2015 20:39

    Je te remercie pour tes explications Shionn :)

  • DanielKumok 01 July 2017 16:07

    Je préviens, j'ai compris l'entièreté du code que j'utilise et je me déçois de devoir demander mais ce qui s'avérait si simple ne fonctionne toujours pas après plusieurs milliards d'essais. J'ai modifié le code car arrivé au bord de la map, la caméra revenait bien dans la map sauf en mouvement, donc je voyais le vide si j'avançais vers lui. Alors, j'ai essayé de dire "Si les caméras ne sont ni aux maximums ni aux minimums (sous-entendus pas hors de la carte) alors activer la caméra"
    if(this.xCamera != max && this.yCamera != max && this.xCamera != min && this.yCamera != min ){ \Caméra flottante etc.

    Ça n'a pas fonctionné alors j'ai tenté avec un while, non plus. J'ai essayé beaucoup de choses mais j'ai l'impression d'être dans le mauvais chemin ou alors j'ai définitivement pas compris quelque(s) choses). (Qu'est-ce que je ne mérite pas tes tutos). (PS: Le codeblock de commonmark m'empêche de poster :/)

  • Shionn 04 July 2017 08:18

    Salut,

    Je ne comprend pas très bien ce que tu cherches à faire si c'est limité la caméra au bord de la carte regarde mon commentaire du 19 mars 2015 pour Cliurra. Mais si tu n'as pas compris cette partie passe à la suite et ne reste pas sur ce point qui est anecdotique. Si tu souhaites faire cela tu dois te dire

    Si la camera est au delà des limites je la ra-mène à ces limites.

    Qu'est ce que tu entend par : ?

    le codeblock de commonmark m'empêche de poster

    Commark mark est simplement un outil pour formater du texte, par exemple mettre quelque chose entre double * le mettra en gras. Il ne bloque rien normalement. Si tu veux mettre du code il faut faire comme suit :

    ~~~
    // mon code ici
    ~~~
    

    Si tu parle des liens externe c'est moi qui est été obliger des les bloquer, à cause des bots, cependant il semblerai que ma dernière parade marche encore mieux donc je vais probablement lever cette restriction.

    P.S. : Je viens de voir que je me suis planté dans le lien common mark, cela sera bientôt corriger.

  • DanielKumok 04 July 2017 11:42

    [Le bug commonmark est négligeable]

    Mais en fait, quand la caméra passe les limites, je la ramène aux limites mais la caméra flottante (active en mouvement donc) me permet de voir au-delà des bords. Puis quand je redeviens immobile, la caméra reviens aux limites, comme prévu. Je ne sais pas être plus clair désolé >_<

  • Shionn 04 July 2017 16:18

    En gros si je comprend bien les limites de la camera marche bien quand t'es immobile mais pas que tu déplaces le perso. C'est donc que tu n'as pas mis ton algo sur les limites de la camera au bon endroit. Tu dois t'y prendre comme suit :

    @Override
    public void update(GameContainer container, int delta) throws SlickException {
      // 1 : déplacement du personnage
      // 2 : calcule de la position idéale de la camera
      // 3 : recalculer la position en prenant en compte les limites
    }
    

    Si tu n'y arrive toujours pas je te laisse le plaisir de m'envoyer ton code par mail.

  • DanielKumok 04 July 2017 17:11

    Je pense que je dois vraiment prêter plus d'attention à l'ordre de mes instructions, je suis idiot. Merci mil fois !!

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.