Slick2d, leçon 15 :: Les phases de jeux

14 December 2014 08:10 Slick2d - Leçons 10-19 Java, Jeux, Slick2d, Tutorial

Aujourd'hui je vais vous présenter comment faire une succession d'écran avec Slick2D. On m'a contacter plusieurs fois en me demandant comment ouvrir une fenêtre Slick2D à partir d'une autre fenêtre. Et bien ne cherchez pas ce n'est simplement pas possible. Je tiens à dire que je voulais faire ce tuto en tous dernier, mais les multiples mails que j'ai reçu me l'on fait faire plus tôt.

Préparation

Que voulons nous faire ? Afficher un écran d'introduction qui permettent de lancer le jeu à l'appui d'une touche, n'importe laquelle pour faire simple ! Pour faire cet écran principal, j'ai cherché et trouvé une joli image sur OpenGameArt.

tuto-slick2d-079-fond

Théorie

Comme nous l'avons vu dans le tous premier Tutorial, un jeu est une boucle infini entre l'affichage et la mise à jour. Pour obtenir notre succession il suffit de faire deux boucle de jeu et de passer de l'un à l'autre.

tuto-slick2d-080-boucle-de-jeu

Avec Slick2D il est très simple de faire une succession d'écran. Depuis le premier tutorial notre jeux est une boucle de type BasicGame. Pour aujourd'hui notre jeux héritera de StateBasedGame, cette classe permet de passer d'une boucle à une autre. Chaque boucle est une classe héritant de BasicGameState. Il nous faut donc 3 nouvelles classe :

  • StateGame héritant de StateBasedGame, qui sera la nouvelle classe principale pour lancer le jeu.
  • MainScreenGameState héritant de BasicGameState, qui permettra l'affichage de l’écran principal et qui lancera le jeu.
  • MapGameState héritant de BasicGameState, qui sera notre jeux tel que nous l'avons crée jusqu'à maintenant, c'est à dire l'affichage de la carte et se déplacer.

Code :: StateBasedGame.

Commençons par créer une nouvelle classe StateGame qui hérite de StateBasedGame. C'est une classe très simple, la partie important est la méthode initStatesList qui permettent de définir la liste des boucles d'affichage dont nous avons besoin.

Classe :: StateGamepublic class StateGame extends StateBasedGame {

  public static void main(String[] args) throws SlickException {
    new AppGameContainer(new StateGame(), 800, 600, false).start();
  }

  public StateGame() {
    super("Lesson 15 :: StateGame");
  }
 
  /**
   * Ici il suffit d'ajouter nos deux boucles de jeux. 
   * La première ajoutèe sera celle qui sera utilisée au début
   */
  @Override
  public void initStatesList(GameContainer container) throws SlickException {
    addState(new MainScreenGameState());
    addState(new MapGameState());
  }
}

Code :: MainScreenGameState

Plaçons l'image de fond dans le dossier src/main/resources/background/forest.png. Puis créons une nouvelle classe sous le nom MainScreenGameState héritant de BasicGameState et complétons la.

Chaque « state » doit avoir un identifiant qui lui est propre et unique. Cet identifiant est a renseigner dans la méthode getID(). Quand nous souhaitons passer d'un state à l'autre il faut utiliser la méthode enterState() de l'objet StateBasedGame qui est donné dans la méthode init(), c'est ce qui est fait dans la méthode keyReleased().

Classe :: MainScreenGameStatepublic class MainScreenGameState extends BasicGameState {

  public static final int ID = 1;
  private Image background;
  private StateBasedGame game;

  @Override
  public void init(GameContainer container, StateBasedGame game) throws SlickException {
    this.game = game;
    this.background = new Image("background/forest.png");
  }

  /**
   * Contenons nous d'afficher l'image de fond. 
   * Le text est placé approximativement au centre.
   */
  @Override
  public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
    background.draw(0, 0, container.getWidth(), container.getHeight());
    g.drawString("Appuyer sur une touche", 300, 300);
  }

  /**
  * Passer à l’écran de jeu à l'appui de n'importe quel touche.
  */
  @Override
  public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
  }

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

  /**
   * L'identifiant permet d'identifier les différentes boucles.
   * Pour passer de l'une à l'autre.
   */
  @Override
  public int getID() {
    return ID;
  }
}

Classe :: MapGameState

Il ne reste plus qu’à faire la boucle de jeu principal qui contient le jeux. Il suffit d'adapter la classe de la précédente leçon mais d'hériter de la classe BasicGameState à la place de BasicGame. Les méthodes sont très proche de l'habituelle classe BasicGame, il faut également un méthode getID() qui permet à Slick d’identifier les boucles.

