Introduction

Cette page contient les informations supplémentaires sur les cours “Programmation Concurrentielle des Interfaces Interactives” enseignée en L3 Informatique à l’université Paris-Saclay en 2014/2015.

Horaires

Le mardi à 14h00, salles E201 et E202.

Equipe enseignante

Résponsable du cours – Frédéric Vernier Chargés du TP – Oleksandr Zinenko, Arnaud Prouzeau

Travail pratique

Commentaire de l’énoncé.

Le travail consite à réaliser un jeu très simple en utilisant le framework graphique Swing du langage Java. Ce jeu correspond à un modèle physique derrière le très médiatisé Flappy Bird. Cette tâche ne nécessite pas de parallèlisme, elle est proposée pour vous familiariser avec la programmation des interfaces graphiques.

Précision de travail à faire

Il est nécessaire de réaliser:

  • la mise à jour de l’écran pricipal;
  • la réaction aux touches appuyées;
  • l’écran de la fin du jeu avec le score;
  • la rélance de jeu après la fin.

Generation de la ligne continue

Utilisation du bruit de Perlin.

Résultats

Le code doit être envoyé par mail à l’un des chargés de TP avant la fin de la session. Tout code envoyé après 16h00 le 10 Février ne sera pris en compte.

Liens utiles

Projet

Documents

Sujet bref

Développer une version du jeu PacMan en utilisant les Threads et les Timers ainsi que les classes de dessin 2D.

Ecran principal du jeu

Règles du jeu

  • Terrain de jeu : un labyrinthe rempli de billes.
  • Joueur représenté par PacMan (jaune).
  • Adversaires automatiques représentés par les fantômes.
  • But de jeu : “manger” toutes les billes sans se faire manger par les fantômes.
  • La bille “magique” permet l’inversion temporaire des rôles de sorte que le PacMan puisse “manger” les fantômes.

Particularités de réalisation

  • Le labyrinthe est stocké dans un fichier texte (voir l’énoncé pour le format).
  • Le jeu doit avoir plusieurs niveaux.
  • Un éditeur graphique des labyrinthes fait partie du projet comme une application dédiée.

Organisation du projet

  • Lecture des fichiers et affichage du labyrinthe.
  • Animation et déplacement du PacMan et des fantômes.
  • Possibilité d’arrêter le jeu et l’écran de score.
  • Editeur des niveaux.

Aspects du parallélisme

  • La mise à jour se fait dans un Thread dédié.
  • Le même Thread gère l’état du jeu (actif, pause, numéro de niveau etc.) avec une machine à état.
  • Le temps avant la réapparition du fantôme et le temps d’inversement des rôles sont calculés par Timer.
  • Le chargement de niveau se fait avec un feedback visuel ralenti par Thread.sleep.

Foire aux questions

Est-ce que je peux utiliser le code trouvé sur Internet ?

J’ai trouvé plusieurs version du jeu sur Internet, puis-je en utiliser certains parties ou m’inspirer?

Non.

Bien que pour les projets de programmation réels on utilise souvent les differentes bibliothèques trouvés ci et là, n’utilisez directement le code trouvé sur Internet. D’un côté, il est souvent écrit par d’autres étudiants et peût contenir des nombreux bugs ou être mal écrit (contrairement aux bibliothèques génériques telles que log4j). De l’autre côté, vous n’allez presque rien apprendre en procedant de telle façon.

Pourquoi l’interfance ne s’affiche qu’après un resize ?

J’essaie de dessiner un objet sur JFrame, mais rien ne s’affiche avant que je change la taille d’une fenêtre sur l’écran.

En AWT, la bibliothèque standard des interfaces Java, toute interaction avec les composantes de l’interface visible se fait dans un thread dédié, Event Dispatch Thread ou EDT. Les fonctionnalités d’affichage fournies par le système d’exploitation ne sont pas toujours conçues pour être utilisées dans un programme à plusieurs Threads. Pour gérer cette situation sans conflits, AWT est obligé de garantir que l’accès aux cettes fonctionnalités est limité à un Thread unique, l’EDT. La réaction aux événements tel que l’appui sur une touche ou le déplacement du souris est exécutée dans ce Thread puisque ces événements viennent du système d’exploitation. L’affichange des Copmonents est aussi géré par l’EDT qui alterne la boucle de dessin avec celle de propagation des événements. L’appel de la fonction JFrame.repaint() à l’intérieur de main ne force pas la mise à jour de la fenêtre, mais demande à l’EDT de le faire dans une prochaine boucle de dessin (qui peut avoir lieu à n’importe quel moment). Quand la taille de la fenêtre change, un événement est généré par le système d’exploitation et la réaction par defaut est de redessiner l’interface en appellant JFrame.paint(Graphics). Par conséquent, le code

public class Foo extends JFrame {
	@Override
	public void paint(Graphics g) {
		g.drawRect(0, 0, 100, 100);
	}
	public static void main() {
		JFrame frame = new Foo();
		frame.setSize(300, 300);
		frame.setVisible(true);
		frame.repaint();
	}
}

ne dessine rien. Le rajout d’un ContentPane à un Frame permet de redessiner le contenu de ce ContentPane chaque fois quand le Frame devient visible ou déplace sur l’écran.

public class Foo extends JFrame {
	@Override
	public void paint(Graphics g) {
		g.drawRect(0, 0, 100, 100);
	}
	public static void main() {
		JFrame frame = new JFrame();
		frame.setContentPane(new Foo());
		frame.setSize(300, 300);
		frame.setVisible(true);
	}
}

Pour plus d’information voir la FAQ française ou la documentation officielle angalise.

Comment dessiner un fantôme ?

Je ne sais pas dessiner, peux-je utiliser les sprites de PacMan trouvé sur Internet ?

Non.

Votre note ne depend pas de la qualité du dessin. En fait la version originelle du PacMan est si vielle que tous les éléments étaient dessinés dans les cases de 16x16 pixels. D’ailleurs il est très facile de dessiner un fantôme en utilisant des primitifs disponibles dans la classe Graphics, c’est à dire des circles et des rectanglges. Il suffit de superposer quelques primitifs comme dans l’image ci-dessous. Plusieurs couleurs semi-transparentes sont utilisées à gauche pour montrer les differentes parties de l’image.

Etapes du dessin

Le JPanel n’a pas la bonne taille

J’ai crée un panel, mais il n’a pas la taille demandée

Si vous utilisez un gestionner de mise en page (layout manager) ou l’ajustement automatique de la taille de fênetre via la fonction pack(), vous ne devez pas appeler la fonction setSize(Dimension) dans votre code. En effet, cette fonction est appelée par le manager avec les paramètres differents de ceux que vous lui passez. Par contre, le manager tient compte de la taille dite préférée du panel qu’on peut modifier en appelant la fonction setPreferredSize(Dimension).

NullPointerException dans les fonctions de dessin

J’ai sauvegardé un Graphics, mais quand je l’utilise, j’ai un NullPointerException.

L’exemplaire de la classe Graphics contient un contexte instantané de dessin. Ce contexte est créé par le système d’exploitation chaque fois pour l’appel de paint(). Il n’existe pas en dehors de cette fonction et ne doit pas être sauvgardé. Si vous voulez dessiner une partie de l’interface sans repeinture immédiate, faites-le sur un BufferredImage et utilisez-le dans le paint ; dès que repaint() est appelé, l’interface utilisera la nouvelle image.