Classe :: MapGameStatepublic class MapGameState extends BasicGameState {
  public static final int ID = 2;
  // déclaration des autres objets (cf leçon précédente)

  @Override
  public void init(GameContainer container, StateBasedGame game) throws SlickException {
    // initialisation des objets (cf leçon précédente)
  }

  @Override
  public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
    // affichage (cf leçon précédente)
  }

  @Override
  public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
    // mise à jour (cf leçon précédente)
  }

  @Override
  public void keyReleased(int key, char c) {
    // quitter le jeux sur [ESC] (cf <a title="Rest sur Tomcat, Leçon 1 : Introduction" href="http://www.shionn.org/rest-over-tomcat-lesson-1">leçon 1)
  }

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

Et normalement le jeu se lance. Sur l’écran principal en appuyant sur une touche vous retrouvez votre jeu habituel.

menu menu

Pour aller plus loin

Pour bien faire il faudrait faire un vrais menu avec police et bouton. Mais comme toujours, cela est une autre histoire.

Ressource

par Shionn, dernière modification le 09 April 2017 17:18
16 réflexions au sujet de « Slick2d, leçon 15 :: Les phases de jeux »
  • Xavier Saliniere 19 February 2015 16:24

    Bonjour,

    Je voudrais avoir,

    Comment véritablement quitter une phase de jeux ?

    Car j'ai remarquer que lorsque l'on passe de phase en phase, celle-ci sont dans un état de pause et reprennent à l'endroit où on les a laissé quand on y retourne.

    Je voudrais donc pouvoir quitter une phase de jeux et que si l'on y retourne, on repasse par la méthode init.

    Voilà et merci beaucoup pour les tutos !

  • Shionn 19 February 2015 22:35

    Bonjour,

    Merci à toi d'avoir posé la question dont je m’étonne ne pas avoir eu plus tot.

    Petite précision sur les méthodes init(). Elle ne sont pas appelée quand on rentre dans un state mais au lancement du jeux ! C'est pour cette raison que bien que je lance la musique dans la méthode init du MapGameState, on l'entende dans le MainScreenGameSate !

    Ce que cela signifie qu'il ne faut surtout pas chercher à rappeler cette méthode ultérieurement. Pourquoi ? parce que les ressources (image par exemple) qui ont été préalablement chargé, ne seront pas déchargé lors du rechargement de celle ci. Enfin elle seront déchargé de la mémoire Java, mais pas de la mémoire graphique (les images sont des textures en OpenGL).

    Bon et donc comment faire ? C'est un point que je vais aborder dans la leçon 18. Si tu ne souhait pas attendre, regarde la méthode "enterState", j'aurai du mal à expliquer plus en détail dans un commentaire.

  • Xavier Saliniere 20 February 2015 08:19

    D'accord, merci de la réponse.

    Ah oui je viens de m'en rendre compte en mettant des system.out.println un peu partout.

    Et bien je pense que je vais attendre vu que j'ai beau parcourir la documentation de slick2D de long en large je ne vois toujours pas comment je peux faire de manière propre ( càd sans réinitialiser à la main).

  • dotista 22 February 2015 22:37

    C'est exactement la même interrogation qui m'a torturé l'esprit. J'ai fini par pondre une solution, en créant un système de State fait maison. Je n'utilise alors qu'un BasicGame qui s'occupe de gérer le passage d'infos entre mes States-bis, dont leur réinitialisation.

    Mais ce que tu cherches Xavier, je crois que cela ne peut être résoluble qu'en réinitialisant toutes tes variables.

  • Shionn 23 February 2015 10:17

    Le système de State de Slick est complet et ne manque d'absolument rien pour répondre à ces problématiques. Les réponses viendront bientôt dans les derniers tutoriels.

    Ce que je peu dire pour l'instant sans écrire un tuto dans les commentaire :

    • les méthode enter() / exit() des states sont très utiles.
    • regarder les frameworks "entité" lié au jeux vidèo
  • ninokiri 03 August 2015 13:49

    Merci pour tes réponses. Je voudrai savoir comment faire une barre de progression (genre chargement). Ensuite, tu pourrai m'expliquer comment mettre mon jeu sur pause? (je pense que ça a avoir avec les threads mais je m'embrouille un peu lors du codage...) merci et continue car ce que tu fais m'aide énormément :)

  • Shionn 16 August 2015 07:39

    La barre de chargement n'est pas évidente contente toi d'un écran fix qui indique que le jeux charge c'est plus simple.

    Pour la pause c'est simple :

    • définit un booléen "pause" qui passe de true à false à chaque fois qu'on appuie sur une touche.
    • lors de l'affichage t'affiche la pause si le booléen est à true sinon t'affiche le jeux
    • lors de l'update t’arrête tous les updates si le booléen est à true.

    C'est une bonne idée de petit tuto je le note pour la reprise en septembre

    PS : désoler d'avoir pris du temps à te répondre.

  • ninokiri 16 September 2015 17:35

    Merci encore pour tes réponses. Si j'ai réussi à résoudre mes précédents problèmes, je bloque sur le systeme de combat. Le mien n'est pas comme dans tes tutos, il s'agit plutôt d'un systeme de case pour les déplacements lors des combats, avec l'aide d'un curseur (comme ceux dans les séries shining force ou final fantasy tactics sur gba). et c'est là que ça coince. comment faire pour limiter les déplacements de chaque perso en fonction de certain paramètres (vitesse, equipement...) ? comment afficher les cases de déplacement lors des combats? merci d'avance pour ta réponse. :)

  • Shionn 18 September 2015 09:44

    Avant tous cela dépend de ta représentation de donnée (ta carte de combat). Quel sont tes règle de déplacement ?

    Je pense que le plus simple des carrés rouge transparent après avoir afficher ta carte. Il faux partir de la position de ton personnage puis faire un arbe de parcourt pour savoir quel case est atteignable. avec un simple algo récursif multiple c'est assez facile à faire.

  • Impa 18 November 2016 18:51

    Salut Shionn! Je me demandais :

    Premièrement, est il possible d'afficher en temps réel les coordonnées du personnages ? Je voudrais intéragir avec des NPCs J'ai déjà dans le MapState

    boolean npcOne = false;
    render {
      if( npcOne == true ) {
        g.drawString("Je suis un NPC", 400, 400);
      }
    
    update{
      if( (player.getX() > 300 && player.getX() 300 & player.getY() < 350) ) {
        if(input.isKeyPressed(input.KEY_E)){
          npcOne = true;
        }
      }
      if (npcOne == true) {
        if (input.isKeyDown(input.KEY_E)) {
          npcOne = false;
        }
      }
    }
    

    Pour les coordonnées X et Y, je suis obligé de faire pleins de tests et c'est un peu pénible

    Aussi, je me demandais comment gérer différents NPCs sur différentes cartes ? Les coordonnées X et Y sont issues de la première carte mais comment faire si on veut créer un NPC sur une autre carte ?

    Merci par avance pour ta réponse

  • Impa 18 November 2016 18:53

    Edit : j'ai mal copié le update

    if((player.getX() > 300 && player.getX()  300 & player.getY() < 350)){
      if(input.isKeyPressed(input.KEY_E)){
        npcOne = true;
      }
    
  • Shionn 20 November 2016 08:55

    Salut Impa, tu t'attaques à un point qui demande beaucoup d'algorythme et très peu de technique de jeux finalement. Pour afficher tes coordonnées il suffit de faire quelque chose comme cela, mais avant la transformation de la caméra :

    g.drawString("coordonnée : " + 0player.getX() + ":" + player.getY(), 10, 10);
    

    Pour traiter plusieurs Npcs, il te faut une classe NPC, et les mettres les instances dans une liste. Mais je t'inviterai à calculer la distance qui te sépare d'un NPC plutôt que te tester un rectangle (cf tes cours de trigonométrie du lycée).

    Je n'aiderai pas plus que cela pour des NPCs, pour l'instant. Puisque j'en parlerai dans un tuto, tutos qui reprendrons quand j'aurais achevé un projet en cours, un moteur de blog en Spring-web-mvc...

  • blackAllSun 27 June 2017 14:33

    Salut Shionn !

    on fait comment pour afficher les monstres sur la carte ? là on sait même pas combien yen a et ou ils sont sur la carte en fait ! Est-ce qu'on crée une classe dans le même genre que MapPlayer et on l'instancie dans MapGameState avec une initialisation :

    MapEnnemy ennemy = new MapEnnemy(map); ... MapEnnemyController controller = new MapEnnemyController(player);

    avec les init() update() render() comme player ? j'ai tenté mais pas convaincue du résultat

    nb: kado pour toi Shionn un programme d'affichage de carte CRC pour ton tuto ! mis à part la sauvegarde et la reflection il marche

    Biz blackAllSun

  • Shionn 29 June 2017 11:07

    Bonjour Black All Sun

    Actuellement le système de combat que j'ai fait est aléatoire, c'est pour cela qu'il n’apparaisse pas sur la carte, un peu a la manière d'un final fantasy. Si tu veux un système prédéfinit, il faut alors prédéfinir les monstres que tu veux dans une list ou un tableau et les afficher de la même manière que tu affiche le joueur.

    Je note cela comme idée pour un prochain tuto.

  • blackAllSun 01 July 2017 11:52

    merci d'avance :D je t'avourais que je me sers surtout de ce tuto pour capter la philosophie mvc ps : regarde dans mon github si t'as 5 min ce ton tuto en carte CRC pour la gestion de projet Biz blackAllSun

  • Shionn 04 July 2017 08:06

    Salut BlackAllSun.

    Honnêtement mon tuto n'est pas un exemple de model MVC.

    Je viens de jeter un œil à ton github, c'est un genre de launcher pour tous les tuto c'est ca ? Je voulais le faire mais sous forme de JavaWebStart pour les rendre dispo depuis le site. Et puis cela s'est jamais fait :p

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.