Vous êtes sur la page 1sur 68

BTS Services informatiques aux organisations – 1re année

PROGRAMMATION OBJET
COURS

BTS SIO – 1re année – Spécialité SLAM

Élisabeth Martins Da Silva

PROGRAMMATION
OBJET
TRAVAUX PRATIQUES
Retrouvez la liste de nos formations sur www.cned.fr
Pour plus d’informations, appelez le 05 49 49 94 94
Du lundi au vendredi, 8 h 30-18 h.
Coût d’une communication ordinaire.

*82950TPPA0013*
CONNECTÉ À VOTRE AVENIR

Les cours du CNED sont strictement réservés à l’usage privé de leurs destinataires et ne sont pas destinés à une utilisation collective.
Les personnes qui s’en serviraient pour d’autres usages, qui en feraient une reproduction intégrale ou partielle, une traduction sans
le consentement du CNED, s’exposeraient à des poursuites judiciaires et aux sanctions pénales prévues par le Code de la propriété
intellectuelle. Les reproductions par reprographie de livres et de périodiques protégés contenues dans cet ouvrage sont effectuées
par le CNED avec l’autorisation du Centre français d’exploitation du droit de copie (20, rue des Grands Augustins, 75006 Paris).

© CNED 2013
Conseils généraux

Importance des TP
Le but principal des TP, quel que soit le module de cours, est de vous faire manipuler des
techniques afin que vous compreniez leur fonctionnement et que vous puissiez acqué-
rir une certaine expérience. Ce passage à la phase concrète est indispensable à votre
apprentissage.

Organisation du fascicule
Ce fascicule ne contient qu’un seul TP qui permet, étape par étape, d’aborder différents
aspects de la programmation objet à travers l’élaboration d’un jeu 2D en client/serveur.
Vous allez mettre en pratique la création d’une application en suivant le modèle MVC.
Vous allez découvrir les techniques de transfert d’objets pour une application en client/
serveur. Vous allez bien sûr aussi mettre en pratique toutes les connaissances objets :
classes abstraites, polymorphisme, interface, collections, etc. Cette mise en pratique
vous permettra de mieux comprendre toutes ces notions. De plus, vous allez découvrir
un nouveau langage : Java. Après le C/C++, c’est le langage actuellement le plus utilisé.
Vous trouverez en fin de fascicule un petit mémento du langage Java, pour avoir les
correspondances avec l’algorithmique.
La réalisation complète de ce TP est indispensable dans votre apprentissage. Vous pour-
rez ainsi contrôler que les notions de programmation objet sont vraiment acquises.

Logiciels nécessaires
Conseils généraux
Pour réaliser ce TP, vous allez travailler avec JDK et Eclipse Ganimède. Tous les outils
utilisés dans ce TP sont gratuits et facilement récupérables sur internet. Vous trouverez Page 3
dès le début du TP, dans la partie « les outils nécessaires », toutes les explications pour
télécharger et installer les différents outils.
Vous aurez aussi besoin de certains fichiers, par exemple les images des personnages.
Ces fichiers sont récupérables directement sur le site du cned, au même endroit que ce
fascicule, avec la correction. Là encore, lorsque vous en aurez besoin, vous aurez toutes
les explications nécessaires.

N’oubliez pas que le but est de prendre plaisir à développer : ce TP est volontairement
dans le domaine du jeu pour que vous appreniez avec plaisir des notions qui sont parfois
d’ailleurs assez complexes.

Bon courage et surtout bon développement !

8 2950 TP PA 00
TP
Urban Marginal
Ce TP est l’occasion de mettre en pratique les connaissances acquises dans
le cours, à travers la création d’un jeu de combat 2D en client/serveur, donc
plusieurs joueurs peuvent jouer à distance. Le langage choisi est Java.

X Capacités attendues en fin de TP


Avoir vu l’intérêt de la programmation objet à travers une application concrète.
Savoir développer une application en client/serveur et en suivant le modèle
MVC.
Avoir assimilé les bases du langage Java.

X Contenu
1. Les outils nécessaires ....................................................................................... 6
2. À quoi va-t-on jouer ? ...................................................................................... 8
3. Les classes et le modèle MVC .......................................................................... 9
4. Création du projet .......................................................................................... 10
5. La première fenêtre ........................................................................................ 11 Travaux pratiques

6. Partie Client/Serveur ...................................................................................... 18 Urban Marginal


7. La construction du jeu .................................................................................... 23
8. Le choix du joueur .......................................................................................... 26 Page 5

9. L’arène du jeu ................................................................................................. 34


10. Le t’chat ........................................................................................................... 48
11. Les déplacements ........................................................................................... 51
12. Les combats..................................................................................................... 53
13. Le son .............................................................................................................. 61
14. Le déploiement ............................................................................................... 64
15. Mini mémento Java ........................................................................................ 66

8 2950 TP PA 00
1. Les outils nécessaires
Java est un langage gratuit, développé par Sun. Il suffit normalement d’aller sur le site
de Sun et de télécharger la dernière version de JDK (Java Development Kit). Après son
installation, le code d’une application Java peut-être écrit dans un simple éditeur, avec
l’extension " java ", puis pré-compilé pour obtenir un fichier avec l’extension " class ".
La machine virtuelle du jdk permet alors d’exécuter le fichier class. L’avantage du fichier
class est sa portabilité : il est exécutable sur n’importe quelle plate-forme.
Pour faciliter le développement, il existe des logiciels qui offrent un environnement
d’aide à la création d’applications. Vous allez utiliser des outils gratuits et les plus connus
dans ce domaine.
Voici les différents outils que vous devez récupérer sur Internet et installer sur votre
ordinateur avant de commencer le TP. Les versions mentionnées sont celles qui ont été
utilisées au moment de la création du TP.

JDK
Intérêt : Java Development Kit contient entre autres le précompilateur et la machine
virtuelle Java qui vont permettre de créer les fichiers class et les exécuter.
Lieu de téléchargement : http://www.java.com/fr/download/manual.jsp (sur le site de sun :
http://fr.sun.com/). Prenez plutôt le téléchargement hors ligne.
Version utilisée : 6u12 (jdk-6u12-windows-i586-p.exe)
Installation : exécutez le fichier téléchargé et suivez les étapes.
Travaux pratiques
Eclipse + Visual Editor
Urban Marginal Intérêt : Eclipse est un environnement de développement qui apporte une aide à la
création d’applications. C’est actuellement l’environnement gratuit le plus utilisé, pour
Page 6 Java. Visual Editor apporte des plugins supplémentaires à Eclipse, permettant l’aide à la
création assistée d’interfaces graphiques : le code est automatiquement généré. Il existe
plusieurs logiciels comme Visual Editor, mais celui-ci est le plus connu et génère un code
propre et facile à comprendre. Avec le lien unique suivant, vous allez pouvoir télécharger
les 2 logiciels.
Lieu de téléchargement : http://wiki.eclipse.org/VE/Installing
Versions utilisées : Eclipse 3.4 Ganymede (« Eclipse IDE for Java EE Developers »), VE 1.3
(ve_eclipse_34_200807092330_win.zip)
Installation : suivez les explications du site (Eclipse ne s’installe pas : il suffit de décom-
presser le fichier. Pour Visual Editor, dezippez le fichier dans le dossier dropins d’Eclipse).

Français !
Intérêt : pour ceux qui ont du mal avec l’anglais, il est possible d’avoir Eclipse en français.
Configuration : il existe le projet Babel de traduction, qui se configure directement dans
Eclipse. Lancez Eclipse (eclipse.exe). Au premier lancement, il faut préciser le workspace
(dossier dans lequel les projets seront installés) : pensez à créer un dossier spécifique et à
le préciser ici. Une fois Eclipse démarré, allez dans Menu « Help », « Software Update »,
onglet « Available Software », cliquez sur « add site ». Tapez l’adresse suivante (http://
build.eclipse.org/technology/babel/test-updates/ganymede) puis ok.
Normalement le lien s’ajoute dans la liste et, quelques secondes plus tard, une mise à
jour se fait, proposant la liste de toutes les traductions possibles. Ne sélectionnez que le

8 2950 TP PA 00
lot français (Babel Language Packs in French) puis cliquez sur Install. Suivez les étapes
et redémarrez Eclipse (cela va de toute façon vous être proposé). Normalement Eclipse
est maintenant en français (à 80%, donc pas d’inquiétude si vous voyez encore un peu
d’anglais).

À quoi ressemble Eclipse ?


Maintenant que tout est installé, voyons rapidement à quoi ressemble l’environnement
Eclipse. Au démarrage, vous tombez sur l’écran de Bienvenue : cliquez sur le rond conte-
nant une flèche (qui mentionne, si vous passez la souris dessus, " Plan de travail "). Vous
obtenez alors l’écran suivant, mais bien sûr encore vide de code et de classes :
Onglets des fichiers
Arborescence des ouverts (les classes)
packages et des classes

Zone de code, colorisée,


avec aide à la saisie
(indentation automatique,
affichage de la liste des
membres…)

Travaux pratiques

Urban Marginal

Page 7
Popup d’aide qui
apparaît en passant la
souris sur certains mots
Zone d’informations et
d’affichage (propriétés,
console Java…)

Repérez le menu Refactoring (Reconcevoir) qui doit vous servir à chaque fois
O que vous voulez apporter des modifications à votre code. Il faudra aussi avoir
ce réflexe lors des oraux à l’examen pour montrer au jury vos connaissances
de l’outil. Pour savoir bien l’utiliser, relisez dans le cours la première partie de
la séquence 5 « Notions évoluées » qui parle du Refactoring.
Sous Eclipse, un projet sera créé, qui se traduira par la création d’un dossier sur le disque.
Ce projet contiendra (on le voit à gauche de la fenêtre) les différents packages de l’appli-
cation (eux aussi seront représentés par des dossiers dans le projet). Chaque package
contiendra d’autres packages et/ou classes (qui sont des fichiers avec l’extension java).
Sur le disque, le dossier du projet possède, entre autres, 2 dossiers : src (qui contiendra les
packages et classes sources de l’application) et bin (qui contiendra la même arborescence
que src, mais cette fois les classes sont précompilées, donc des fichiers avec l’extension
class).

8 2950 TP PA 00
2. À quoi va-t-on jouer ?
Avant tout, on va jouer à coder... Ce code va permettre de mettre au point une applica-
tion unique, qui pourra soit démarrer un serveur, à l’écoute de clients, soit démarrer un
client qui se connecte à un serveur. Voici les différentes fenêtres et étapes du jeu.

Fenêtre de démarrage du jeu


Elle permet de choisir d’être serveur ou client. Dans
le cas d’un client, il doit d’abord préciser l’adresse
IP du serveur pour se connecter. Après ce choix, si
le serveur est lancé, il se met à l’écoute de clients et
ouvre la fenêtre d’Arène qui permettra de visualiser,
côté serveur, tout ce qui se passe, sans pour autant
pouvoir agir. Ce sont les clients qui vont agir. Cette
première fenêtre est volontairement simpliste.

Pour un client, fenêtre de choix


du personnage
Si un client a été lancé, il s’est alors connecté
à un serveur et, si la connexion s’est bien pas-
sée, alors la fenêtre du choix du personnage
apparaît. Elle permet au joueur de saisir son
pseudo et de choisir un personnage en utili-
Travaux pratiques sant les flèches. Une fois le personnage choisi,
il suffit de cliquer sur GO. Cette fois, la fenêtre
Urban Marginal a plus un aspect « jeu » au niveau graphique.

Page 8 Côté client et serveur, fenêtre


de l’arène
C’est la zone du jeu. Côté
serveur, elle permet juste de
visualiser ce qui se passe et
ne permet aucune action.
On pourrait même éviter de
l’afficher, cependant cela
reste un bon moyen de tests.
Côté client, elle permet de
jouer (déplacer le person-
nage, tirer des boules sur
d’autres joueurs pour leur
faire perdre de la vie et en
gagner soi-même) et de dis-
cuter (avec la zone de discus-
sion qui se trouve en dessous
de l’arène). Lorsqu’un joueur
se déplace, il prend diffé-
rents « états » de marche
et se tourne dans la direc-
tion où il avance. Lorsqu’il

8 2950 TP PA 00
est touché par une boule, il réagit. Enfin, lorsqu’il meurt, il tombe et reste à la même
position dans l’arène. Des murs, posés aléatoirement empêchent les déplacements et
arrêtent les trajectoires des boules.

3. Les classes et le modèle MVC


Java étant un langage « tout objet », l’application va être entièrement construite autour
d’un ensemble de classes. Celles-ci vont être organisées en 3 grands packages afin d’es-
sayer de suivre un peu la logique du modèle MVC (Modèle Vue Contrôleur).

Qu’est-ce que le MVC ?


C’est un modèle préconisant une organisation des classes en trois groupes distincts :
• modèle : va contenir les classes métiers de l’application (Jeu, Joueur, Boule…) ;
• vue : va contenir les classes de visualisation (les fenêtres) ;
• contrôleur : va contenir les classes qui vont inter agir entre la vue et le modèle.
L’intérêt d’une telle architecture est avant tout l’organisation du code, mais aussi l’indé-
pendance entre le modèle et la vue. Toute modification sur la vue n’aura aucune inci-
dence sur le modèle, et vice versa. C’est le contrôleur qui a un rôle d’interface entre les
deux et qui va s’occuper d’interpréter les ordres des uns et des autres. Par exemple, si la
vue vient de saisir une information, elle avertit le contrôleur de cette saisie, qui se charge
de solliciter le modèle pour enregistrer l’information saisie.
Travaux pratiques
Organisation des classes de l’application
Le diagramme de classes (page suivante) présente, pour chaque package, l’organisation Urban Marginal
des classes. Le package « Outils » qui vient s’ajouter aux autres, contient des classes
« standards », que vous allez écrire mais qui seront très largement réutilisables pour Page 9
d’autres applications. Les propriétés et méthodes n’ont pas été représentées afin d’allé-
ger le diagramme pour une meilleure compréhension.
Voici quelques explications pour mieux comprendre ce diagramme.
Package controleur : il contient l’interface Global qui sera implémentée par quasiment
toutes les classes de l’application. Cette interface apporte toutes les constantes globales.
Il contient aussi la classe Controle. L’application démarre sur cette classe qui s’occupe
d’instancier les 3 fenêtres et la classe Jeu du package modele. La classe Controle va
s’occuper de faire le lien entre la vue et le modele.
Package vue : il contient les trois fenêtres de l’application.
Package modele : il contient les classes métiers de l’application. On y trouve la classe
Jeu avec 2 classes filles (pour distinguer le jeu côté serveur ou côté client), la classe Objet
pour représenter les objets qui seront manipulés (Joueur, Mur, Boule), la classe Label
pour mémoriser et numéroter un JLabel (un JLabel est l’objet graphique qui servira
pour l’affichage et qui sera transmis entre le serveur et le client via le réseau) et la classe
Attaque (qui permet de gérer le tir d’une boule).
Package outils : il contient deux packages qui seront réutilisables pour d’autres appli-
cations (la package connexion pour gérer l’aspect client/serveur et le package son pour
jouer du son !).

8 2950 TP PA 00
Voici le diagramme de classes simplifié :

Entrée de l'application

Travaux pratiques

Urban Marginal

Page 10

4. Création du projet
Il est temps de passer à la création du projet. Normalement, vous avez déjà créé un dos-
sier pour l’espace de travail Eclipse. Votre projet sera mémorisé dans ce dossier.

Création d’un nouveau projet


Fichier/Nouveau/Projet… Pour le type de projet, choisissez Java/Projet Java et cliquez sur
Suivant. Donnez comme nom de projet " Urban Marginal " (sans les guillemets) et cli-
quez sur Terminer. S’il vous est proposé d’ouvrir maintenant la " perspective java ", dites
non (c’est une aide qui pour le moment ne nous intéresse pas).

Création des packages


Remarquez à gauche, dans l’Explorateur de projets, le dossier " Urban Marginal ".
Cliquez sur la croix à gauche. Le dossier contient " src " et " Bibliothèque… ". Essayez
d’ouvrir " src ". Il est vide. Normal : la partie " sources " (donc le code) de votre projet
n’est pas commencée. On va commencer par créer les 4 packages : faites un clic droit
sur src, Nouveau/Package. Donnez le nom " controleur " puis Terminer. Le package s’est
ajouté à gauche. Toujours en faisant clic droit sur src, créez les 3 autres packages.

8 2950 TP PA 00
Création des classes
Pour créer les classes, il suffira de faire un clic droit sur la package où doit être placée la
classe et faire Nouveau puis choisir la bonne option (on verra plus loin comment créer
une simple classe, une classe de type fenêtre ou une interface).

Sauvegardes
Comme d’habitude, il est fortement conseillé de sauvegarder régulièrement votre tra-
vail. Un simple ctrl-S permet de sauver la classe active (ou Fichier/Enregistrer). Si vous
voulez tout sauver en une fois, Fichier/Enregistrer tout. Cependant, le ctrl-S doit devenir
une habitude toutes les 2 ou 3 lignes de code ! Vous repèrerez une classe dont les der-
nières modifications n’ont pas été sauvées, par une petite étoile à côté de son nom, dans
les onglets.

5. La première fenêtre
Vous allez apprendre à créer la toute première fenêtre du jeu ainsi que les classes de base
nécessaires au démarrage (la classe EntreeJeu de la fenêtre, le Controleur mais aussi les
classes du Jeu).

La classe EntreeJeu
La première classe que vous allez créer est une classe de visualisation, donc une fenêtre.
Cette classe doit, pour cela, hériter de la classe existante Frame qui gère les objets gra-
Travaux pratiques
phiques et les événements sur ces objets. On appelle ces classes particulières, des frames.
D’ailleurs, dans la suite du dossier, je n’utiliserai plus le terme « fenêtre » mais le terme Urban Marginal
« frame ». Voici la procédure pour créer une classe frame (procédure qui ne sera pas
rappelée lors de la création des autres frames du jeu) : clic droit sur la package vue, Page 11
Nouveau/Autre… Sélectionnez Java/Visual Class (si vous ne trouvez pas Visual Class dans
Java, c’est que vous avez mal installé Visual Editor). Cliquez sur Suivant. Dans la fenêtre
qui apparaît, donnez le nom de la classe : EntreeJeu. Dans la partie Style, à gauche, sélec-
tionnez Swing/Frame (effectivement, Frame fait parti du package swing de java, offrant
plusieurs outils visuels). Cliquez sur Terminer. Dans la partie Explorateur de projets, à
gauche, la classe EntreeJeu.java s’est ajoutée. Dans la partie centrale, pour ce type de
classes, la zone se divise en deux : la partie haute représente visuellement la frame (c’est
là que l’on pourra placer directement les objets graphiques) et la partie basse contient le
code. D’ailleurs du code s’est déjà créé automatiquement. Prenons le temps de l’analyser
et ainsi de commencer à découvrir le langage Java dont la syntaxe est similaire au C++).

8 2950 TP PA 00
Package dans lequel
se trouve la classe
package vue;
import java.awt.BorderLayout; Import des classes
import javax.swing.JPanel; nécessaires
import javax.swing.JFrame;
La classe hérite de
la classe JFrame
public class EntreeJeu extends JFrame {

private static final long serialVersionUID = 1L;


private JPanel jContentPane = null;
Propriétés privées.
serialVersionUID est un numéro de série
/** que l’on n’utilisera pas. jContentPane est le
* This is the default constructor panel principal de la frame (un panel est
*/ une zone pouvant contenir d’autres panels
Public EntreeJeu() { et/ou objets graphiques)
super(); Constructeur :
initialize(); super() fait appel au constructeur de la classe mère.
} Initialize() appelle la méthode correspondante

/**
* This method initializes this
* Méthode initialize() :
* @return void setSize permet de dimensionner la frame,
Travaux pratiques */ setContentPane attend en paramètre l’objet
private void initialize() { panel principal,
Urban Marginal
this.setSize(300, 200); setTitle donne une valeur à la barre de titre.
Page 12 this.setContentPane(getJContentPane());
this.setTitle("JFrame");
}

/**
* This method initializes jContentPane
*
* @return javax.swing.JPanel
*/ Getter sur le panel principal. Si le panel
n’est pas encore créé, il l’initialise et
private JPanel getJContentPane() {
définit son type de Layout*.
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
}
return jContentPane;
}

}
* Layout : organisation de l’affichage (l’affichage peut être géré de différentes façons :
en ligne, à partir du bord, dans un quadrillage… on choisira à chaque fois un layout null
afin de positionner les objets graphiques en fonction des pixels par rapport au coin haut
gauche).

8 2950 TP PA 00
Changement de type de layout
Clic droit directement au milieu de la frame, Set Layout/null. Remarquez le changement
qui a été fait dans le code, au niveau du setLayout.
Ajout des objets graphiques
La palette se trouve masquée à droite (cliquez sur la petite flèche " Show palette " pour
l’ouvrir). La palette contient les " Swing Components " (les objets graphiques comme les
boutons, zones de texte…), les " Swing Containers " (les conteneurs d’objets qui per-
mettent donc de regrouper plusieurs objets, comme les JPanels…), les " Swing Menus "
(pour intégrer des menus, mais on ne les utilisera pas ici) et les " AWT Controls " (les
contrôles graphiques simples, provenant du package AWT, très utilisé pour les Applets
Java, mais qui ne nous concernent pas ici).
Placer un objet graphique est très simple : il suffit de cliquer sur l’objet dans la palette,
d’ouvrir la zone nécessaire directement dans la frame pour positionner et dimensionner
l’objet. Une fenêtre va alors s’ouvrir pour demander le nom de l’objet. La frame est aussi
redimensionnable en direct. Sachant cela, dimensionnez la frame et placez les objets gra-
phiques nécessaires les 3 JLabels n’étant pas des JLabels « actifs » c’est-à-dire susceptibles
de changer, vous pouvez laisser les noms pas défaut) :

cmdServeur

cmdClient
Travaux pratiques
cmdQuitter
Urban Marginal
txtIP

Page 13
Pensez à changer le titre de la frame (clic droit, Set Title), les intitulés de chaque bouton
(clic droit, Set Text) et initialisez txtIP avec le texte " 127.0.0.1 " (clic droit, Set Text) ce
qui sera plus pratique pour les tests (comme ils seront au début en local, cela évitera de
saisir à chaque fois l’adresse).
Vous venez d’utiliser des raccourcis bien pratiques pour modifier certaines propriétés. Il
est possible d’accéder à la liste de toutes les propriétés : cliquez sur l’un des objets gra-
phiques et, en bas d’Eclipse, sélectionnez l’onglet " Propriétés ". Vous avez ainsi la liste
de toutes les propriétés de l’objet.
Vous avez certainement envie de faire un premier test d’affichage. Ca ne sera malheu-
reusement pas encore possible car pour qu’une application démarre, il faut qu’une des
classes de l’application contienne une méthode main(). Ce n’est pas le cas. Le main sera
dans la classe Controle qui va être créée juste après. Mais n’allons pas trop vite. On va
d’abord analyser le code généré. C’est indispensable pour comprendre la logique du
code d’une frame.
Génération automatique du code
Observez le code qui a été automatiquement généré. Les propriétés qui correspondent
aux objets ont été ajoutées. Dans la méthode getJContentPane(), vous pouvez voir
les caractéristiques des JLabels (setBounds pour la taille, setText pour le contenu…).
Remarquez aussi la méthode add sur jContentPane, appliquée plusieurs fois. En fait,
il faut faire un add sur un JPanel autant de fois que d’objets à insérer dans le JPanel.
Visual Editor utilise la logique suivante (qui n’est pas la seule possible) : les caractéris-

8 2950 TP PA 00
tiques des JLabels, considérés comme des objets de moindre importance car normale-
ment sans action possible, sont directement intégrées dans la méthode getContentPane.
En revanche, pour les autres objets, ils ont chacun leur propre getter qui a un double
rôle : le rôle classique de getter (retourner l’objet) mais aussi, si l’objet est null (donc
pas encore créé) alors il a le rôle de créateur (en appliquant toutes les caractéristiques
demandées). Au final, tous les objets, pour être affichés, doivent être intégrés (avec add)
dans le JPanel général (jContentPane) ou dans un autre JPanel qui sera lui-même intégré
dans le JPanel général.
Organisation du code et commentaires
Observez les indentations du code : tout a été proprement géré suivant les nouvelles
normes d’indentations (accolades ouvrantes en fin de ligne, et fermantes alignées sur le
début du bloc, décalages dans chaque bloc…).
Observez aussi la colorisation, bien pratique : en gras pour les mots réservés, en bleu
pour les propriétés… Essayez d’ajouter un commentaire (avec //), il sera en vert.
Justement, parlons des commentaires : certains ont été ajoutés automatiquement, et ont
un format prédéfini (avec entre autres l’utilisation du signe @). Ces commentaires vont
être reconnus par le logiciel pour construire automatiquement la " Java Doc " (docu-
mentation du programme). Vous connaissez déjà ce principe de commentaires dans le
module 2946 « Développement d’applications ». Vous pouvez par contre les compléter
en utilisant vos propres normes de commentaires. Voici un exemple de règles de présen-
tation. À vous de fixer vos propres règles. Attention, dans la correction que vous allez
récupérer, ce sont ces règles qui ont été respectées mais cela ne vous force en rien à les
suivre. Les commentaires liés à la javadoc ont même été supprimés, ce qui n’est d’ailleurs
Travaux pratiques
pas une bonne idée (c’est une mauvaise habitude qui me reste !). Il est même conseillé,
Urban Marginal maintenant que vous savez le faire, d’ajouter les commentaires manquants en tête de
classes et de méthodes pour pouvoir, en fin de TP, générer la javadoc.
Page 14
//--- xxx ---
//--- Propriétés : objets graphiques --- Devant les lots de propriétés
private JPanel jContentPane = null ;
// xxx
//--- Autres propriétés --- Sur la même ligne, pour apporter une précision
private Controle controle ; //accès au controle de l’application
// xxx
//--- Constructeur --- Devant une ligne ou un groupe de lignes
public EntreeJeu () { dans une méthode (lorsqu’une précision
super (); semble nécessaire pour la compréhension)
// initialisation des composants
initialize();
}

//**************************************************************
// Méthodes de création de l’interface
//**************************************************************

//************************
// xxx
//************************
Pour isoler un lot de méthodes

8 2950 TP PA 00
Quand vous allez pour écrire du code, prenez toujours l’habitude de commencer par
écrire la ligne de commentaire avant de commencer à écrire le code (c’est une très bonne
habitude et aussi une bonne aide pour réfléchir).
Ajout des événements
La classe Frame, dont hérite notre classe, permet de gérer des événements sur les objets
graphiques. Là encore, avec Visual Editor, le code peut être généré automatiquement.
Clic droit sur le bouton Demarrer / Events / Add Events. Dans la liste, ouvrir Mouse et
sélectionner mouseClicked. Vous remarquez au passage le nombre d’événements pos-
sibles qui peuvent être gérés sur un objet graphique. Il existe 2 moyens de gérer un évé-
nement : avec la classe Adapter ou l’interface Listener. Le principe est très proche. Pour
le moment on ne va pas s’y attarder et on va garder le choix proposé : MouseAdapter.
Cliquez sur Terminer.
Allez tout de suite voir le code généré qui a été ajouté dans la méthode getCmdServeur() :
cmdServeur.addMouselistener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent e) {
System.out.println("MouseClicked()");
}
});
Là prenons le temps de bien tout comprendre : c’est indispensable pour la suite.
MouseAdapter est une classe préexistante qui se trouve dans le package event (qui
contient toutes les classes d’événements), lui-même dans le package awt (qui gère tout
ce qui est affichage basic), lui-même dans le package java. Pourquoi préciser le chemin
complet pour accéder à la classe MouseAdapter ? Parce qu’on n’a pas mis d’import cor- Travaux pratiques
respondant en début de code. Supprimez " java.awt.event. " devant MouseAdapter.
Remarquez que MouseAdapter est souligné en rouge. Sans cliquer, placer la souris sur Urban Marginal
le mot. Une popup s’ouvre pour signaler l’erreur et proposer des solutions. La première
est souvent la plus adaptée (mais pas toujours !) : ici, la première solution est " Importer Page 15
MouseAdapter (java.awt.event) ". Cliquez dessus. MouseAdapter n’est plus souligné.
Allez tout en haut du code pour voir les imports. Remarquez que vous ne voyez qu’une
ligne car ils sont regroupés : pour tous les voir il suffit de cliquer sur le " + " qui est dans
la marge. Dans la liste des imports, vous remarquez java.awt.event. Vous avez compris le
principe ? Un import évite de préciser le chemin complet pour accéder à une classe qui ne
se trouve pas dans le même package que la classe actuelle. En suivant la même logique,
enlevez le java.awt.event. qui se trouve devant MouseEvent.
Dans la liste des imports, remarquez que certains sont soulignés en jaune. Les souligne-
ments en rouge sont des erreurs (donc qui empêchent l’exécution) alors que les jaunes
sont des warning. Placez la souris dessus sans cliquer. Le message précise pourquoi il y
a un problème : l’import n’est jamais utilisé. Effectivement, les 2 imports soulignés sont
nécessaires pour gérer le BorderLayout qui était défini par défaut et que l’on a enlevé.
Vous pouvez supprimer les 2 imports qui sont soulignés puisqu’ils ne servent pas.
Continuons l’explication du code de l’événement. On applique la méthode addMouse-
Listener (préexistante) qui attend en paramètre un objet de type MouseAdapter. Ici, un
raccourci d’écriture que vous ne connaissez pas encore est utilisé : en même temps qu’on
instancie la classe, on redéfinit la méthode mouseClicked. Cette méthode événementielle
reçoit en paramètre un objet de type MouseEvent qui contiendra des informations sur
l’événement. Cela ne nous sera pas très utile pour un événement de souris, mais nette-
ment plus pour un événement de clavier, car il sera possible de savoir quelle touche a été
utilisée. Dans la méthode mouseClicked, un affichage a été placé. Bien sûr il n’est là que

8 2950 TP PA 00
pour un éventuel test. Il sera remplacé par le code que l’on veut exécuter au moment
du click sur le bouton.
System.out.println permet d’afficher un message dans la console Java : cet affichage ne
se voit donc pas dans l’exécution du programme, mais uniquement dans la console qui
est une zone spécifique (que vous voyez d’ailleurs dans les onglets en bas d’Eclipse) : ce
sera bien pratique pour faire des tests dans les programmes.
Cet affichage, on va bien sûr pour le moment l’enlever, mais pour faire les choses pro-
prement, plutôt que de mettre le code de l’événement directement à cet endroit, on va
mettre un appel à une méthode que l’on écrira plus haut, juste en dessous du construc-
teur ; supprimez la ligne d’affichage et remplacez-la par :
cmdServeur_clic() ;
On pourrait mettre le nom qu’on veut : c’est juste pour donner un nom parlant à la
méthode (le nom du bouton suivi de l’événement). Placez-vous juste en dessous du
constructeur et écrivez la méthode :
//--- Clic sur le bouton Demarrer ---
private void cmdServeur_clic () {
System.out.println ("clic sur Demarrer");
}
Pourquoi en private ? Parce qu’aucune autre classe n’a de raison d’accéder à cette
méthode. Pourquoi un affichage ? C’est temporaire : cela permettra de tester que l’évé-
nement marche bien. Avez-vous remarqué qu’en tapant System suivi d’un point, la liste
des membres accessibles est apparue ? Vous vous en doutez : c’est une aide bien pratique
pour trouver des membres et aussi pour contrôler une bonne saisie.
Travaux pratiques
En suivant la même logique que pour le bouton Demarrer, créez l’événement et la
Urban Marginal méthode associée pour les 2 autres boutons. En ce qui concerne le bouton Quitter, plutôt
qu’un affichage, vous allez taper la commande qui permet d’arrêter l’application :
Page 16
System.exit(0) ;
On va s’arrêter là pour le moment, au niveau de cette classe.

La classe Controle
La classe Controle est, comme vous l’avez vu dans le diagramme de classes, celle qui
représente l’entrée dans l’application. Il est temps de la créer pour faire un premier
test. Cette classe n’est pas une frame mais une classe classique : pour la créer, faites
un clic droit sur le package controleur, puis Nouveau/Classe. Tapez le nom de la classe
(Controle) et cochez la case " public static void main " (c’est la seule classe qui contiendra
la méthode main qui permet de démarrer l’application) puis Terminer. Cette fois, toute
la zone centrale est réservée au code, et très peu de code a été généré.
La méthode main
Dans la méthode main, enlevez le commentaire et mettez juste une ligne de code qui
permet d’instancier la classe :
new Controle() ;
Vous allez ainsi créer une instance de Controle et donc lancer le constructeur.
Le constructeur
Déclarez en privé la propriété frmEntreeJeu de type EntreeJeu (donc du type de la frame
que vous venez de créer). Remarquez que EntreeJeu est souligné : effectivement cette
classe ne se trouve pas dans le même package. Insérez l’import nécessaire.

8 2950 TP PA 00
Écrivez le constructeur. Dans le constructeur, affectez à la propriété frmEntreeJeu une
instance de la classe correspondante. Ligne suivante, appliquez à cette même propriété
la méthode setVisible en mettant true en paramètre. La première ligne permet de créer
la frame mais elle n’est pas pour autant visible. La seconde ligne la rend visible.
Avant de continuer, on va faire un tout premier test. Cela va permettre de donner la
démarche d’un test. Le petit bonhomme suivant sera présent pour chaque test de l’appli-
cation.
Dans la partie droite (Explorateur de projets), cliquez sur " Urban Marginal " pour le sélec-
tionner (il faut toujours lancer le test à partir de là). Juste au dessus, cliquez sur la flèche
blanche qui se trouve dans un cercle vert. Une fenêtre s’affiche pour vous demander si votre
code est une Applet ou une application. Sélectionnez application puis ok (cette question ne
sera plus posée par la suite). Le pré-compilateur s’occupe alors de créer les fichiers class
et la machine virtuelle les exécute. Normalement, vous devriez voir apparaître la frame.

Cliquez sur le bouton Demarrer : l’onglet de la console apparaît en bas d’Eclipse et vous
devriez voir s’afficher " Clic sur Demarrer ". Cliquez sur Connecter (si vous ne voyez
plus la frame, elle est derrière Eclipse) et cette fois vous obtenez le message " clic sur
Connecter ". Cliquez sur Quitter : le frame se ferme.
Les événements provenant de la vue
La classe Controle, en dehors du main et du constructeur, va avoir 2 grands rôles : gérer
les événements provenant de la vue et gérer les événements provenant du modèle. Il
y aura donc 2 grands groupes de méthodes à gérer. Pensez à les séparer par 2 grands
commentaires.
Créez la méthode publique evenementVue qui ne retourne rien (void) et qui reçoit en
Travaux pratiques
paramètre uneframe de type Object et info de type Object. Pourquoi tous les 2 de type
Object ? Object est la superclasse mère de toutes les classes. Donc tous les objets sont par Urban Marginal
défaut de type Object. Comme on ne sait pas à l’avance le type précis des 2 paramètres,
on prend le type le plus large. Le paramètre uneframe contiendra la frame à l’origine de Page 17
l’événement (n’oublions pas que l’on aura à gérer 3 frames dans cette application). Le
paramètre info contiendra l’information à traiter.
Dans cette méthode, faites un test sur uneframe : si uneframe est un objet de type
EntreeJeu (il faut utiliser le mot réservé instanceof entre l’objet et la classe pour faire
ce contrôle), alors appelez la méthode non encore écrite evenementEntreeJeu en lui
envoyant info en paramètre.
Écrivez la méthode privée evenementEntreeJeu qui ne retourne rien et qui reçoit en
paramètre info de type Object. Cette méthode s’occupera de démarrer un jeu serveur ou
un jeu client, suivant le bouton activé par l’utilisateur.
Échanges d’informations entre les classes
La classe Controle s’occupe de créer une instance de la classe EntreeJeu. Elle aura donc
accès sans difficulté aux membres publics de cette classe en passant par cette instance.
Mais comment faire pour que la classe EntreeJeu ait accès aux membres de la classe
Controle ? Le seul moyen est, au moment de la création de l’instance de la classe
EntreeJeu, d’envoyer à son constructeur une copie de l’instance actuelle de la classe
Controle. Ainsi, la classe EntreeJeu aura une instance de la classe Controle et accèdera
à ses membres publics. Comment faire précisément ? Voici la démarche : dans la classe
Controle, dans le constructeur, au moment d’instancier la classe EntreeJeu, envoyez this
en paramètre :

frmEntreeJeu = new EntreeJeu(this) ;

8 2950 TP PA 00
Retournez dans la classe EntreeJeu. Déclarez en privé la propriété controle de type
Controle. Ajoutez l’import nécessaire. Dans la signature du constructeur, ajoutez le
paramètre controle de type Controle. Dans le code du constructeur, après super() (car
aucune ligne de code ne peut être placée avant), valorisez la propriété controle avec le
paramètre correspondant.
Rappel : ne pensez surtout pas que vous prenez beaucoup de place mémoire en faisant
ainsi des copies des objets. Les objets ne sont pas copiés mais c’est juste leur adresse qui
est ainsi transférée. Du coup, on peut se permettre de transférer facilement les objets,
sans pour autant abuser afin que l’ensemble reste cohérent.
EntreeJeu sollicite le controleur
Il est maintenant possible, de la part de la frame, de solliciter le controleur pour lui
transférer ses événements. Dans la méthode cmdServeur_clic(), supprimez l’affichage et
remplacez-le par l’appel de la méthode evenementVue sur l’objet controle, en envoyant
en premier paramètre this (pour que le controle sache de quelle frame provient la
demande) et en second paramètre la chaîne " serveur ". En effet, le controle doit
juste savoir s’il faut démarrer un jeu de type Serveur ou Client. Avec la même logique,
modifiez le code derrière le bouton cmdClient (en envoyant cette fois, comme chaîne,
l’adresse IP contenue dans txtIP que vous récupérez avec la méthode getText).
Pour vérifier si l’échange entre les classes se passe bien, ajoutez, dans la méthode evene-
mentEntreeJeu de la classe Controle, un affichage console du paramètre info. Attention,
le paramètre reçu est de type Object, et pour l’afficher, il faut le caster (transtyper) en un
String (chaîne). Si vous ne vous rappelez pas comment caster un objet, voici la méthode :
il faut mettre devant l’objet le nom de la nouvelle classe entre parenthèses. Voici donc
Travaux pratiques
la ligne de code à écrire :
Urban Marginal
System.out.println((String)info) ;
Le casting d’objets va se faire très souvent dans ce TP, donc rappelez vous de la méthode.
Page 18

Lancez un test (n’oubliez pas de sélectionner " Urban Marginal " avant de cliquer sur la
flèche). Cette fois, le test se lance directement sans poser de question. Cliquez sur le bouton
Démarrer : la console devrait afficher " serveur ". Cliquez sur le bouton Connecter : la
console devrait afficher " 127.0.0.1 ". Cliquer sur Quitter.
Si tout marche bien, vous pouvez supprimer la ligne d’affichage dans la console.
La classe Controle doit s’occuper, à partir du choix de l’utilisateur, de créer un jeu Client
ou Serveur. Mais la création de ces types de jeux va immédiatement donner lieu à une
connexion entre le client et le serveur. Donc, avant d’aller plus loin, il faut s’occuper de
créer les classes techniques, du package connexion, qui vont gérer les connexions entre
ordinateurs.

6. Partie Client/Serveur
C’est la partie la plus complexe de l’application. On est cependant obligé de quasiment
commencer par elle sinon rien d’autre ne peut être fait. Dans le package outils, créez le
package connexion (attention, lors de la création du package, vous êtes obligé de préci-
ser outils.connexion comme nom car le dossier source sera Urban Marginal/src).
Dans cette partie, vous allez découvrir plusieurs notions fondamentales :

8 2950 TP PA 00
Socket : objet de connexion contenant entre autre une adresse IP et un numéro de port
d’écoute (pour mieux comprendre : quand vous vous connectez à un site sur Internet,
vous tapez une adresse à laquelle correspond une adresse IP, et le port d’écoute par
défaut des serveurs web est 80, donc vous n’avez pas à le préciser).
Canal d’entrée et canal de sortie : créés à partir d’un socket, ces objets permettent
soit de recevoir (entrée) des informations, soit d’en envoyer (sortie) vers l’ordinateur
distant.
Thread : c’est la notion la plus importante à comprendre. Un thread est un processus
(un module indépendant) qui s’exécute en parallèle du reste. Donc, quand un thread
s’exécute, le reste du programme continue son exécution. C’est indispensable pour les
applications Client/Serveur car, pendant qu’un ordinateur (client ou serveur) attend des
informations de l’ordinateur distant, cela ne doit pas bloquer le reste de l’application.
Vous connaissez déjà cette notion sans le savoir : en effet, la gestion de l’attente des
événements (clic sur un bouton) est gérée automatiquement dans un thread. Donc un
programme qui gère le clic sur un bouton, va mettre en place un thread d’attente du clic,
qui va tourner en permanence et ce, sans bloquer pour autant le reste de l’application.
Multi threading : c’est la possibilité de gérer plusieurs threads. Ce terme est utilisé dans
le cas d’une application dont le serveur est capable de gérer plusieurs clients à la fois,
donc chaque client dans un thread différent.

Classe ServeurSocket
Quel est le but ? Cette classe doit créer un socket spécial pour un serveur, puis, à l’aide
de ce socket, doit se mettre à l’écoute des clients qui voudraient se connecter. En effet,
plusieurs joueurs pourront accéder au serveur pour entrer dans l’arène du jeu. Travaux pratiques

Dans le package connexion, créez une simple classe ServeurSocket. Urban Marginal
Constructeur
Page 19
Dans cette classe, créez le constructeur qui doit recevoir en paramètre lerecepteur de type
Object et port de type int. lerecepteur recevra l’objet qui veut solliciter ServeurSocket (ce
sera l’instance de la classe Controle), cela permettra de renvoyer des informations vers
cet objet. port recevra le numéro de port d’écoute du socket du serveur.
Pour récupérer lerecepteur, créez une propriété privée du même nom de type Object.
Dans le constructeur, valorisez la propriété lerecepteur avec le paramètre correspondant.
Un socket spécifique à un serveur doit être généré. Pour cela, déclarez en privé une pro-
priété de type ServerSocket (attention, Server sans " u " : ce n’est pas notre classe mais
une classe prédéfinie de java) et du nom de serversocket. Intégrez l’import nécessaire
(java.net.ServerSocket). Dans le constructeur, affectez à cette propriété, une instance de
la classe ServerSocket en envoyant au constructeur port en paramètre. Remarquez que
l’instanciation est soulignée en rouge. En mettant la souris dessus, le message dit " Type
d’exception IOException non géré ". Cliquez sur " entourer d’un bloc try/catch ".
Explication : à chaque fois que vous écrivez un code qui risque de provoquer une erreur
(par exemple, une tentative d’ouverture d’un fichier qui peut-être n’existe pas, ou ici,
une tentative de création d’un socket alors que le port n’est pas disponible, ou que
l’ordinateur n’a pas de carte réseau), dans ces différents cas, Java protège l’intégrité du
programme en forçant la " capture " de l’erreur. Comment marche le try/catch : le code
qui est dans le try est exécuté. En cas d’erreur, le programme ne plante pas mais c’est
le code qui est dans le catch qui est exécuté. Le catch reçoit en paramètre l’erreur qui a
été générée.

8 2950 TP PA 00
Dans le cas où le socket ne veut pas se créer, il y a donc un problème important. On va
afficher le message d’erreur dans la console et arrêter l’application : effacez le contenu
du catch, placez un affichage console en mettant en paramètre ("erreur création socket
serveur : "+e). Arrêtez l’application (System.exit(0)).
Héritage de la classe Thread
Une fois le socket du serveur créé, celui-ci doit se mettre à l’écoute des clients qui vont
se connecter. Cette écoute doit se faire dans un thread isolé pour éviter de bloquer le
reste du programme. La classe ServeurSocket entière doit être alors un thread. Pour cela,
faites-la hériter de la classe Thread en insérant « extends Thred » à la suite du nom de
la classe. Cette classe possède une méthode abstraite, run(), qui doit donc être redéfinie
dans les classes filles. C’est le contenu de cette méthode qui va être isolé (dans un thread
indépendant). Pour exécuter cette méthode, il ne faut surtout pas l’appeler directement.
Il faut appeler la méthode start() de la classe mère, qui s’occupe de mettre en place le
thread, puis qui appelle la méthode run(). Donc, à la fin du constructeur (soit après
l’accolade fermante du catch, soit à la fin du try), appelez la méthode start().
Méthode run
Écrivez la méthode publique run() qui ne retourne rien et qui ne reçoit aucun paramètre.
Le but étant de récupérer à chaque fois le socket du client qui se connecte, déclarez en
variable locale de la méthode, la variable socket de type Socket. Faites l’import néces-
saire. Cette méthode doit boucler infiniment sur l’attente de nouveaux clients. Il faut une
boucle infinie : faite une boucle (while) et mettez comme condition (true). L’ordinateur
va boucler tant que la condition est vraie, hors true est toujours vrai ! Dans la boucle,
Travaux pratiques
il faut se mettre à l’écoute d’un client : la méthode accept() sur un socket serveur s’en
occupe et retourne le socket du client qui se connecte. Faites bien la différence entre
Urban Marginal la classe ServerSocket qui permet de se mettre à l’écoute de clients, et la classe Socket
qui permet de gérer un simple socket (donc socket client). Donc appliquez la méthode
Page 20 accept() à la propriété serversocket et récupérez le retour de cet appel dans la propriété
socket.
L’appel de la méthode accept() est souligné : il est à nouveau proposé de l’entourer d’un
try/catch. Faites-le. S’il y a une erreur, c’est que le socket du serveur a un problème (par
exemple si l’objet correspondant a été détruit). Dans le catch, faites comme précédem-
ment (en affichant un message console et en fermant l’application).
Dans le try, on va gérer aussi des affichages console pour vérifier que tout se passe bien :
au dessus de l’attente du client, faites un affichage console avec le message " le serveur
attend ", et après l’attente du client, faites un affichage console avec le message " un
client s’est connecté ".
Une fois qu’un client s’est connecté, il faut lancer un autre thread, spécifique à ce client,
qui va attendre les éventuels messages envoyés par le client. Ce sera donc du multi-
threading. Mais l’attente d’un message provenant d’un client va donner lieu à un bloc de
code qui sera le même côté client, lorsque ce dernier se mettra en attente d’un message
provenant du serveur. Du coup, on va mettre cette attente dans une classe séparée que
l’on pourra réutiliser pour le client.

Classe Connection
Créez la classe Connection dans le package connexion. Faites hériter cette classe de la
classe Thread.

8 2950 TP PA 00
Constructeur
Créez le constructeur qui reçoit en paramètre socket de type Socket et lerecepteur de
type Object. socket contiendra le socket de l’ordinateur distant, lerecepteur recevra l’ob-
jet lerecepteur de la classe ServeurSocket ou ClientSocket ( instance de la classe Controle
car lerecepteur est l’objet vers lequel il faut envoyer les informations reçues).
Déclarez en privé la propriété lerecepteur de type Object et valorisez cette propriété
dans le constructeur, avec le paramètre.
À partir du socket reçu en paramètre, il faut créer les deux canaux d’entrée/sortie
pour communiquer avec l’ordinateur distant. Déclarez en privé la propriété in de
type ObjectInputStream. Cette propriété sera le canal d’entrée pour recevoir les mes-
sages provenant de l’ordinateur distant. Déclarez en privé la propriété out de type
ObjectOutputStream. Cette propriété sera le canal de sortie pour envoyer des messages
vers l’ordinateur distant. Pensez à chaque fois à insérer les bons imports.
Dans le constructeur, affectez à la propriété out, une instance de la classe
ObjectOutputStream en envoyant en paramètre, l’appel de la méthode getOut-
putStream() appliquée à l’objet socket. Cette ligne de code réclame un try/catch. Ajoutez
le. Dans le catch, on est dans le cas d’une erreur de création de canal de sortie, qui peut
provenir par exemple du socket qui a été fermé entre temps. Dans le catch, comme pré-
cédemment, affichez à la console un message adéquat et fermez l’application.
À la suite (donc après la fermeture du catch), on va créer le canal d’entrée. Affecter à
la propriété in, une instance de la classe ObjectInputStream en envoyant en paramètre,
l’appel de la méthode getInputStream() appliquée à l’objet socket. Il faut à nouveau un
try/catch : gérez le comme précédemment.
Travaux pratiques
Si tout s’est bien passé (la création des deux canaux) alors le run() du thread va pouvoir
être lancé : appelez la méthode start() de la classe mère. Urban Marginal

Méthode run Page 21


Créez la méthode run(). Il faut boucler sur l’attente des messages provenant de l’ordi-
nateur distant, mais cette fois, la boucle ne doit pas être infinie. En effet, si l’ordinateur
distant se déconnecte, alors il ne faut plus être à l’écoute. Pour mémoriser la déconnec-
tion de l’ordinateur distant, on va utiliser un booléen. Déclarez dans la méthode run()
la variable locale inOk de type booléen (boolean), et initialisez-là à true. À chaque fois
qu’un message arrive, il faudra le récupérer dans une variable. Ce message étant de type
Object, déclarez en local la variable reception de type Object.
Faites une boucle tant que inOk est vrai. Dans la boucle, affectez à la variable reception,
l’appel de la méthode readObject() sur la propriété in. Cette méthode va attendre la
réception d’un message de la part de l’ordinateur distant. Dès qu’un message arrive, son
contenu est alors transféré dans la variable reception. Un try/catch est encore réclamé.
Ajoutez-le. Mais cette fois, observez bien : il y a un try et deux catch ! Effectivement c’est
possible quand il peut y avoir plusieurs types d’erreur possibles sur une même ligne de
code. Ici, il peut y avoir une erreur d’entrée/sortie (IO) puisqu’il y a communication avec
l’extérieur, et il peut y avoir une erreur de type de classe non trouvé puisqu’on récu-
père un objet (donc si ce qu’on récupère n’est pas au moins au format Object, l’erreur
se produira). Dans le cas de l’erreur de classe, affichez un message console et stoppez
l’application. Dans le cas de l’erreur IO, c’est différent : cela veut dire que l’ordinateur
distant s’est déconnecté (il a peut-être fermé l’application). Au lieu d’afficher un mes-
sage console, on va afficher une messagebox pour que l’utilisateur puisse être informé
de la déconnexion de l’ordinateur distant. Pour cela, dans le catch correspondant, appe-

8 2950 TP PA 00
lez la méthode statique showMessageDialog de la classe JOptionPane et envoyez en
premier paramètre null et en second paramètre le message « l’ordinateur distant s’est
déconnecté ».
Si l’ordinateur distant s’est déconnecté, il ne faut surtout pas continuer à attendre des
messages de sa part, donc mettez à false la variable inOk et, pour faire les choses propre-
ment, on va fermer le canal d’entrée : appliquez la méthode close() sur la variable in (ces
2 lignes de code doivent être mises dans le catch IO, après l’affichage de la messagebox).
Le close réclame un try/catch : ajoutez-le. Dans le catch, la fermeture du canal s’est mal
passée : faites un affichage console mais ne fermez pas l’application car ça ne sera pas
bloquant pour la suite.
La classe n’est pas tout à fait terminée, mais suffisante pour pouvoir continuer. On y
reviendra plus tard.
Revenez rapidement dans la classe ServeurSocket. Dans la méthode run(), après l’affi-
chage « un client s’est connecté », c’est à cet endroit qu’il faut créer le thread indé-
pendant pour ce client, afin d’attendre ses messages. Pour cela, instanciez la classe
Connection en envoyant socket en premier paramètre (c’est le socket du client qui vient
d’être récupéré) et lerecepteur en second paramètre (on passera par lerecepteur pour
traiter les messages reçus).

Classe ClientSocket
La classe ClientSocket est nettement plus simple. En effet, le ServeurSocket s’occupe de
boucler sur l’attente des clients, et pour chaque client qui se connecte, s’occupe de créer
un thread indépendant pour boucler sur l’attente des messages de ce client. La classe
Travaux pratiques ClientSocket va juste tenter de se connecter à un seul serveur, puis lancer un unique
thread indépendant pour attendre les messages du serveur.
Urban Marginal
Dans le package connexion, créez la classe ClientSocket.
Page 22 Constructeur
Écrivez le constructeur qui reçoit trois paramètres : ip de type String (qui contiendra l’ip
du serveur auquel il faut se connecter), port de type int (qui contiendra le numéro du
port d’écoute du serveur) et lerecepteur de type Object (qui contiendra une instance de
la classe Controle vers laquelle il faudra transférer les informations reçues du serveur).
Dans le constructeur, déclarez une variable socket de type Socket : cet objet recevra le
socket client créé lors de la tentative de connexion au serveur. Puis, affectez à cet objet
socket, une instance de la classe Socket en envoyant en paramètre ip et port (car pour
qu’un socket client se crée, il faut lui donner l’adresse du serveur et son port d’écoute).
Un try/catch est demandé : ajoutez-le. Deux catch apparaissent : soit le serveur n’est pas
disponible (UnknownHost), soit il y a eu un problème d’entrée/sortie (IP incorrecte, par
exemple). Dans les deux cas, plutôt qu’un message console, faites un messagebox pour
que l’utilisateur sache pourquoi il y a eu un problème de connexion. Ne fermez pas
l’application car on veut laisser la chance à l’utilisateur de retenter une connexion. Il faut
tout de même se rappeler qu’il y a eu un problème. Pour cela on va utiliser un booléen.
Déclarez en propriété privée connexionOK de type booléen. Au début du constructeur,
initialisez-le à false. Dans le try, juste après la création du socket, donc si tout s’est bien
passé, faites un affichage console pour dire que la connexion au serveur a réussi, et pas-
sez à true la propriété connexionOK. Enfin, faites un getter sur cette propriété.
Une fois que le socket est correctement créé (donc à la fin du try), il faut lancer le thread
indépendant pour attendre les messages provenant du serveur : instanciez la classe
Connection en lui envoyant en paramètre socket et lerecepteur.
La classe ClientSocket est totalement terminée.

8 2950 TP PA 00
Utilisation des 3 classes de connexion
Retournez dans la classe Controle, et remplissez la méthode actuellement vide evene-
mentEntreeJeu avec un test : si info reçu en paramètre, après l’avoir casté en String,
contient " serveur ", alors instanciez la classe ServeurSocket en lui envoyant comme
premier paramètre this (l’instance actuelle de Controle) et comme second paramètre un
numéro de port (on va utiliser au hasard 6666 qui de toute évidence est libre). Faites
l’import nécessaire.

Lancez un test (n’oubliez pas de sélectionner « Urban Marginal » avant de cliquer sur la
flèche). Cliquez sur le bouton Démarrer : dans la console, vous devriez voir apparaître le
message « Le serveur attend ». C’est tout ! Mais c’est déjà beaucoup ! Cliquez sur Quitter.

Mettez un sinon à votre test précédent : si vous êtes dans le sinon, c’est que l’utilisa-
teur à cliqué sur Connecter. Dans le sinon, faites un nouveau test, car effectivement il
faut contrôler, lorsque le client se connecte, que la connexion s’est bien passée. Donc
dans le test, instanciez la classe ClientSocket en lui envoyant en paramètre info (casté
en String) qui contient l’adresse IP du serveur, 6666 pour le port, et this pour l’instance
de la classe Controle. Mettez toute cette instanciation entre parenthèse, mettez juste
après un point et ajoutez l’appel du getter sur connexionOK que vous avez écrit dans
la classe ClientSocket. Par cette ligne, vous remarquez que l’on peut très bien instancier
une classe (donc créer un objet) et en même temps lui appliquer une méthode. Pour le
moment ne mettez rien dans la partie alors du test. On le remplira plus tard.
Lancez un test. Cliquez sur Demarrer. Remarquez à nouveau que le message « Le serveur
attend » s’affiche dans la console. Tout en laissant la première frame ouverte, lancez une
deuxième fois l’application : une nouvelle frame s’ouvre. Cette fois cliquez sur Connecter. Travaux pratiques
Dans la console, vous devez voir maintenant 3 lignes d’informations : la première ligne
qui s’était déjà affichée, puis «un client s’est connecté», puis à nouveau « Le serveur Urban Marginal
attend ». La console affiche ce qui se passe côté serveur, mais vous pouvez voir aussi la
seconde console, côté client : pour cela, sur la même ligne que les onglets (où se trouve Page 23
la console) à droite, repérez le petit écran (en passant la souris dessus, il y a le message
« afficher la console sélectionnée »), cliquez sur la petite flèche à côté, pour voir appa-
raître la liste des consoles, et sélectionnez la seconde console. Cette fois vous devriez voir
le message « connexion au serveur réussie ». Cliquez sur Quitter dans la première frame
(celle qui a permis de lancer le serveur). La frame doit se fermer, mais une messagebox
doit apparaître précisant « L’ordi distant s’est déconnecté ». Faites ok et cliquez aussi sur
Quitter de la frame restante.
Si tous ces messages apparaissent, c’est que l’aspect client/serveur marche très bien,
même si nos tests ici sont faits en local, en lançant l’application serveur et cliente sur la
même machine. L’adresse 127.0.0.1 permet de tester en local, ce qui est bien pratique
pour créer l’application. Par la suite, vous ferez des tests sur des machines différentes.
Voilà le plus dur est fait. L’air de rien, ces 3 classes (qui vont être un peu complétées par la
suite) vont pouvoir être facilement réutilisables par d’autres applications qui nécessitent
de réaliser du client/serveur.

7. La construction du jeu
Le jeu côté serveur et le jeu côté client ne vont pas fonctionner de la même façon.
Cependant, certains points sont communs (tous les deux vont devoir traiter des informa-
tions reçues de l’ordinateur distant, par exemple) et surtout, le controleur qui a besoin

8 2950 TP PA 00
de communiquer avec le jeu, doit pouvoir le faire sans toujours se poser la question si
c’est le jeu côté serveur ou côté client. Tout cela pour dire qu’on va créer des classes
JeuClient et JeuServeur mais aussi une classe mère Jeu. C’est parti !

La classe Jeu
Dans le package modele, créez la classe Jeu (c’est une classe simple mais cochez en plus
la case Abstract). Remarquez le mot abstract qui s’est mis à côté de class. Cela signifie
donc que cette classe ne sera pas directement instanciée.
La propriété controle
Les classes qui vont gérer le jeu doivent pouvoir communiquer avec le controleur. Donc,
au moment de leur création (que ce soit un jeu côté client ou serveur) elles recevront une
instance de la classe Controle, qu’il faudra mémoriser. Déclarez en protected (pour que
les 2 classes filles puissent y accéder) la propriété controle de type Controle.
Le setter sur connection
Les 2 classes filles auront aussi besoin de gérer la réception d’un objet de Type
Connection : soit le jeu client reçoit l’objet de connection du serveur pour communiquer
avec lui, soit le jeu serveur reçoit un objet de connection d’un des clients pour communi-
quer aussi avec lui. Donc dans les 2 cas, on reçoit une connection mais on ne fait pas la
même chose avec. Du coup, on veut forcer les 2 classes filles à avoir un setter sur un objet
connection, mais chaque fille n’aura pas le même code dans cette méthode. Pour « for-
cer » toutes les filles à avoir une méthode avec la même signature, on crée une méthode
abstraite. Dans la classe Jeu, créez la méthode publique et abstraite setConnection qui
Travaux pratiques reçoit en paramètre connection de type Connection (attention, ne vous trompez pas
d’import : prenez bien outils.connexion). Quand on écrit une méthode abstraite, il faut
Urban Marginal mettre abstract devant le type de la méthode et ne pas mettre de corps à la méthode
(donc pas d’accolades, juste un point virgule après la signature).
Page 24
La réception
Les 2 classes filles auront besoin de traiter la réception de messages provenant de l’ordi-
nateur distant. Dans la classe Jeu, créez la méthode publique et abstraite reception
qui reçoit en paramètre connection de type Connection (pour savoir qui a envoyé le
message, ce qui est primordial pour le serveur) et info de type Object (qui contiendra le
message envoyé par l’ordinateur distant).
L’envoi
Les 2 classes filles auront à envoyer des informations à l’ordinateur ou aux ordinateurs
distants. Il y aura du coup plusieurs types d’envois, cependant tous auront besoin de
passer par l’objet connection pour envoyer. Mais l’envoi n’a pas encore été géré dans la
classe Connection. Allez dans la classe Connection et écrivez la méthode publique envoi
qui ne retourne rien et qui reçoit en paramètre unobjet de type Object (ce sera l’objet à
envoyer). Pour l’envoi, on utilise le canal de sortie out. Dans envoi, appliquez la méthode
writeObject sur l’objet out, en mettant en paramètre, unobjet. La méthode va s’occuper
d’envoyer l’objet unobjet vers l’ordinateur distant. Un try/catch est réclamé : ajoutez-
le. Dans la catch, mettez juste un affichage console pour signaler une erreur sur l’objet
out. Ne stoppez pas l’application. Dans le try, après le writeObject, il faut appliquer la
méthode flush sans paramètre, à l’objet out pour vider le canal de sortie.
Revenez dans la classe Jeu et écrivez la méthode publique (non abstraite) envoi, qui ne
retourne rien et qui reçoit les mêmes paramètres que la méthode reception. À l’intérieur,

8 2950 TP PA 00
appelez la méthode envoi sur l’objet connection, en mettant en paramètre l’objet info.
Cet objet sera alors envoyé à l’ordinateur distant, par l’intermédiaire de la connexion
qui a été créée.
La déconnection
Les 2 classes filles auront à gérer la déconnection de l’ordinateur distant. Créez la
méthode publique et abstraite deconnection qui reçoit en paramètre connection de type
Connection.
La classe Jeu est terminée. Elle ne contient pas grand-chose, mais va s’avérer bien pra-
tique, en particulier à travers ses méthodes abstraites qui pourront être appelées quel
que soit le type de jeu (client ou serveur).

Les classes filles JeuServeur et JeuClient


Il n’est pas encore question d’écrire complètement les 2 classes filles, mais juste de les
démarrer. Dans le package modele, créez les 2 classes JeuServeur et JeuClient. Précisez
pour chaque classe qu’elle hérite de la classe Jeu (en ajoutant, à côté du nom de la classe,
" extends Jeu ").
Remarquez que le nom de chaque classe est souligné. Placez la souris dessus : les
méthodes abstraites ne sont pas implémentées. Effectivement, les méthodes abstraites
de la classe mère doivent être obligatoirement présentes dans les classes filles. Cliquez
sur " ajouter les méthodes non implémentées ". Les méthodes ont été ajoutées automa-
tiquement. Le " @Override " permet de repérer les méthodes abstraites mais n’est pas
obligatoire.
Dans les 2 classes, créez les constructeurs correspondants, qui reçoivent le paramètre Travaux pratiques
controle de type Controle, et qui valorise la propriété protégée controle de la classe
mère, avec le paramètre. Urban Marginal

Le démarrage du jeu Page 25


Revenez dans la classe Controle. Déclarez la propriété privée lejeu de type Jeu. Dans
la méthode evenementEntreeJeu, après l’instanciation de ServeurSocket, affectez à la
propriété lejeu, une instance de la classe JeuServeur en envoyant this au constructeur.
Un jeu de type serveur va ainsi être créé. Ensuite, la frame d’entrée peut être fermée :
appliquez la méthode dispose() sur l’objet frmEntreeJeu. Après cette frame, le serveur
doit afficher l’arène du jeu. Dans le package vue, créez la frame Arene (nouveau/autre/
Java/Visual Class) et donnez lui comme titre " Arène ". Pour le moment on va laisser cette
frame en l’état.
Revenez dans la classe Controle. Déclarez en privé la propriété frmArene de type Arene.
Dans la méthode evenementEntreeJeu, après avoir fermé la frame frmEntreeJeu, affec-
tez à frmArene une instance de la classe Arene, puis appliquez à la propriété frmArene
la méthode setVisible avec true en paramètre, pour rendre la frame visible.
Lancez un test. Cliquez sur le bouton Demarrer. La frame se ferme et la nouvelle frame
Arène, qui est vide, s’ouvre. Fermez-la en cliquant sur la croix. Attention, fermer ainsi la
frame ne semble pas arrêter le processus (remarquez le carré rouge à droite de l’onglet de
la console). Cliquez sur ce carré rouge pour arrêter le processus.

Dans la méthode evenementEntreeJeu, dans la partie sinon (la partie concernant le jeu
côté client), si la connexion s’est bien passée, créez un jeu client comme vous l’avez fait
pour le jeu serveur, créez tout de suite frmArene mais sans la rendre visible car il faut

8 2950 TP PA 00
gérer d’abord la frame du choix du joueur, fermez la frame frmEntreeJeu. Dans le pac-
kage vue, créez la frame ChoixJoueur et donnez-lui comme titre " Choix ".
Dans la classe Controle, déclarez en propriété privée frmChoixJoueur de type ChoixJoueur
que vous venez de créer. Dans la méthode evenementEntreeJeu, après la création de
frmArene, créez et rendez visible frmChoixJoueur.
Lancez un test. Cliquez sur Demarrer pour lancer un serveur : la frame de l’arène s’ouvre.
Lancez à nouveau l’application. Cliquez sur Connecter pour lancer un client : cette fois
c’est la frame Choix qui s’ouvre. Vous pouvez fermer les 2 frames sans oublier de stopper
les 2 processus en cliquant sur le carré rouge pour les 2 consoles.

Pour éviter ce problème de non fermeture de processus quand une fenêtre est fermée,
on va ajouter une ligne de code dans le constructeur des 3 frames, juste après l’appel
de super().
setDefaultCloseOperation(EXIT_ON_CLOSE) ;

Faites rapidement un nouveau test pour contrôler que les processus se stoppent bien auto-
matiquement à la fermeture sur la croix.

Travaux pratiques
8. Le choix du joueur
Urban Marginal
La frame du choix du joueur n’est accessible que par le client : elle permet de choisir un
Page 26 personnage, de saisir le pseudo et d’entrer dans l’arène. La construction de cette frame
va se faire différemment de la précédente. Cette fois, plutôt que de placer des boutons,
on va intégrer une image de fond et placer des JLabels vides pour gérer le clic sur les
différentes zones de l’image. On gardera cependant l’intégration d’une zone de texte
pour saisir le pseudo.

La partie graphique fixe


Voici à quoi va ressembler la frame une fois qu’elle sera construite :

8 2950 TP PA 00
L’image de fond
Avant tout, n’oubliez pas de mettre le layout à null, comme pour la frame précédente,
afin de pouvoir positionner les objets graphiques sans difficulté.
Les médias nécessaires pour réaliser ce TP (images, sons…) se trouvent dans le dossier
media qui est directement dans la correction (téléchargeable au même endroit que ce
fascicule de TP). Pour pouvoir l’utiliser dans le projet, copiez ce dossier dans le dossier de
votre projet (Urban Marginal), donc au même niveau que les dossiers src et bin.
Pour mettre une image de fond, il faut commencer par dimensionner la frame à la taille
de l’image (en réalité, un peu plus car il faut prendre en compte la hauteur de la bande
de titre et les bords. L’image fait 400x275 (vous pouvez aller contrôler). Chez moi, la
barre de titre fait 27 et le bord 4. Donc, allez dans la méthode initialize() et modifiez
la taille en conséquence (408, 302). Remarquez que la zone de visualisation a automati-
quement adapté la taille de la frame. Si vous sentez une différence (ce sera par exemple
le cas si vous êtes sous Vista), alors essayez d’adapter au mieux la taille (on n’est tout de
même pas au pixel près).
N’importe où dans la frame, et de n’importe quelle taille, placez un jLabel et nommez
le lblFond. Le code correspondant s’est automatiquement généré. C’est ce JLabel qui va
contenir l’image de fond. Dans le code, méthode getJContentPane(), modifiez la taille
du JLabel en mettant (0, 0, 400, 275).
Dans le code, enlevez la ligne qui initialise le texte du JLabel (supprimez complètement
la ligne de code).
Pour intégrer l’image dans le JLabel, pour changer on va écrire le code : placez vous
juste en dessous du redimensionnement du JLabel, ajoutez une ligne vide. Appliquez Travaux pratiques
la méthode setIcon au JLabel lblFond et, en paramètre, instanciez la classe ImageIcon
en envoyant en paramètre le nom de l’image avec son chemin complet (« media/fonds/ Urban Marginal
fondchoix.jpg »). Faites l’import nécessaire pour ImageIcon. Normalement l’image doit
s’afficher. Si ce n’est pas le cas, fermez l’onglet de ChoixJoueur et ouvrez le à nouveau. Page 27
Si ça n’apparaît toujours pas, mettez en commentaire la ligne concernée, enregistrez,
changez de ligne, puis enlevez le commentaire. Tout ceci parait du « bricolage » (ça
l’est !) mais cela semble venir d’un petit « bug » d’Eclipse. Dans le cas où vous n’obtenez
toujours pas l’image, lancez l’application pour contrôler qu’à l’exécution, elle s’affiche.
Si elle ne s’affiche pas, alors le problème est différent (vous avez fait une erreur dans le
chemin ou l’image n’est pas au bon endroit).
Les objets graphiques
Placez un JLabel sur la flèche de gauche et appelez le lblPrecedent. Enlevez la ligne de
setText de ce JLabel. Faites un clic droit sur ce JLabel, Events/AddEvents… Sélectionnez
Mouse/mouseClicked, puis cliquez sur Terminer. En prévision, ajoutez dans le code la
procédure événementielle lblPrecedent_clic(), et remplacez l’affichage système derrière
l’événement de la souris, par un appel à votre procédure.
Suivez les mêmes étapes pour la flèche de droite en donnant le nom lblSuivant. Idem
pour le GO en donnant le nom lblGO.
Placez une zone de texte dans la zone blanche à côté de Pseudo, et nommez la txtPseu-
do. Enfin, placez un JLabel de la taille de la zone d’affichage (la zone entre les 2 flèches
blanches, qui va contenir l’image du petit personnage) et appelez le lblPersonnage.
Pour ce JLabel, allez dans les propriétés et sélectionnez le centrage pour l’alignement
horizontal et vertical.

8 2950 TP PA 00
Faites un test pour contrôler que l’image de fond s’affiche. En revanche vous ne voyez pas
les JLabels, mais vous pouvez repérer la zone de texte en cliquant dessus.

Comme les boutons de la frame n’en sont pas (ce sont des JLabels positionnés sur des
zones de l’image, on va faire en sorte que l’utilisateur repère la possibilité de cliquer sur
certaines zones en changeant l’aspect de la souris.
Pour cela, commencez par créer la méthode privée souris_normale() qui ne retourne rien.
Cette méthode applique la méthode setCursor sur jContentPane (le JPanel principal). La
méthode setCursor attend un objet Cursor en paramètre : instanciez la classe Cursor : le
constructeur attend en paramètre un numéro correspondant à un type de curseur, et il se
trouve que la classe Cursor possède des constantes publiques et statiques correspondant
à ces numéros. Utilisez donc la constante DEFAULT_CURSOR.
De même, créez la méthode privée souris_doigt() qui cette fois utilisera la constante
HAND_CURSOR.
Ces 2 méthodes doivent être appelées maintenant derrière les bons événements : ajoutez
les 2 événements mouseEntered et mouseExited sur le JLabel lblGO. Le but est de faire
en sorte que la souris devienne un doigt pointé quand on entre dans la zone (mouseEn-
tered) et qu’elle redevienne normale quand on en sort (mouseExited). Remplacez les
affichages systèmes mis par défaut, par l’appel de vos méthodes.
Faites de même pour les flèches (lblPrecedent et lblSuivant).

Faites un test pour contrôler que le curseur de la souris change bien quand vous survolez
Travaux pratiques les flèches ou le GO.
Urban Marginal
On va faire en sorte qu’au chargement de la frame, le curseur soit positionné par défaut
Page 28 dans la zone de texte pour la saisie du pseudo. À la fin du constructeur de la frame, après
le initialize(), appliquez la méthode requestFocus() sur la zone de texte.

La partie graphique variable


L’interface Globale
Il est temps de faire une petite pause pour rendre le code plus propre. Depuis le début
du codage, vous avez plusieurs fois mis des valeurs « en dur » dans votre code : le nom
et chemin de l’image de fond, le numéro de port… Vous risquez de continuer ainsi
avec d’autres valeurs comme maintenant les noms des images des personnages. Dans
l’ensemble de l’application, plusieurs constantes globales seront nécessaires. Plutôt que
de les disperser dans tout le code, on va les regrouper dans un même endroit. Une inter-
face est l’endroit idéal. Je vous rappelle qu’une interface est une classe spéciale, qui est
par défaut abstraite et qui ne peut contenir que des propriétés publiques, statiques et
finales (donc des constantes) et des méthodes abstraites. Dans notre cas, on va créer une
interface juste pour mémoriser des constantes, donc il n’y aura pas de méthodes.
On va créer l’interface Globale dans le package controleur : clic droit sur le package /
nouveau / Interface. Donnez le nom Global, puis Terminer. Tous les noms des constantes
que vous allez déclarer et initialiser dans cette interface devront être en majuscule (ce
n’est pas une obligation, mais c’est une norme d’écriture couramment utilisée en pro-
grammation objet pour distinguer les constantes du reste).
Dans la classe, déclarez la propriété PORT (toutes les propriétés sont publiques, statiques
et finales : public static final) de type entier et initialisez cette propriété avec 6666.

8 2950 TP PA 00
Voyons maintenant comment utiliser une constante : allez dans la classe Controle car c’est
là que l’on a besoin du numéro de port. La classe doit avoir accès à l’interface Global :
elle se trouve dans le même package donc il n’y aura pas d’import à faire, ce qui ne sera
pas le cas des autres classes (il faudra donc penser à gérer l’import). La classe Controle
doit implémenter l’interface Global pour avoir directement accès à ces membres : dans
l’entête de la classe, juste après « public class Controle » et avant l’accolade, ajoutez :

implements Global

Je vous rappelle qu’une classe ne peut hériter que d’une seule autre classe, en revanche
elle peut implémenter plusieurs interfaces.
Il ne vous reste plus qu’à remplacer tous les 6666 de la page (il y en a 2) par la constante
PORT. Normalement la constante apparaît en bleu et italique. D’ailleurs, si vous passez
la souris dessus, vous verrez apparaître la valeur de la constante.
Maintenant que vous avez compris le principe, ajoutez dans Global les constantes sui-
vantes :
CHEMIN = "media/"
CHEMINFONDS = CHEMIN + "fonds/"
FONDCHOIX = CHEMINFONDS + "fondchoix.jpg"

Modifiez la classe ChoixJoueur de façon à faire appel à FONDCHOIX . Vous remarquez


que dans la partie visualisation, l’image de fond ne se voit plus. C’est pour cette raison
que dans un premier temps je vous ai fait mettre l’image « en dur » (pour la voir et pou-
voir travailler dessus en plaçant les JLabels aux bons endroits). Cela reste plus propre de
Travaux pratiques
l’avoir sous forme de constante placée dans Global.
Urban Marginal

Faites tout de même un test pour contrôler que l’image s’affiche correctement. Page 29

On aura donc régulièrement besoin, par la suite, d’ajouter des constantes dans l’inter-
face Global.
L’affichage du personnage
Pour comprendre comment vont s’afficher les personnages, allez voir dans le dossier
media/personnages/ les noms des différents personnages. Remarquez la constitution des
noms. C’est en plusieurs parties :
perso // tous les fichiers des personnages commencent ainsi
1 // numéro du personnage (pour le moment il y en a 3)
marche // état du personnage ("marche" : il est en train de marcher ;
// "touche" : il est blessé ; "mort" : il meurt)
1 // étape dans l’état du personnage (première étape de la
// marche…)
d0 // direction (d0 pour gauche, d1 pour droite)

8 2950 TP PA 00
Compte tenu de cette construction du nom, le mieux est encore une fois de travailler
avec des constantes. Dans l’interface Global, ajoutez les constantes suivantes :
GAUCHE = 0 // pour la direction
DROITE = 1
CHEMINPERSOS = CHEMIN + "personnages/"
PERSO = CHEMINPERSOS + "perso"
EXTIMAGE = ".gif"
MARCHE = "marche" // les différents états
BLESSE = "touche"
MORT = "mort"
NBPERSOS = 3 // nombre de personnages
H_PERSO = 44 // taille de l’image du personnage
L_PERSO = 39

Retournez dans ChoixJoueur. Dans cette frame, tous les personnages seront présentés
tournés vers la droite, dans la première position de marche. On a donc juste besoin de
gérer le numéro du personnage qu’il faut afficher. Déclarez numPerso comme propriété
privée de la classe et, à la fin du constructeur, initialisez le à 1 (de façon à commencer
par le premier personnage).
Créez la méthode privée affichePerso(). Le seul but de cette méthode est d’affecter au
JLabel lblPersonnage la bonne image en fonction de numPerso. Vous savez déjà affec-
ter une image à un JLabel (vous l’avez vu pour l’image de fond). Donc, en concaténant
correctement des constantes, des chaînes et numPerso, affichez le bon personnage dans
Travaux pratiques la bonne position.
Urban Marginal Appelez la méthode affichePerso à la fin du constructeur, juste après l’initialisation de
numPerso.
Page 30 Lorsque plusieurs JLabels se superposent (c’est le cas de lblFond et lblPersonnage, l’ordre
d’addition des JLabels dans le JPanel général est important : le premier ajouté est celui
qui sera le plus en premier plan : allez voir dans getContentPane si lblPersonnage est
ajouté (add) avant ou après lblFond. Si la ligne de code est après, placez-la avant.

Faites un test pour voir si le personnage s’affiche.

Il faut maintenant pouvoir changer de personnage.

Les actions
Les boutons suivant et précédent
Derrière le clic sur un des 2 JLabels qui permettent de passer au personnage précédent
ou suivant, il suffit de modifier numPerso puis de réafficher l’image. Le but est de bou-
cler sur les images : donc en cliquant par exemple plusieurs fois sur la flèche vers la
droite, on doit passer à l’image 2, puis 3, puis revenir à la 1 etc… Idem dans l’autre sens
pour la flèche vers la gauche. Je m’arrête à 3 parce qu’il y a 3 personnages : n’oubliez
pas que dans Global, vous avez déclaré une constante NBPERSOS qui contient le nombre
de personnages (susceptible de varier si vous en ajoutez). Donc dans le code, n’utilisez
pas 3 mais NBPERSOS. Vous pouvez modifier numPerso et faire un test, vous pouvez aussi
trouver plus court et faire cette modification en une seule ligne de code (en utilisant

8 2950 TP PA 00
judicieusement % qui donne le reste d’une division entière)). Si vous n’y arrivez pas en
une ligne, faites la méthode classique : vous verrez la méthode courte dans la correction.
N’oubliez pas ensuite d’appeler affichePerso.

Faites un test pour contrôler que le personnage change bien en cliquant sur les 2 flèches,
et que l’affichage boucle comme cela a été demandé plus haut.

Le bouton Go
Sur le clic sur GO, il faut commencer par contrôler que le pseudo a bien été saisi : faites
un test sur le contenu de txtPseudo (attention ce n’est pas si simple : pour vérifier si le
contenu est vide, vous allez récupérer le contenu avec la méthode getText(), puis appli-
quer la méthode equals qui permet de faire une comparaison, et en paramètre, mettre
"" pour comparer avec une chaîne vide). Si la zone de texte est vide, affichez une mes-
sagebox précisant que le pseudo est obligatoire, puis remettez le focus sur la zone de
texte.
Dans le cas contraire, il faut lancer l’arène. C’est bien sûr le controleur qui doit s’en
charger. Mais nous n’avons pas le controleur. Il faut le réceptionner par le constructeur :
retournez dans la classe Controle à l’endroit où vous instanciez ChoixJoueur. Faites
comme pour JeuClient ou JeuServeur : envoyez en paramètre du constructeur, this. Ainsi
vous envoyez à la frame l’instance actuelle de controle. Bien sûr une erreur apparaît,
c’est normal puisqu’il faut maintenant modifier le constructeur de la frame.
Retournez dans ChoixJoueur, au niveau du constructeur, ajoutez dans la signature le
paramètre controle de type Controle. Déclarez aussi en propriété privée de la classe,
Travaux pratiques
controle de type Controle. Dans le constructeur, valorisez la propriété controle avec le
paramètre correspondant. Urban Marginal
Dans la classe Controle, vous avez déjà écrit la méthode evenementVue qui permet de
récupérer et traiter les événements provenant de la vue. On va donc faire appel à cette Page 31
méthode. Retournez dans la classe ChoixJoueur, méthode lblGO_clic, parti "else" : à cet
endroit, on veut démarrer le jeu, donc appelez la méthode evenementVue sur la pro-
priété controle, et envoyez en premier paramètre this (puisqu’il faut envoyer la frame
qui fait appel au controleur), et en second paramètre… on verra plus tard car on ne
connaît pas encore la chaîne à transférer.
Retournez dans la classe Controle pour traiter ce nouvel événement : dans evenemen-
tVue, ajoutez un test similaire au test déjà présent, pour contrôler si la frame reçue en
paramètre est une instance de ChoixJoueur. Dans ce cas, allez la méthode non encore
écrite evenementChoixJoueur.
Comme vous avez déjà écrit la méthode evenementEntreeJeu, écrivez la méthode evene-
mentChoixJoueur. Cette méthode doit s’occuper d’envoyer des informations au serveur
(le pseudo et le numéro du personnage sélectionné). Vous vous doutez qu’au cours du
jeu, le serveur va recevoir des informations différentes du client (message du t’chat,
pseudo, déplacement…). Le but est de faire en sorte que le serveur soit capable de distin-
guer le contenu de l’information qui lui arrive. On va utiliser 2 méthodes pour distinguer
les contenus : la distinction par le type (on contrôlera si ce que l’on reçoit est un String,
ou un jLabel etc…) et la distinction par l’entête (pour les informations de même type, ce
sera le cas pour le String du pseudo ou du t’chat, on collera au début du message, une
valeur et un séparateur pour reconnaître le contenu. Le séparateur va servir à découper
la chaîne et aussi à récupérer plusieurs parties (donc on pourra envoyer en même temps

8 2950 TP PA 00
le pseudo et le numéro du personnage). Pour toutes ces raisons, on va ajouter dans
Global quelques constantes bien pratiques :
SEPARE = "~" // caractère de séparation (volontairement rare)
PSEUDO = 0 // le message contiendra le pseudo et numéro du
// personnage
On ajoutera les autres types de message au fur et à mesure des besoins.
Pour envoyer des informations, il va falloir passer par la classe JeuClient. Allez dans cette
classe : il n’y a pas encore de méthode envoi. Par contre, si vous regardez dans la classe
Jeu, la méthode existe, mais elle attend l’information à envoyer et surtout un objet de
type Connection pour savoir à qui envoyer. C’est là qu’on a un petit problème : dans la
classe JeuClient, on a besoin d’avoir un objet connection qui permet de faire le lien avec
le serveur. Dans la classe JeuClient, déclarez en privé la propriété connection de type
Connection. La classe JeuClient contient déjà la méthode setConnection qui est redéfinie
de la classe mère : remplissez-la en valorisant la propriété avec le paramètre.
Mais qui va appeler cette méthode pour valoriser la connection ? Il n’y a que la classe
Connection qui peut dans un premier temps envoyer l’objet connection (donc lui-même).
Mais attention, c’est le controleur qui gère tout et d’ailleurs la classe Connection a reçu
une instance du controleur, par de la classe Jeu. Il va falloir donc faire en sorte que
Connection avertisse le controleur qui lui-même informera le jeu.
Pour commencer, allez dans la classe Controle et créez en public la méthode setConnec-
tion qui reçoit en paramètre un objet connection de type Connection. Déclarez en privé
la propriété connection de type Connection, et valorisez là dans le setter.
Allez dans la classe Connection, à la fin du constructeur, après l’appel du start, appli-
Travaux pratiques
quez la méthode setConnection à l’objet lerecepteur en envoyant en paramètre this (la
Urban Marginal connection actuelle) : ça ne marche pas ? C’est normal. lerecepteur est de type Object,
donc il ne connaît pas la méthode setConnection. Cependant, même si lerecepteur
Page 32 est de type Object, en réalité c’est à l’origine un objet de type Controle : donc avant
d’appliquer la méthode, transtypez-le et, pour éviter de faire un import, transtypez-le
directement avec controleur.Controle (c’est pour vous montrer que ça marche aussi). Si
vous avez du mal, voici la ligne de code :
((controleur.Controle)lerecepteur).setConnection(this) ;
Maintenant que le controleur a reçu l’objet de connection, il faut qu’il en informe le
JeuClient. À quel moment ? Dès que le jeu côté client est créé : dans la classe Controle,
dans evenementEntreeJeu, repérez l’endroit où vous instanciez JeuClient. La ligne en
dessous, appelez la méthode setConnection sur l’objet lejeu et envoyez en paramètre la
propriété privée connection.
Pourquoi avons-nous fait tout ça ? Pour que le client puisse envoyer des informations
au serveur. Cependant il n’a toujours pas de méthode pour envoyer. Je vous rappelle
que la méthode d’envoi est dans la classe Jeu. Dans la classe JeuClient, surchargez cette
méthode en ne lui mettant plus qu’un paramètre : l’information à envoyer (info de type
Object). Dans la méthode, appelez la méthode envoi de la classe mère, en mettant en
paramètre la propriété connection, et l’information à envoyer.
Maintenant tout est prêt pour l’envoi : on voulait donc envoyer au serveur le pseudo et
le numéro du personnage. Retournez dans la classe Controle, méthode evenementChoix-
Joueur(). Appelez la méthode envoi sur l’objet lejeu mais attention, c’est la méthode
envoi de JeuClient qui nous intéresse, donc il faut correctement transtyper lejeu. En
paramètre, envoyez le paramètre info.

8 2950 TP PA 00
Dans ChoixJoueur, retournez dans la méthode lblGO_clic. Vous vous rappelez qu’on
avait laissé le second paramètre en suspend. Il est possible maintenant de le remplir. Ce
second paramètre est l’information sous forme de chaîne à envoyer au serveur. Mettez
la constante PSEUDO, suivi de la constante SEPARE (ne vous inquiétez pas sur le types
des valeurs concaténées : dès qu’il y a au moins une valeur de type chaîne dans la conca-
ténation, tout est considéré comme chaîne), suivi du pseudo (récupérez le contenu de
txtPseudo avec getText() ), suivi encore de la constante SEPARE et, pour finir, suivi du
numéro du personnage (numPerso).
Après cet envoi, dans la classe Controle, méthode evenementChoixJoueur, il faut fermer
la frame du choix du joueur (vous savez faire, avec la méthode dispose()), et rendre
visible la frame de l’arène.
Faites un test, sélectionnez un personnage et faites GO sans taper de pseudo : normale-
ment une messagebox apparaît signalant le problème. Tapez un pseudo et faites GO : la
frame de l’arène (vide) apparaît. Pour le moment on ne peut pas tester si l’envoi du pseudo
et du personnage a marché. Ça ne va pas tarder.
La réception du pseudo et du personnage par le serveur
Dans la classe JeuServeur, il existe bien une méthode reception. Cependant qui va l’appe-
ler ? Sachant que les informations qui sont envoyées arrivent au niveau de la connection,
et que la connection ne peut communiquer qu’avec le controleur, soit on crée dans le
controleur une méthode qui s’occupe d’appeler la méthode reception du jeu, soit on
crée dans le controleur un getter sur lejeu afin d’accéder à la méthode reception en
passant par ce getter. On va utiliser cette 2e solution : dans la classe Controle, créez un
getter sur lejeu.
Travaux pratiques
Dans la classe Connection, juste après la reception d’un objet (dans le run), on va trans-
férer l’objet reçu vers la classe Jeu correspondante, en passant par le controleur : en Urban Marginal
transtypant correctement lerecepteur, appliquez-lui la méthode getLejeu pour enfin
accéder à la méthode reception. Cette méthode attend 2 paramètres : l’objet de connec- Page 33
tion (donc this) et l’objet reçu (donc reception).
Juste pour contrôler que le serveur réceptionne bien le pseudo et numéro de person-
nage, allez dans la classe JeuServeur, méthode reception, et faites un affichage console
de l’objet info reçu en paramètre.
Faites un test et contrôlez dans la console, si côté serveur, vous recevez bien une chaîne
qui commence par 0 (c’est le code pour avertir de l’arrivée d’un pseudo) suivi de ~, puis du
pseudo, encore de ~ et enfin d’un chiffre (1, 2 ou 3) représentant le numéro du personnage
choisi.
Vous pouvez enlever la ligne de code de l’affichage console qui n’était là que pour véri-
fier le transfert.

8 2950 TP PA 00
9. L’arène du jeu
Il est temps de construire l’arène.

La structure de l’arène
Les caractéristiques graphiques de l’arène
Retournez dans la classe Arene. On va construire les bases de l’arène. Le but au final sera
d’obtenir ceci :
Les murs et les personnages vont
s’ajouter de façon dynamique.
Cependant, pour gérer l’ajout de
tous ces JLabels, on aura besoin de
2 JPanels (donc des conteneurs) :
un pour les murs et un pour les
personnages. Il va donc y avoir un
JLabel pour l’image de fond, les
2 JPanels pour les personnages et
les murs, puis, en bas, une zone
de texte pour saisir les phrases du
t’chat, un conteneur avec ascen-
seur pour intégrer l’affichage du
t’chat, et la grande zone de texte
(JTextArea) pour afficher le t’chat.
Travaux pratiques
Avant tout, mettez vous en layout
Urban Marginal
null, puis agrandissez un peu la
frame et, sans trop vous inquiéter
Page 34
des dimensions (que l’on va défi-
nir par la suite), placez vers le haut un JLabel du nom de lblFond (videz son texte), 2
JPanels (que vous trouverez dans la partie Containers) avec les noms jpnMurs et jpnJeu
(qui contiendra les personnages), et vers le bas, la zone de texte (JTextField) du nom de
txtSaisie, le conteneur de type JScrollPane du nom de jspChat et enfin, à l’intérieur un
JTextArea du nom de txtChat. Vous pouvez très bien faire une disposition comme ceci,
pour le moment (le seul point à respecter étant de créer txtChat à l’intérieur de jspChat) :

lblFond (JLabel)
jpnJeu (JPanel)

jpnMurs (JPanel)

8 2950 TP PA 00
Quand vous ajoutez txtChat à l’intérieur du JPanel jspChat, il est normal que le txtChat
prenne la taille du JPanel.
Avant de s’inquiéter du positionnement des objets graphiques, on va contrôler l’ordre
des ajouts dans le JPanel général. Vous avez déjà pu constater dans la frame précédente
que l’ordre est important pour l’affichage (le premier objet ajouté est celui qui est le plus
au premier plan). Allez dans la méthode getJContentPane. Les seuls objets graphiques
qui risquent de se chevaucher, et dont l’ordre doit être contrôlé, sont lblFond, jpnMurs
et jpnJeu. Il faut ajouter en premier jpnJeu, puis jpnMurs et enfin lblFond. Modifiez
l’ordre des lignes pour respecter cet ordre. Pour les autres objets graphiques, cela n’a
pas d’importance.
Les 2 JPanels jpnJeu et jpnMurs doivent être en layout null, comme tous les JPanels que
l’on a manipulé jusqu’à maintenant, afin que le positionnement des objets dans le JPanel
se fasse par coordonnées (faites la modification dans les 2 getters correspondants).
En ce qui concerne les positionnements précis, comme certaines tailles, en particulier la
taille de la zone de jeu, doivent être mémorisées dans des variables pour être réutilisées
(en particulier pour éviter qu’un joueur n’aille au delà de la limite du jeu), on va tout
simplement mémoriser en constante toutes les tailles. Allez dans l’interface Global et
ajoutez les constantes suivantes :

txtSaisie (JTextField)
H_ARENE = 600 jspChat (JScrollPane)
L_ARENE = 800 qui contient
H_CHAT = 200 txtChat (JTextArea)
H_SAISIE = 25 Travaux pratiques
MARGE = 5 // elle va servir pour les écarts entre différents objets
Urban Marginal
Dans la classe Arene, implémentez l’interface Global pour accéder à ces nouvelles
constantes. Page 35
Directement dans le code, vous allez modifier la taille des objets suivants :
• la frame elle-même (dans initialize) : L_ARENE, H_ARENE + H_CHAT ;
• lblFond, jpnMurs et jpnJeu : 0, 0, L_ARENE, H_ARENE ;
• txtSaisie : 2, H_ARENE + MARGE, L_ARENE – 2*MARGE, H_SAISIE ;
• jspChat : MARGE, H_ARENE + H_SAISIE + 2*MARGE, L_ARENE – 3*MARGE, H_CHAT
– H_SAISIE – 8*MARGE.
À partir de là, bien sûr la visualisation ne sert plus à grand-chose car elle n’est pas
capable d’interpréter les constantes. Ce n’est pas plus mal : cela va vous habituer à tra-
vailler uniquement dans le code.
On va ajouter l’ascenseur uniquement vertical dans l’affichage du t’chat : dans la
méthode getJspChat, dans le if, appliquez la méthode setVerticalScrollBarPolicy sur
l’objet jspChat. Cette méthode attend en paramètre une valeur correspondant au type
d’ascenseur voulu. Envoyez en paramètre la propriété publique et statique VERTICAL_
SCROLLBAR_ALWAYS de la classe JScrollPane.
Il reste à placer l’image de fond. Allez dans l’interface Global et ajoutez la constante :
FONDARENE = CHEMINFONDS+"fondarene.jpg"
De retour dans la classe Arene, ajoutez cette image au JLabel lblFond (vous savez faire).
Insérez l’import nécessaire.

8 2950 TP PA 00
Pour que le fond soit visible, alors que 2 autres JPanels sont placés par-dessus, il faut que
les 2 autres JPanels soient transparents. Pour cela, dans les getters des 2 JPanels, utilisez
sur chaque JPanel la méthode setOpaque et mettez false en paramètre.

Il est temps de faire un test : lancez le serveur. L’arène devrait s’afficher correctement,
comme l’image présentée plus haut, mais bien sûr sans les murs ni les personnages.
Lancez aussi le client pour constater la même chose.

La gestion des objets de l’arène


L’arène va contenir 3 types d’objets : les murs, les personnages et les boules. La particu-
larité de ces objets est qu’ils devront apparaître aussi bien côté serveur que côté client.
À chaque modification, il faudra informer tous les clients pour que la modification soit
prise en compte.
Il faut donc réfléchir sur le moyen de gérer le jeu et de faire en sorte que tout le monde
affiche les mêmes objets.
Solution 1 : le serveur est transmetteur, les clients gèrent les calculs. Dans cette solution,
un ordre fait par un client, est transféré au serveur qui se contente de transférer l’ordre
à tous les clients. Ce sont les clients qui gèrent les calculs pour exécuter l’ordre.
Solution 2 : le serveur gère tous les calculs et les clients se contentent d’afficher ce que
le serveur envoie. C’est cette solution que l’on va prendre. L’idée est donc d’envoyer au
client directement des objets à afficher, par exemple un JPanel complet, ou un JLabel,
sachant que quand on envoie un JLabel, il est envoyé avec toutes ses caractéristiques
(contenu mais aussi position…).
Travaux pratiques Quand un joueur entre dans l’arène, il faudra lui envoyer le JPanel complet des murs,
en une seule fois. Ensuite, au fur et à mesure d’actions sur d’autres joueurs (un nouveau
Urban Marginal joueur arrive, ou un joueur s’est déplacé, etc.), il recevra le JLabel de ce joueur. On aura
alors soit à l’ajouter au JPanel du jeu, soit à remplacer l’ancien JLabel si ce joueur existait
Page 36 déjà. Cela met à jour un nouveau problème : comment repérer que le JLabel reçu corres-
pond ou non à un JLabel déjà présent ? Un composant graphique possède un numéro
de rang dans son conteneur (dans son JPanel). C’est ce numéro que l’on va utiliser. Mais
le problème est que lorsqu’on envoie un JLabel, ce dernier ne contient pas ce numéro
qui ne peut être retrouvé que par rapport à son conteneur. Or, à la réception, il n’est
plus possible de trouver le conteneur du JLabel. D’où l’existence, dans le diagramme
de classes, de la classe Label. Cette classe va nous permettre d’affecter un numéro à un
JLabel (son rang dans le conteneur). Ce qui signifie que ce n’est pas un JLabel que l’on
va transférer, mais un objet de type de notre classe Label : on pourra alors récupérer à
l’arrivée le JLabel qu’il contient, ainsi que le numéro, et c’est gagné.

Serveur Client

Qu’est ce qu’on
en fait ?
rang perdu
rang 2 dans jpnJeu On ne sait pas
s’il faut l’ajouter
ou remplacer un
JLabel existant

8 2950 TP PA 00
Serveur Client

objet de type Label

On ajoute (s'il
rang 2 n'existe pas) ou
on remplace (s'il
existe) le JLabel
de rang 2
rang 2

Maintenant que vous savez comment ça va marcher, on va créer la structure des classes
principales qui vont gérer les objets du jeu.

Les classes principales


La classe Label
Dans le package modele, créez la classe Label.
Comme les objets de cette classe doivent être transférés via le réseau, ils seront sériali-
sés. La classe doit donc être sérialisable. Pour cela, implémentez la classe de l’interface
Serializable, et faites à la main (car il ne le trouve pas tout seul) l’import nécessaire :
import java.io.Serializable ;
Déclarez en propriété publique et statique nbLabel qui est un entier. Cette propriété à Travaux pratiques
portée de classe va permettre de mémoriser le numéro du dernier Label ajouté, afin de
gérer le numéro suivant. Pourquoi ne peut-on pas tout simplement récupérer le rang Urban Marginal
du JLabel dans le JPanel ? Parce qu’au moment de la création d’un Label, le JLabel cor-
respondant n’est pas encore créé. Et c’est aussi l’occasion de manipuler une propriété à Page 37
portée de classe…
Déclarez les 2 propriétés privées numLabel (de type entier) et jLabel (de type JLabel,
faites l’import nécessaire) puisqu’un Label va contenir un JLabel et son numéro.
Écrivez le constructeur qui va s’occuper, à partir des paramètres, de valoriser les 2 pro-
priétés privées.
Il ne reste plus qu’à écrire les 2 getters sur les propriétés, afin de pouvoir récupérer leur
contenu.
La classe est terminée.
La classe Objet
Les 3 types d’objets qui vont être manipulés (les joueurs, les murs et les boules) ont des
caractéristiques communes, en particulier leur représentation graphique dans un JLabel
que l’on va donc gérer à travers un objet de type Label. Les caractéristiques communes
vont être mises dans une classe mère que l’on va appeler Objet (et non pas Object, qui
est la super-classe mère de toutes les classes, donc pas de confusion !).
Toujours dans le package modele, créez la classe abstraite Objet. Cette classe est abstraite
parce qu’on ne va jamais l’instancier : on créera des joueurs, des murs et des boules.
Déclarez en protégé les propriétés posX et posY (de type entier) qui contiendront la posi-
tion du JLabel. Pourquoi des variables supplémentaires alors qu’il est possible d’obtenir
la position à partir du JLabel ? Parce qu’on va gérer pas mal de calculs dessus, et ces
calculs doivent se faire parfois sans modifier directement la position du JLabel.

8 2950 TP PA 00
Déclarez aussi en protégé la propriété label de type Label et enfin créez les 3 getters
correspondants.
On va s’arrêter là pour le moment : la classe sera complétée par la suite, suivant les
besoins.

La gestion des murs


La classe Mur
Maintenant que les bases sont construites (la classe Label, la classe Objet), on va pouvoir
enfin s’intéresser aux murs.
Les murs vont être placés dès le départ, de façon aléatoire et dans un nombre fixé en
constante. Commencez par déclarer dans Global la constante :
NBMURS = 20
Profitez-en pour ajouter d’autres constantes nécessaires pour les murs :
CHEMINMURS = CHEMIN + "murs/"
MUR = CHEMINMURS + "mur.gif" // image du mur
H_MUR = 35 // hauteur de l’image
L_MUR = 34 // largeur de l’image
Dans le package modele, créez la classe Mur. Cette classe hérite (extends) de la classe
Objet et implémente l’interface Global.
Créez le constructeur. Les murs devant être placés aléatoirement, on doit calculer le posX
et posY de façon aléatoire, tout en restant dans la taille de l’arène. Pour cela, il existe
Travaux pratiques
la méthode statique random() de la classe Math qui renvoie un réel aléatoire entre 0
et 1 non compris. Il suffit de multiplier ce nombre par la taille de l’arène (sans oublier
Urban Marginal d’enlever la taille du mur, pour éviter de placer un mur juste à l’extérieur). Le résultat
de cette opération donne un nombre réel (type double), il faut le convertir en un entier
Page 38 avec la méthode statique round() de la classe Math (qui s’occupe de convertir ce qui lui
est envoyé en paramètre). Le résultat est de type long (entier long), voilà pourquoi c’est
souligné en rouge. Placez la souris dessus : on vous propose d’ajouter un transtypage.
Faites le : (int) a été ajouté en tête de l’opération. N’oubliez pas que le calcul doit se faire
pour posX et aussi posY.
Toujours dans le constructeur, créez l’objet label (qui provient de la classe mère) en lui
affectant une instance de la classe Label. En paramètre, envoyez -1 comme numéro de
label (on se moque de numéroter les murs : ils seront tous envoyés en une seule fois et
ils ne seront jamais modifiés) et comme 2e paramètre, créez un nouveau JLabel (insérez
l’import nécessaire).
Toujours dans le constructeur, on va tout de suite affecter toutes les caractéristiques
nécessaires au JLabel du mur (centrage, position, image).
Pour centrer horizontalement le contenu d’un JLabel, il faut utiliser la méthode setHori-
zontalAlignment et lui envoyer en paramètre la constante statique CENTER de la classe
SwingConstants. setHorizontalAlignment est une méthode qui doit s’appliquer à un
JLabel, donc, en passant par la propriété label de la classe mère, accédez à son JLabel
(vous avez écrit un getter pour ça).
Pour centrer verticalement, c’est exactement la même chose, mais avec la méthode
setVerticalAlignment.
Définissez la position et la taille du JLabel (vous connaissez la syntaxe : allez la récupérer
dans une des frames), qui doit être positionné à posX et posY, et qui doit faire comme
taille L_MUR et H_MUR.
Enfin, affectez l’image du mur au JLabel (ça aussi, vous savez faire).

8 2950 TP PA 00
La création des murs
Dans la classe JeuServeur, on a besoin d’une collection de murs. C’est une collection
simple : déclarez en privé lesmurs du type ArrayList<Mur> et instanciez directement la
classe.
Dans le constructeur de JeuServeur, ajoutez l’initialisation à 0 de la propriété statique
nbLabel (de la classe Label). Je vous rappelle que nbLabel contiendra le numéro du der-
nier label ajouté, mais uniquement pour les labels modifiables (donc les joueurs et les
boules).
Créez la méthode constructionMurs() qui ne reçoit aucun paramètre et qui va s’occu-
per de générer les murs. Il suffit de faire une boucle sur le nombre de murs (utilisez la
constante de Global, et pensez à implémenter la classe JeuServeur de l’interface Global)
et, dans la boucle, d’ajouter (avec la méthode add) à la collection lesmurs un nouvel
objet de type Mur.
L’affichage des murs côté serveur
Les murs sont créés, cependant ils ne s’affichent pas encore car les JLabels correspon-
dants ne sont pas insérés dans le JPanel des murs.
Pour accéder à la vue, comme d’habitude on va passer par le controleur et lui envoyer
l’ordre d’intégrer un nouveau mur dans le JPanel des murs.
Allez dans la classe Controle. Pour le moment, elle ne gère que des événements prove-
nant de la vue. On va commencer à gérer des événements provenant du modele, avec la
même logique de traitement.
Créez la méthode publique evenementModele qui reçoit 3 paramètres : unjeu de type
Travaux pratiques
Object (pour savoir si c’est JeuServeur ou JeuClient qui est à l’origine de la demande),
ordre de type String (qui contiendra une chaîne d’information précisant l’ordre, donc la Urban Marginal
demande à traiter) et info de type Object qui contiendra éventuellement l’objet à traiter.
Dans cette méthode, faites un premier test pour contrôler si unjeu est une instance de la Page 39
classe JeuServeur. Si c’est le cas, appelez la méthode evenementJeuServeur (non encore
écrite mais ça va venir tout de suite après) et envoyez en paramètre de la méthode, ordre
et info.
Écrivez la méthode evenementJeuServeur qui reçoit donc 2 paramètres (ordre et info).
On va s’occuper de gérer un ordre qui demande d’ajouter un nouveau mur dans l’arène.
Dans la méthode, faites un test sur ordre pour voir s’il contient la chaîne « ajout mur ».
Si c’est le cas, on est censé appeler une méthode dans la frame Arene qui permet d’ajou-
ter un mur. Cette méthode n’existant pas encore, on va tout de suite la créer. Allez dans
Arene et créez la méthode publique ajoutMur qui reçoit en paramètre un objet de type
JLabel (donc un mur) et qui l’ajoute au JPanel des murs. Juste après, faites un repaint du
JPanel (méthode repaint() ).
Revenez à la classe Controle dans la méthode evenementJeuServeur. Vous allez pouvoir
compléter le test en mettant à l’intérieur, l’appel de la méthode ajoutMur : envoyez en
paramètre info (qui contient le JLabel du mur) en le transtypant en JLabel puisqu’il est
pour le moment de type Object.
L’événement étant maintenant géré, on va pouvoir l’exploiter. Revenez dans la classe
JeuServeur, méthode constructionMurs et, dans la boucle, après avoir ajouté un mur à la
collection, appelez sur l’objet controle, la méthode evenementModele en envoyant les
bons paramètres (réfléchissez, sachant qu’avec la méthode get sur une collection, vous
pouvez récupérer l’élément qui se trouve à l’indice passé en paramètre et qu’à partir de
cet élément, il faut récupérer son jLabel).

8 2950 TP PA 00
Maintenant que toutes les méthodes sont écrites pour construire les murs et les afficher
côté serveur, il reste à appeler la méthode constructionMur au bon endroit. Pour le
jeu côté serveur, il faut l’appeler au moment où l’arène est créée : allez dans la classe
Controle, méthode evenementEntreeJeu, partie " serveur ". Juste après avoir instancié
Arene et avant de l’afficher, appelez la méthode constructionMurs sur l’objet lejeu (après
l’avoir transtypé en JeuServeur).

Il y a bien longtemps que l’on n’a pas fait de test. Lancez simplement le serveur pour
constater que les murs s’affichent. Faites plusieurs tests à la suite pour contrôler que les
murs se positionnent bien aléatoirement. Vérifiez aussi qu’il y a bien 20 murs.

Les murs sont parfois superposés. C’est normal : on n’a pas géré les collisions. Ça aurait
fait beaucoup de problèmes d’un coup ! La superposition des murs n’est pas vraiment
un problème. On s’occupera du problème des collisions entre autres quand les joueurs
se déplaceront (pour éviter de traverser un mur).

La gestion des joueurs


La classe Joueur
On aimerait que les murs s’affichent côté clients. Mais pour le moment les joueurs ne
sont pas gérés du côté du serveur. Comme pour les murs, il va falloir gérer une collection
de joueurs. Cependant la collection sera un peu plus complexe.
Mais avant tout, dans le package modele, créez la classe Joueur qui hérite de la classe
Objet et qui implémente l’interface Global. Créez un constructeur vide. La classe sera
remplie plus tard.
Travaux pratiques
Le dictionnaire de joueurs
Urban Marginal
Dans la classe JeuServeur, déclarez en privé la propriété lesjoueurs du type
Hashtable<Connection, Joueur> et instanciez directement la classe. Faites l’import néces-
Page 40
saire pour le Hashtable (prenez l’import java.util). Le Hashtable va permettre de gérer
un dictionnaire, donc avec une clé : la clé sera de type Connection. Donc on va repérer
chaque joueur par sa connection, ce qui sera bien pratique pour le contacter.
À quel moment ajouter un nouveau joueur dans le dictionnaire ? Quand un nou-
veau client se connecte. Rappelez-vous, dans la classe Connection, lors d’une nouvelle
connexion, on fait appel à la méthode setConnection pour informer le jeu. Allez dans la
classe JeuServeur : elle possède la méthode redéfinie setConnection qui pour le moment
est vide. Dans cette méthode, ajoutez au dictionnaire lesjoueurs (utilisez la méthode put
pour ajouter) un nouveau joueur, en mettant en clé du dictionnaire, l’objet connection
reçu en paramètre de setConnection.
En réalité, la classe Connection ne fait pas appel directement au setConnection de la
classe jeu, mais il passe par le controleur. Allez dans la classe Controle, méthode setCon-
nection. Pour le moment cette méthode se contente de valoriser la propriété connection.
Lorsque le client se connecte au serveur, côté JeuClient on a déjà géré l’initialisation de
la connexion (regardez l’appel de setConnection dans la méthode evenementEntreeJeu,
partie " client "). Mais côté serveur, la méthode setConnection de la classe JeuServeur
n’a pas été encore sollicitée. Il faut qu’elle soit appelée dès qu’il y a une connexion d’un
client, donc directement dans la méthode setConnection de la classe Controle. Dans cette
méthode, après la valorisation de la propriété connexion, faites un test pour contrôler si
la propriété lejeu est une instance de JeuServeur. Si c’est le cas, alors appelez la méthode
setconnection sur l’objet lejeu en lui envoyant en paramètre connection.

8 2950 TP PA 00
Les murs côté client
Les murs s’affichent côté serveur mais pas encore côté client. Il faut donc gérer l’envoi de
cette information au client. On va directement envoyer le JPanel complet.
Cela suppose que dans la frame Arene, il doit y avoir une méthode qui est capable de
réceptionner un JPanel et de transférer tout son contenu dans le JPanel des murs. Dans la
classe Arene, créez la méthode ajoutPanelMurs qui reçoit en paramètre un objet de type
JPanel et qui ajoute cet objet au panel des murs (utilisez la méthode add sur l’objet jpn-
Murs). Après cet ajout, il faut redessiner le JPanel : utilisez la méthode repaint(). Enfin,
redonnez le focus au jContentPane.
Maintenant, il faut que le controleur gère un ordre qui viendra du serveur, qui lui
demandera d’envoyer le panel des murs au client. Dans la classe Controle, méthode
evenementJeuServeur, faites un nouveau test sur ordre pour contrôler s’il contient la
chaîne " envoi panel murs ". Si c’est le cas, alors appelez la méthode envoi sur l’objet
lejeu (après l’avoir transtypé en JeuServeur) et envoyez en 1er paramètre info (transtypé
en Connection car il contiendra l’objet connection pour contacter le client) et en second
paramètre le JPanel des murs récupérés dans la frame Arene avec getJpnMurs (dans
Arène, mettez cette méthode en public pour pouvoir y accéder). Vous vous demandez
peut-être pourquoi on ne gère pas l’envoi directement dans la classe JeuServeur : parce
que dans la classe JeuServeur, on n’a pas accès à la vue, et donc au jpnMurs. Voilà pour-
quoi il faut faire une demande d’envoi au controleur.
Retournez dans la classe JeuServeur, dans setConnection. Après avoir ajouté le joueur au
dictionnaire, faites appel à la méthode evenementModele sur l’objet controle. Mettez
les paramètres nécessaires (ce n’est pas difficile à trouver).
Travaux pratiques
Lorsque le client recevra ce panel, il fera appel au controleur pour lui demander d’ajou-
ter ce panel dans l’arène. On va écrire cette méthode. Allez dans la classe Controle. Urban Marginal
Dans la méthode evenementModele, ajoutez un second test : si unjeu est une instance
de JeuClient, alors faites appel à la méthode evenementJeuClient (qui sera écrite juste Page 41
après) en envoyant en paramètre, ordre et info.
Écrivez la méthode evenementJeuClient. Dans cette méthode, faites un test sur ordre
pour contrôler s’il contient " ajout panel murs ". Si c’est le cas, appelez la méthode
ajoutPanelMurs que vous venez d’écrire dans la classe Arene. Envoyez en paramètre info
transtypé en JPanel.
Il faut maintenant que le jeu côté client réceptionne l’information provenant du serveur
et sollicite le controleur pour l’ajout du panel. Allez dans la classe JeuClient, méthode
reception qui est pour le moment vide. Faites un test sur info : si c’est une instance
de JPanel alors on vient de recevoir le JPanel des murs. Dans ce cas, faites appel à la
méthode evenementModele sur la propriété controle. À vous de trouver les paramètres.

Lancez un serveur : les murs s’affichent. Lancez un client : normalement les murs s’af-
fichent aussi. Contrôlez qu’ils sont aux mêmes positions que le serveur.

La gestion des clients côté serveur


L’enregistrement des informations du client
On s’est occupé de gérer l’arrivée d’un nouveau joueur et donc son ajout dans le dic-
tionnaire, mais pour le moment, l’arrivée d’informations provenant d’un joueur n’est
pas traitée. Autant le serveur va envoyer au client des types d’objets différents (JPanel,

8 2950 TP PA 00
JLabel…), autant le client va se contenter d’envoyer au serveur des chaînes d’informa-
tions, qui commenceront toujours par un code spécial pour reconnaître le type d’infor-
mation envoyée. Vous avez déjà géré de la part du client, l’envoi du choix du pseudo et
du numéro de personnage. Il faut maintenant gérer la réception de ces informations.
Dans la classe JeuServeur, allez dans la méthode reception qui est pour le moment
vide. La première étape va consister en la décomposition de l’information. La méthode
split sur les objets de type String permet de décomposer une chaîne par rapport à un
caractère de séparation, et de retourner un tableau de chaînes. Donc, déclarez infos de
type String[] (donc un tableau) et affectez lui l’appel de la méthode split sur info (pré-
alablement transtypé en String) en utilisant la constante SEPARE comme caractère de
séparation.
Juste après, faites un switch sur la première case du tableau. Attention, cette case
contient un numéro mais qui est au format String. Or le switch n’accepte que des entiers.
Vous ne pourrez pas directement transtyper un String en int : utilisez la méthode sta-
tique parseInt de la classe Integer qui va permettre de transformer en int le contenu de
infos|0]. Dans le switch, faites un premier case avec PSEUDO. Dans le cas où le message
commence par la valeur PSEUDO, c’est qu’un nouveau client vient d’envoyer le choix de
son pseudo et du numéro de personnage. N’oubliez pas de mettre "break;" à la fin de
chaque case, juste avant la case suivante.
Pour prendre en compte ces informations, il va falloir compléter un peu la classe Joueur.
Dans la classe Joueur, ajoutez les propriétés privées pseudo (String) et numPerso (int).
Écrivez la méthode initPerso() qui reçoit 2 paramètres et qui valorise avec ces paramètres
les 2 propriétés privées. Cette méthode va aussi s’occuper de générer les Labels néces-
Travaux pratiques saires (le Label du personnage et celui qui s’affichera juste en dessous et qui contiendra
le pseudo et la vie). Comme la classe Joueur hérite de la classe Objet, elle a déjà une
Urban Marginal
propriété de type Label qui contiendra le personnage. Il faut un second Label pour l’affi-
chage du pseudo et de la vie : en propriété privée de la classe Joueur, déclarez un objet
Page 42
message de type Label. Écrivez aussi son getter.
Dans initPerso, après les 2 valorisations, créez la propriété label en lui affectant une
instance de la classe Label. Le premier paramètre attendu doit être le numéro du Label :
envoyez la propriété statique nbLabel de la classe Label, et incrémentez là juste après.
En second paramètre, créez un objet de type JLabel. Centrez horizontalement et verti-
calement le JLabel qui se trouve dans le Label que vous venez de créer. De même, créez
la propriété message avec la même logique, mais en ne faisant ensuite qu’un centrage
horizontal. On va aussi choisir une police et taille d’écriture pour le message : utilisez la
méthode setFont sur le JLabel de message, et envoyez en paramètre une instance de la
classe Font avec les 3 paramètres suivants :
("Dialog", Font.PLAIN, 8)

Retournez dans la classe JeuServeur, méthode reception, dans le case sur PSEUDO.
Maintenant vous pouvez appeler la méthode initPerso sur le joueur concerné (récupé-
rez le joueur dans le dictionnaire, avec la méthode get, en utilisant la clé connection)
et envoyez infos[1] en premier paramètre et infos[2] transformé en entier, en second
paramètre.
Le positionnement aléatoire du personnage dans l’arène
Il faut maintenant calculer un positionnement aléatoire du personnage. À la différence
des murs, il va falloir faire en sorte que le personnage ne touche pas un mur ou un autre.

8 2950 TP PA 00
Comme on aura plusieurs fois l’occasion de contrôler des collisions entre objets (quand
les boules vont être tirées, quand les joueurs vont se déplacer…) on va utiliser une
méthode qui va marcher dans tous les cas, et qui sera donc dans la classe Objet. L’idée est
de contrôler l’Objet courant (quel qu’il soit) avec un autre Objet. Il suffit alors de contrô-
ler les positions de chacun, de les comparer pour voir si les 2 objets se chevauchent. Bon,
pour éviter de perdre trop de temps sur la recherche de cette méthode (qui représente
tout de même une jolie petite réflexion), je vous la donne. Mettez cette méthode dans
la classe Objet.
//--- Un objet touche un autre objet ---
public boolean toucheObjet (Objet objet) {
if (objet.label==null) {
return false ;
}else{
if (objet.label.getJLabel()==null) {
return false ;
}else{
int l_obj = objet.label.getJLabel().getWidth() ;
int h_obj = objet.label.getJLabel().getHeight() ;
int l_this = this.label.getJLabel().getWidth() ;
int h_this = this.label.getJLabel().getHeight() ;
return(!((this.posX+l_this<objet.posX ||
this.posX>objet.posX+l_obj) ||
(this.posY+h_this<objet.posY ||
this.posY>objet.posY+h_obj))) ; Travaux pratiques
}
} Urban Marginal
}
Page 43
Dans la classe Joueur, il faut créer une méthode qui va générer la position aléatoire
du joueur. Mais auparavant, il faut, dans la même classe, créer des méthodes qui vont
contrôler la collision d’un objet avec une collection d’autres objets (pour vérifier par
exemple qu’un personnage ne touche pas un des murs, ou un des autres personnages).
Dans la classe Joueur, créez la méthode privée booléenne toucheJoueur qui reçoit en
paramètre lesjoueurs (le dictionnaire de joueurs). Dans la méthode, il faut boucler sur le
dictionnaire : utilisez le principe " pour chaque ", dont voici la syntaxe :
for (UneClasse unObjet : uneCollection)
La boucle va parcourir la collection et récupérer chaque objet. Dans un dictionnaire, pour
récupérer la collection de valeurs, utilisez la méthode values() sur le dictionnaire.
Sachant cela, faites une boucle pour parcourir les joueurs compris dans le dictionnaire
de joueurs et, pour chaque joueur, commencez par contrôler que le joueur récupéré
n’est pas le même que le joueur actuel (l’instance actuelle de la classe) : pour faire ce
test, appliquez la méthode equals sur l’objet de type Joueur, récupéré, et envoyez en
paramètre this. Si le joueur récupéré n’est pas identique, alors appelez la méthode tou-
cheObjet de la classe mère, en envoyant en paramètre le joueur récupéré. Si la méthode
retourne vraie, il y a collision, alors retournez directement true. Après la boucle, si on en
est arrivé là, c’est qu’on n’a trouvé aucune collision, alors retournez false.
Avec la même logique, créez la méthode toucheMur à quelques différences près : elle
reçoit en paramètre la collection de murs, inutile d’appliquer la méthode values (vous

8 2950 TP PA 00
pouvez directement utiliser la collection), inutile de contrôler l’égalité avec le joueur
puisqu’un joueur ne peut pas être un mur !
Maintenant il est possible de créer la position du personnage : créez la méthode privée
premierePosition qui reçoit en paramètre le dictionnaire de joueurs et la collection de
murs. Dans cette méthode, commencez par affecter une taille au JLabel du personnage
(mettez le en coordonnées 0, 0 mais donnez lui la taille L_PERSO, H_PERSO). La définition
de la taille est indispensable pour gérer les collisions.
Après cela, faites une boucle du type do/while pour que la condition soit à la fin :
do {
traitements ;
}while(condition) ;
Dans la boucle, générez aléatoirement une valeur à posX et posY, de sorte que cette
valeur positionne le personnage dans l’arène. Horizontalement, n’oubliez pas d’enlever
L_PERSO à L_ARENE. Verticalement, il faut non seulement enlever la hauteur du person-
nage (H_PERSO) mais aussi la hauteur du message (déclarez la constante H_MESSAGE =
8 dans l’interface Global).
La condition pour rester dans la boucle est la suivante : il faut boucler tant que l’instance
actuelle touche un mur ou un autre joueur. Sachant cela et en utilisant les 2 méthodes
qui viennent d’être écrites, écrivez la condition.
Il ne reste plus qu’à solliciter la méthode premierePosition au bon endroit. À la fin de
la méthode initPerso, appelez la méthode premierePosition : mais vous n’avez pas le
dictionnaire de joueurs et la collection de murs ! Ajoutez ces 2 paramètres à initPerso et
Travaux pratiques faites la modification nécessaire dans JeuServeur (en envoyant ces 2 paramètres supplé-
mentaires lors de l’appel de initPerso).
Urban Marginal
L’affichage du personnage dans l’arène côté serveur
Page 44 Pour que l’affichage du personnage se fasse, il faut commencer par avoir une méthode
qui va s’en occuper, dans la classe Arene. Dans la classe Arene, créez la méthode ajou-
tJoueur qui fonctionne comme ajoutMur excepté qu’elle ajoute le paramètre dans le
JPanel jpnJeu. La méthode servira aussi bien pour ajouter le personnage que son mes-
sage.
Cette méthode doit être sollicitée par le controleur. Dans la classe Controle, méthode
evenementJeuServeur, ajoutez un test sur ordre. Si ordre contient « ajout joueur », alors
appelez la méthode ajoutJoueur que vous venez d’écrire, et envoyez en paramètre, info
après l’avoir transtypé en JLabel.
C’est JeuServeur qui doit solliciter le controleur pour ajouter le joueur dans la vue. Dans
la classe JeuServeur, écrivez la méthode nouveauLabelJeu qui reçoit en paramètre label
de type Label. Cette méthode appelle sur la propriété controle, la méthode evene-
mentModele. Trouvez les paramètres à envoyer. Cette méthode va donc envoyer vers le
controleur soit le Label du personnage, soit le Label du message. À quel endroit solliciter
cette méthode ? Au moment où les Labels sont créés : allez dans la classe Joueur. Pour
appeler des méthodes de JeuServeur à partir de cette classe, cela suppose qu’elle doit
posséder une instance de la classe JeuServeur : déclarez en propriété privée jeuServeur
du type JeuServeur. Faites en sorte que le constructeur de la classe reçoive une instance
de JeuServeur et valorise la propriété privée correspondante (du coup pensez aussi à
envoyer l’instance de JeuServeur au Joueur lorsque, dans JeuServeur, vous instanciez
Joueur).

8 2950 TP PA 00
Dans la classe Joueur, méthode initPerso, juste après les 3 lignes de code qui créent et
modifient la propriété label (qui va contenir le personnage) appelez la méthode nou-
veauLabelJeu de JeuServeur, en lui envoyant label en paramètre (pour que ce label soit
ajouté à l’arène). En fin de méthode initPerso, après les 3 lignes de code qui gèrent cette
fois la propriété message, faites de même mais en envoyant message en paramètre.
Les JLabels correspondants sont ajoutés mais pour le moment ils ne contiennent rien. On
va donc gérer leur remplissage, et du coup leur affichage. Mais pour gérer l’affichage, il
est nécessaire d’avoir quelques propriétés supplémentaires. Dans la classe Joueur, il faut
ajouter les propriétés suivantes :
vie (entier) // vie restante du joueur
orientation (entier) // tourné vers la gauche (0) ou vers la droite (1)
etape (entier) // numéro d’étape dans l’animation

Pour le moment, initialisez la vie à 10 dans le constructeur, la propriété etape à 1 et


l’orientation à DROITE.
Créez la méthode affiche qui reçoit en paramètre etat (String qui contiendra l’état du
joueur : marche, blessé ou mort), et etape (entier qui contiendra le numéro d’étape
dans l’animation : cette étape étant parfois à gérer de l’extérieur en particulier quand le
joueur est blessé, d’où la nécessité du paramètre).
Dans cette méthode, on va s’occuper des 2 JLabels (le personnage et le message). Sur
le premier JLabel (que vous allez récupérer dans la propriété label, avec la méthode
getJLabel), vous allez le positionner et dimensionner avec setBounds, comme vous savez
déjà faire (posX et posY contiennent le positionnement, L_PERSO et H_PERSO le dimen- Travaux pratiques
sionnement), puis vous lui affectez l’image voulue en concaténant correctement le nom
(regardez comment vous avez fait dans ChoixJoueur et cette fois utilisez les propriétés Urban Marginal
privées et les paramètres de affiche pour correctement reconstruire le nom). Pour le
second JLabel (le message), positionnez et dimensionnez correctement (prévoyez que Page 45
le message soit de 10 pixels plus large à gauche et à droite que le personnage, de plus
le message doit s’afficher juste en dessous du personnage), puis affectez-lui, avec la
méthode setText, le texte voulu (en concaténant le pseudo, puis " : " et la vie).
Reste à savoir quand appeler cette méthode affiche. En fait, il faut afficher lors de
l’initialisation du personnage, et aussi à chaque fois qu’il y a une action. Dans la classe
Joueur, à la fin de la méthode initPerso, appelez la méthode affiche en envoyant comme
paramètres MARCHE et etape. En ce qui concerne l’appel d’affiche après une action, on
s’en occupera plus tard.
Avant de lancer un test, on va faire une petite modification de code : vous aviez placé
l’envoi des murs dans la méthode setConnection de JeuServeur. C’est un peu tôt et cela
peut parfois provoquer des erreurs côté client (si la frame n’est pas créée à temps vu
qu’elle se crée en même temps que la génération de la connexion). Le mieux est d’en-
voyer les murs au moment où le client est correctement enregistré : déplacez donc cette
ligne de code dans le case PSEUDO de la méthode reception (en tête du case).
Lancez un serveur puis un client. Sélectionnez un personnage, tapez un pseudo puis entrez
dans l’arène. Regardez l’arène du serveur : elle contient le personnage choisi, et en dessous,
vous pouvez voir le pseudo suivi de 10 pour la vie. Refaites un test complet pour contrôler
que le personnage se met bien à un endroit différent et qu’il ne se positionne pas sur un
mur.
Ça marche côté serveur, mais le personnage n’apparaît pas encore côté client.

8 2950 TP PA 00
Le transfert des informations vers le client
Le transfert du personnage pour l’affichage côté client
On pourrait se contenter d’envoyer les 2 Labels au client concerné. Mais s’il y a plusieurs
clients, ils doivent tous être informés de l’arrivée du nouveau joueur. Donc, excepté le cas
particulier d’un nouveau client qui se connecte à qui on envoie juste à lui le JPanel des
murs, à chaque fois que le serveur envoie une information, il faut qu’il l’envoie à tous les
clients en même temps. Il manque une méthode qui gère cet envoi multiple.
Dans la classe JeuServeur, surchargez la méthode envoi (déjà créée dans la classe mère)
mais qui ne doit avoir qu’un paramètre : info de type Object (l’objet d’information à
envoyer). Dans la méthode, faites une boucle (de type pour chaque) afin de récupérer les
clés du dictionnaire des joueurs (utilisez la méthode keySet() sur le dictionnaire). Dans la
boucle, appelez la méthode envoi de la classe mère pour envoyer, avec la clé récupérée,
l’objet info reçu en paramètre.
Quand doit-on envoyer les Labels du joueur à tout le monde ? À chaque fois qu’il y a
modification (ou création !) du Label, donc juste après l’affichage. Dans la classe Joueur,
fin de méthode affiche, après avoir affiché les Labels du joueur, envoyez-les à tous (faites
2 envois).
Le serveur envoie mais le client ne réceptionne pas encore. Allez dans la classe JeuClient,
méthode reception. Ajoutez un test pour contrôler cette fois si l’objet reçu est de type
Label. Si c’est le cas, il va falloir solliciter le controleur pour qu’il demande à la vue d’affi-
cher ce Label. On a donc d’autres méthodes à écrire avant de compléter celle-ci.
Dans la classe Arene : surprise, on a déjà la méthode ajoutJoueur qui permet d’ajouter
Travaux pratiques un Label de personnage et de message. Normal, cette méthode nous a servi à afficher
le personnage côté serveur. Mais malheureusement cette méthode ne convient pas pour
Urban Marginal
le client. En effet, côté serveur, on ajoute un JLabel, puis la méthode affiche se charge
de modifier l’affichage du JLabel. Côté client, on reçoit le JLabel et, soit il n’existe pas
Page 46
encore dans le JPanel, dans ce cas il faut l’ajouter, soit il existe et dans ce cas il faut le
supprimer avant de l’ajouter à nouveau. Donc, dans la classe Arene, créez la méthode
ajoutmodifJoueur qui reçoit 2 paramètres : num de type entier (numéro de rang qui
sera récupéré dans l’objet Label reçu et qui contient le rang du JLabel dans le JPanel)
et unlabel de type JLabel. Dans cette méthode, on va commencer par tenter de sup-
primer le JLabel qui se trouve à l’indice indiqué : sur le jpnJeu, appliquez la méthode
remove en lui envoyant en paramètre num. La suppression ne va marcher que s’il existe
un JLabel à ce rang. Sinon, ça va planter. Le but étant que ça ne plante pas, vous allez
entourer ce remove d’un try/catch. Du coup l’erreur sera juste capturée. Quoi mettre en
paramètre du catch ? un objet ex de type ArrayIndexOutOfBoundsException. Le nom
est assez explicite ! Dans le catch (dans les accolades) ne mettez rien. Le but est juste de
supprimer s’il existe, sinon il ne se passe rien. Après le catch, ajoutez le JLabel reçu en
paramètre au JPanel du jeu (attention, l’ajout doit se faire au bon rang, donc mettez 2
paramètres à l’ajout, le JLabel, et en second paramètre, num). Enfin, faites un repaint
du JPanel jpnJeu.
Dans la classe Controle, dans evenementJeuClient, ajoutez le même test que vous aviez
fait pour evenementJeuServeur avec l’ordre " ajout joueur ". Dans le test, faites appel
à la méthode ajoutmodifJoueur de Arene et envoyez en premier paramètre le numéro
récupéré dans info (après l’avoir transtypé en Label) et en second paramètre le JLabel
récupéré dans info.

8 2950 TP PA 00
Enfin, revenez dans la classe JeuClient, méthode reception, dans le test si l’information
reçue est un Label, faites appel à la méthode evenementModele sur le controleur et
envoyez lui les bons paramètres pour ajouter le Label à la vue.
Faites un test complet : le personnage s’affiche côté serveur, mais, déception, toujours
pas côté client. Si vous avez la chance de le voir aussi côté client (ce qui arrive parfois
car l’erreur est un peu aléatoire !) dites vous que le problème serait intervenu plus tard.
Pourquoi ? On va faire un autre test… Dans Arene, ajoutez un affichage console dans
ajoutmodifJoueur (affichez directement le contenu du JLabel) puis refaites un test. Le mes-
sage s’affiche dans la console et on reconnaît même les informations du JLabel. Si tout va
bien, on a même 2 affichages (pour le personnage et le message). Mais alors pourquoi ça
ne s’affiche pas ?!!! Pourquoi cela a marché pour les murs et pas les joueurs ?

Ce petit problème m’a bloqué pendant 2 jours… La solution est la suivante : allez dans
la classe Connection, méthode envoi, et juste avant la ligne qui envoie l’information
(writeObject), écrivez la ligne suivante :
this.out.reset()
En faisant un reset sur le canal d’envoi, vous « nettoyez » ce canal pour l’envoi suivant.

Faites un nouveau test complet. Ça marche ! Faites un test avec cette fois 2 clients. Les
2 personnages s’affichent correctement côté serveur et chez le premier client. En revanche,
seuls les murs s’affichent pour le second client, puis l’appli provoque une erreur.

Cette erreur est normale : le second client reçoit son personnage qui a déjà un numéro 2
alors qu’il n’a pas reçu les personnages précédents. Il faut donc maintenant s’occuper, à
l’arrivée de chaque nouveau client, de lui envoyer les Labels de tous les anciens joueurs. Travaux pratiques

Le transfert de tous les joueurs déjà présents dans l’arène pour l’affi- Urban Marginal
chage côté client
Dans la classe JeuServeur, méthode reception, case PSEUDO, après avoir envoyé le JPanel Page 47
des murs au nouveau joueur et avant l’appel de initPerso, on va envoyer tous les JLabels
des joueurs précédents à ce nouveau joueur.
Mais là, on est confronté à un problème délicat : les labels doivent absolument arriver
dans l’ordre de leur création. Or, s’ils sont envoyés dans l’ordre du dictionnaire, celui-
ci étant trié sur sa clé, donc sur l’objet connection, rien ne dit que ce sera l’ordre de
création. Pour résoudre ce problème et simplifier au maximum les manipulations, on
va créer une simple collection de joueurs, en parallèle du dictionnaire, juste pour avoir
les références des joueurs dans l’ordre. Dans la clase JeuServeur, déclarez en propriété
privée lesJoueursDansLordre de type ArrayList de Joueur et instanciez directement. Dans
la méthode reception, case PSEUDO, juste après avoir appelé la méthode initPerso sur le
nouveau joueur du dictionnaire, puisque vous venez d’initialiser un personnage (et de
créer ses labels) insérez ce nouveau joueur dans la collection lesJoueursDansLordre avec
la ligne suivante :
this.lesJoueursDansLordre.add(this.lesjoueurs.get(connection)) ;

Toujours dans la même méthode, cette fois juste avant l’appel de initPerso, il est main-
tenant possible d’envoyer tous les JLabels des joueurs précédents au nouveau joueur.
Faites une boucle (pour chaque) sur la collection lesJoueursDansLordre. Dans la boucle,
appelez 2 fois la méthode envoi de la classe mère, en mettant connection en premier
paramètre, et le Label du personnage et le Label du message du joueur de la boucle, en
second paramètre.

8 2950 TP PA 00
C’est parti pour un nouveau test : un serveur, 2 clients. Normalement les 2 personnages
s’affichent dans les 3 frames.

10. Le t'chat
Avant de s’occuper des personnages dans l’arène (déplacements, attaques…) on va rapi-
dement s’occuper du t’chat qui est très facile à gérer.

La saisie uniquement côté client


D’abord, on va supprimer l’affichage de la ligne de saisie qui se trouve dans la frame du
serveur. Pour cela, dans la classe Arene, déclarez en privé le booléen client. Cette pro-
priété permettra de savoir si c’est l’arène du client ou non.
Dans le constructeur, recevez en paramètre typejeu de type String. On enverra la chaîne
" serveur " ou " client " au moment de la création de la frame. Dans le constructeur,
après le super(), valorisez la propriété client avec true si typejeu contient " client ", false
sinon (ça peut s’écrire en une ligne…).
Dans la méthode getJContentPane, entourez d’une condition l’ajout de getTxtSaisie au
jContentPane (l’ajout ne doit se faire que si client est à true). Dans la classe Controle, lors
de chaque instanciation de la classe Arene, mettez en paramètre " client " ou " serveur "
Travaux pratiques suivant le cas.

Urban Marginal
Faites un test pour contrôler que la zone de saisie ne s’affiche plus côté serveur, mais est
toujours présente chez le client.
Page 48

L’envoi de la phrase au serveur


Gestion de l’événement sur la zone de saisie
Lorsqu’un client saisit une phrase, elle doit être traitée lors de la validation dans la zone
de saisie. Il faut donc gérer cet événement.
Suite aux modifications sur la frame Arene, la zone de visualisation n’est plus exploi-
table. Ce n’est pas grave : on va gérer le code en partie à la main. Allez dans la frame
EntreeJeu, méthode getCmdServeur, par exemple, et copiez le code de l’événement de la
souris (les 5 lignes de code) pour les coller dans la frame Arene, méthode getTxtSaisie, à la
fin du if. Remplacez cmdServeur par txtSaisie. Remplacez addMouseListener par addKey-
Listener (c’est un événement du clavier qui nous intéresse). Remplacez MouseAdapter
par KeyAdapter. Remplacez mouseClicked par keyPressed. Remplacez MouseEvent par
KeyEvent et enfin remplacez cmdServeur_clic() par txtSaisie_keyPressed(e) (cette fois on
récupère e pour savoir quelle touche a été utilisée). Ajoutez les 2 imports nécessaires et
écrivez la méthode privée txtSaisie_keyPressed. Elle est vide pour le moment.
Envoi de la phrase
Pour envoyer la phrase, il va falloir passer par le controleur. D’une manière générale,
à partir de l’arène, seul le client a des choses à envoyer, et ce sera toujours une chaîne
d’information.

8 2950 TP PA 00
Allez dans la classe Controle, méthode evenementVue, ajoutez un 3e test dans la même
logique que les 2 précédents. La méthode evenementArene qui va être sollicitée doit
attendre en paramètre l’objet à transférer, donc info. Écrivez ensuite la méthode privée
evenementArene. Cette méthode se contente d’appeler la méthode envoi sur l’objet
lejeu (après l’avoir transtypé en JeuClient) et de mettre en paramètre info (l’objet à
envoyer).
Pour pouvoir passer par le controleur, l’arène doit posséder une instance de Controle.
Comme vous l’avez déjà fait pour les 2 autres frames, déclarez en privé controle de type
Controle, valorisez cette propriété dans le constructeur de Arene, qui doit donc recevoir
aussi en paramètre une instance de Controle. Pensez aussi à envoyer cette instance au
moment de la création de la frame (création qui est dans la classe Controle).
De retour dans la classe Arene, méthode txtSaisie_keyPressed, dans cette méthode faites
un test : il ne faut envoyer l’information que si la touche pressée est la touche valida-
tion. Comment faire ? Utilisez la méthode getKeyCode sur le paramètre : cette méthode
retourne le code de la touche. Comparez ce code avec le code de la touche validation,
obtenu avec le constante statique VK_ENTER de la classe KeyEvent. Si le teste est vérifié,
alors appelez la méthode evenementVue du controleur en lui envoyant entre autre le
message à transférer au serveur. Mais que contient ce message ? On a déjà envoyé un
message au serveur (le pseudo…) en faisant démarrer le message avec un code pour que
le serveur repère que c’était un pseudo. De même, dans l’interface Global, rajoutez la
constante CHAT = 1 (comme vous aviez mis PSEUDO = 0) pour utiliser ce code de repé-
rage. De retour où vous étiez dans Arene, construisez le message à envoyer en conca-
ténant CHAT, le caractère de séparation (SEPARE) puis le contenu de la zone de texte
txtSaisie. Ensuite, videz le contenu de txtSaisie et redonnez le focus à jContentPane. Travaux pratiques

La réception de la phrase Urban Marginal

Gestion de la réception et affichage côté serveur


Page 49
La phrase est envoyée, il faut maintenant que le serveur la reçoive. Encore une fois, on
va commencer par la fin.
Il faut que l’arène possède une méthode qui permette de mettre à jour la zone d’affi-
chage de la conversation. Allez dans la classe Arene et créez la méthode ajoutChat qui
reçoit en paramètre unephrase de type String. Cette méthode modifie le contenu de
txtChat (avec la méthode setText). Dans le contenu, concaténez la phrase reçue en para-
mètre, avec la chaîne " \r\n " (pour gérer un retour à la ligne) suivi de l’ancien contenu
de txtChat.
Dans la classe Controle, méthode evenementJeuServeur, ajoutez un test sur ordre, en
contrôlant s’il contient " ajout phrase " . Dans ce cas, faites appel à la méthode ajoutChat
de la frame Arene en lui envoyant info transtypé en String.
Pour constituer correctement la phrase (qui va commencer par le pseudo du client qui l’a
écrite, suivi du signe " > ") il faut pouvoir récupérer son pseudo. Dans la classe Joueur,
rajoutez un getter sur le pseudo.
Enfin, dans la classe JeuServeur, méthode reception, ajoutez un case (CHAT). Dans ce
case, déclarez la variable laphrase de type String, et affectez lui la concaténation du
pseudo du joueur concerné, avec " > ", suivi de la phrase reçue (qui se trouve dans
infos[1]). Appelez ensuite la méthode evenementModele du controleur en lui envoyant
les bons paramètres (pour ajouter la phrase dans la vue).

8 2950 TP PA 00
Lancez un serveur puis un client. Tapez un message dans la zone de texte et validez. Le
message s’est affiché côté serveur, précédé du pseudo. Tapez un autre message et validez.
Vous pouvez voir maintenant côté serveur les 2 messages (ils sont volontairement dans
l’ordre inverse pour voir en permanence le dernier message saisi).
En revanche, les messages n’apparaissent pas côté client. On va s’en occuper.
Transfert vers tous les clients et affichage côté client
Au niveau de l’envoi, le but n’est pas que d’envoyer la phrase mais le contenu complet de
txtChat. Cela aura l’avantage que tout le monde aura le même affichage : pas de risque
de décalage s’il y a des problèmes c/s et surtout un nouvel arrivant aurait l’affichage
de la conversation complète. Le mieux est donc d’envoyer au moment où le controleur
s’occupe d’ajouter la phrase dans la vue.
Dans la classe Controle, méthode evenementJeuServeur, partie « ajout phrase », après
avoir sollicité l’arène, appelez la méthode envoi sur lejeu (après l’avoir transtypé en
JeuServeur) et envoyez en paramètre le contenu de txtChat de l’arène (attention, met-
tez au préalable en public la méthode getTxtChat et n’oubliez pas d’utiliser la méthode
getText pour récupérer le contenu).
Pour la réception, on va encore une fois commencer par la fin. Il faut écrire dans la classe
Arene une méthode qui reçoit un String en paramètre et remplace le contenu de txtChat
par ce string. Écrivez cette méthode que vous allez appeler remplaceChat.
Dans la classe Controle, méthode evenementJeuClient, ajoutez un test sur ordre. Si ordre
contient " remplace chat ", appelez la méthode remplaceChat de la classe Arene et
envoyez lui info transtypé en String.
Travaux pratiques Enfin, dans la classe JeuClient, méthode reception, ajoutez un test : si info est de type
String, alors appelez la méthode evenementModele en mettant les bons paramètres.
Urban Marginal
Lancez un serveur puis un client. Tapez plusieurs messages et constatez que les phrases
Page 50 s’écrivent aussi bien côté client que côté serveur. Ajoutez un nouveau client. Dès qu’il va
taper du texte (ou que le premier client va en taper) le nouveau client voit apparaître toute
la conversation.

Ce serait bien que le petit nouveau soit informé de la conversation dès son arrivée.
Mieux encore, ce serait bien que par l’intermédiaire de la conversation, tout le monde
soit informé de l’arrivée du petit nouveau !
Dans la classe JeuServeur, méthode reception, partie PSEUDO, en fin de case, construisez
une nouvelle phrase. Le but est de construire une phrase pour obtenir le message suivant
(en prenant comme exemple un joueur dont le pseudo est Ed) :
*** Ed vient de se connecter ***

O En déclarant ainsi la phrase, il n’est plus nécessaire de la déclarer dans le case


CHAT (d’ailleurs vous obtenez une erreur). Enlevez donc la partie déclaration.

Une fois la chaîne construite, sollicitez le controleur comme vous l’avez fait dans la partie
CHAT, pour ajouter et envoyer cette nouvelle phrase.

Faites un nouveau test complet, avec au moins 2 joueurs, pour contrôler que les messages
de bienvenue s’affichent et qu’un nouveau client est directement informé de toute la
conversation passée.

8 2950 TP PA 00
11. Les déplacements
Il est temps de faire bouger nos petits personnages.

Les déplacements côté client


Vous avez remarqué qu’on a parfois redonné le focus au jContentPane qui est le JPanel
principal. C’est parce que c’est sur ce JPanel que les événements des touches vont être
gérés.
Dans la classe Arene, faites une copie des 5 lignes de code qui gèrent l’événement clavier
dans la méthode getTxtSaisie, et collez ces 5 lignes dans getJContentPane, dans le if sur
client (car effectivement les événements ne doivent être gérés que côté client). Dans ces
5 lignes, remplacez txtSaisie par jContentPane, et txtSaisie_keyPressed(e) par jContent-
Pane_keyPressed(e).
Écrivez la méthode jContentPane_keyPressed. Cette méthode va s’occuper de tester le
type de touche utilisée, et de l’envoyer au serveur (à condition que ce soit une touche
valable). Pour écrire cette méthode, il va nous manquer quelques constantes.
Allez dans l’interface Global. Vous avez déjà créé les constantes GAUCHE et DROITE. Il
manque les constantes HAUT et BAS. Ajoutez les en leur donnant respectivement les
valeurs 2 et 3. On va en profiter pour ajouter la constante TIRE = 4 (pour gérer le tir
d’une boule). Les constantes PSEUDO et CHAT sont déjà créées et permettent de coder
le message envoyé au serveur. Il manque la constante ACTION = 2 pour que le serveur
puisse traiter une action (déplacement, tir).
De retour dans la classe Arene, méthode jContentPane_keyPressed, commencez par
Travaux pratiques
déclarer et initialiser à -1 la variable locale valeur. Cette variable va recevoir la valeur de
la touche utilisée, à condition que ce soit une touche valide. Dans le cas où valeur reste Urban Marginal
à -1 en fin de méthode, alors on ne fera rien. Donc, après l’initialisation de valeur, faites
un switch sur le code de la touche reçue en paramètre. Dans le switch, testez si le code Page 51
correspond à une des flèches ou à la touche espace (vous trouverez ces 5 constantes
statiques dans la classe KeyEvent : VK_SPACE, VK_LEFT, VK_RIGHT, VK_DOWN, VK_UP).
Suivant la touche utilisée, affectez une de vos constantes à la variable valeur (affectez
GAUCHE pour VK_LEFT, etc…). Après le switch, contrôlez le contenu de valeur. S’il est
différent de -1, alors sollicitez le controleur en appelant la méthode evenementVue et en
envoyant comme chaîne, la concaténation de ACTION, avec SEPARE, puis valeur.

Les déplacements côté serveur


C’est au tour du serveur de récupérer l’action. Lorsque le serveur va recevoir l’action,
il va immédiatement solliciter le joueur concerné pour qu’il gère l’action. On va donc
commencer par cette gestion.
Le déplacement et les collisions
Dans la classe Joueur, on va commencer par une méthode qui va standardiser le dépla-
cement (quelle que que soit la direction, des choses similaires se passent, donc plutôt
que d’écrire plusieurs fois le même code, il va être réuni dans une même méthode). De
plus, comme les méthodes de collisions sont déjà écrites, on va pouvoir les exploiter
facilement.
Écrivez la méthode privée deplace qui retourne un entier (ce sera la valeur de la nouvelle
position) et qui reçoit plein de paramètres :

8 2950 TP PA 00
action (entier) // pour connaître l’action sollicitée (gauche, droite…)
position (entier) // pour la position de départ
orientation (entier) // pour l’orientation de départ
lepas (entier) // pour la valeur du déplacement (en positif ou négatif)
max (entier) // pour la valeur à ne pas dépasser
lesjoueurs // le dictionnaire de joueurs
lesmurs // la collection de murs

C’est parti pour le contenu de la méthode. Valorisez la propriété orientation avec le


paramètre correspondant (l’orientation va changer si les flèches gauche ou droite ont
été utilisées). Déclarez une variable locale ancpos dans laquelle vous affectez position
(cette variable servira à revenir à l’ancienne position si la nouvelle n’est pas bonne, par
exemple si le personnage traverse un mur). Incrémentez le paramètre position avec le
paramètre lepas (lepas contient une valeur positive ou négative, qui permet donc un
déplacement dans un sens ou l’autre). Maintenant il faut contrôler si on ne sort pas de
l’arène : testez si position est inférieur à 0, dans ce cas remettez position à 0, puis testez
si position est supérieur à max, dans ce cas remettez position à max. Il reste à contrôler
s’il n’y a pas une collision avec un mur ou un autre joueur. Mais pour gérer les collisions,
on a besoin que les propriétés posX et posY de l’objet contiennent les nouvelles valeurs,
hors pour le moment on travaille avec le paramètre position sans avoir touché directe-
ment aux propriétés. Du coup, faites un test sur action : si action contient GAUCHE ou
DROITE alors affectez position à la propriété posX, dans le cas contraire, c’est à posY
qu’il faut affecter position. Faites ensuite un test en utilisant les méthodes toucheMur
Travaux pratiques et toucheJoueur : si un mur ou un joueur est touché, alors il faut revenir à l’ancienne
position (en réaffectant ancpos à position). Il ne reste plus qu’à passer à l’étape sui-
Urban Marginal
vante : l’étape représente le numéro dans la séquence d’animation (car un personnage à
plusieurs images pour la marche, par exemple et pour donner l’illusion de mouvement,
Page 52
il faut afficher à chaque fois une image différente). Pour cela, il suffit d’ajouter 1 à la
propriété etape et, quand elle dépasse le nombre d’étapes maximums, la remettre à 1.
Combien il y a d’étapes de marches au maximum ? Ça me parait judicieux de le déclarer
en constante dans Global : déclarez NBETATSMARCHE = 4. Vous pouvez maintenant uti-
liser cette constante pour faire le test précédent (au fait, le changement d’étape peut
s’écrire en une ligne, mais si vous n’y arrivez pas, faites le test classique). Il ne reste plus
qu’à retourner position.
On vient de manipuler un paramètre, lepas, qui va permettre de faire avancer (ou recu-
ler) le personnage. Ce sera toujours du même nombre de pixels, en positif ou en négatif.
Autant définir tout de suite ce nombre en constante : dans Global, déclarez LEPAS à la
valeur 10.
Écrivez la méthode publique action qui reçoit en paramètre action (entier qui contien-
dra le numéro de l’action), le dictionnaire lesjoueurs et la collection lesmurs. Dans cette
méthode, faites un switch sur action : la variable peut prendre les valeurs GAUCHE,
DROITE, HAUT, BAS et TIRE. Dans chaque case (excepté TIRE qui pour le moment va res-
ter vide) affectez à la propriété posX (pour GAUCHE et DROITE) ou posY (pour HAUT et
BAS) le résultat de l’appel de la méthode deplace que vous venez d’écrire. Au niveau des
paramètres, envoyez toujours action en premier paramètre, puis posX ou posY suivant
les cas, puis GAUCHE et DROITE en orientation pour forcer le changement de position
du personnage, par contre envoyez la propriété privée orientation dans le cas de HAUT
ou BAS (car dans ce cas, il n’y a pas de raison de changer l’orientation du joueur donc il

8 2950 TP PA 00
garde la même), envoyez ensuite LEPAS ou –LEPAS (le négatif est pour le déplacement à
gauche ou vers le haut), au niveau du max, donnez la taille de l’arène (en largeur ou en
hauteur suivant les besoins) diminuée de la taille du personnage (et de la taille aussi du
message si c’est un déplacement vertical), enfin envoyez lesjoueurs et lesmurs. Après le
switch, appelez la méthode affiche en envoyant en paramètres MARCHE et etape, afin
qu’après l’action, le personnage soit réaffiché et envoyé à tous. La méthode est terminée
pour le moment.
Le traitement du déplacement par le serveur
Où appeler la méthode action que l’on vient d’écrire ? Allez dans JeuServeur, méthode
reception, ajoutez un case ACTION. Dans le cas d’une reception d’une action, faites
appel, pour le joueur concerné, de la méthode action en envoyant en premier paramètre
l’action qui se trouve dans infos[1] (après l’avoir transformé en entier avec la méthode
statique parseInt de la classe Integer), et en second et troisième paramètres, lesjoueurs
et lesmurs.
Et maintenant comment gérer l’envoi de l’information à tous les clients pour qu’ils soient
tous informés du déplacement du joueur ? C’est déjà fait puisqu’on s’en occupe à chaque
fois que l’on affiche l’ajout ou la modification d’un joueur.
Faites un nouveau test complet. Utilisez les flèches pour déplacer le personnage.
Normalement vous devez voir l’animation du déplacement, le personnage doit se mettre à
chaque fois dans la bonne direction, il ne doit pas sortir de l’arène et ne doit pas passer à
travers un mur ou un autre joueur. Contrôlez aussi que les déplacements se voient en direct
dans toutes les arènes ouvertes.

La gestion des déplacements est terminée. Travaux pratiques

Urban Marginal

12. Les combats Page 53

Pour gérer les combats, on va devoir s’attaquer à un problème un peu délicat : la gestion
du déplacement automatique de la boule. Effectivement, jusque là, les personnages se
déplacent suite à une action : à chaque pas correspond une action. Dans le cas du tir,
lorsque la boule est lancée, elle doit continuer son parcours toute seule sans bloquer le
jeu : les joueurs doivent pouvoir, par exemple, se déplacer en même temps. Ce parcours
va donc se faire dans un processus indépendant : notion que vous connaissez déjà avec
la gestion de la connexion.
Mais on va traiter le problème étape par étape.

La Boule
La classe Boule
Avant de s’occuper du problème du mouvement de la boule, on va commencer par
simplement la créer. Dans le package modele, créez la classe Boule. Faites la hériter de
Objet et faites la implémenter l’interface Global pour avoir accès à toutes les constantes.
A priori, comme on va affecter une boule par joueur, on va la créer dès la création du
joueur. Cependant on la rendra invisible. Dès que le joueur va tirer, il suffira de bien
positionner la boule et de la rendre visible.
Avant de s’occuper de créer la boule du joueur, on va écrire le constructeur de la classe
Boule, qui va se charger d’initialiser le Label correspondant. Écrivez le constructeur : valo-

8 2950 TP PA 00
risez la propriété label de la classe mère, en lui affectant une instance de la classe Label :
le premier paramètre attendu étant le numéro de Label, utilisez la propriété statique
nbLabel sans oublier de l’incrémenter juste après (je vous rappelle que cette propriété est
un compteur qui nous permet de numéroter de façon séquentielle et unique les JLabels
du JPanel du jeu). En second paramètre, instanciez un JLabel. Le Label correspondant à
la boule est créé, il faut maintenant lui donner quelques caractéristiques. C’est un peu
le même principe que dans le constructeur de Mur. Donc, avec la même logique, centrez
horizontalement et verticalement le contenu du JLabel de la boule. Il faudrait ensuite
dimensionner le JLabel mais on n’a pas encore de constantes pour cela. Commencez par
déclarer (dans Global) les constantes L_BOULE et H_BOULE de valeur 17, puis utilisez
ces constantes pour fixer la taille du JLabel de la boule (pour le moment, au niveau
position, vous pouvez le mettre en 0, 0). Ensuite, affectez l’image de la boule au JLabel
(commencez là aussi par déclarer dans Global la constante CHEMINBOULES, qui contient
CHEMIN+"boules/" et la constante BOULE qui contient CHEMINBOULES+"boule.gif"). Il
ne reste plus qu’à rendre invisible le JLabel (avec la méthode setVisible sur le JLabel de
la boule).
Les caractéristiques de la boule sont paramétrées, cependant il reste à intégrer le JLabel
de la boule dans le JPanel côté serveur et à l’envoyer à tout le monde. Pour cela, on a
besoin d’accéder aux méthodes de JeuServeur, ce qui suppose que la classe Boule doit
recevoir une instance de JeuServeur. Déclarez en propriété privée jeuServeur de type
JeuServeur. Ajoutez le paramètre nécessaire dans la signature du constructeur et, dans
le constructeur, valorisez la propriété privée avec le paramètre.
À la fin du constructeur, vous pouvez maintenant appeler la méthode nouveauLabelJeu
Travaux pratiques sur la propriété jeuServeur, et mettre la propriété label (de la classe mère) en paramètre.
Ainsi le JLabel correspondant sera ajouté au JPanel côté serveur.
Urban Marginal
Pour l’envoi de la boule à tous les joueurs, il faut d’abord s’occuper de la boule dans la
classe Joueur.
Page 54
La boule dans la classe Joueur
Il faut maintenant associer une boule par joueur. Allez dans la classe Joueur et déclarez
en propriété privée boule de type Boule. Ecrivez le getter correspondant. À la fin de la
méthode initPerso, créez la propriété boule en lui affectant une instance de la classe
Boule (en envoyant en paramètre, la propriété jeuServeur). Juste après, pour que la
boule soit envoyée à tous les joueurs pour être intégrée dans le JPanel du jeu, appelez la
méthode envoi sur l’objet jeuServeur et mettez en paramètre le Label de la boule (atten-
tion, par la boule elle-même mais son Label). Il faut aussi penser à envoyer les boules des
anciens au nouveau joueur : dans la classe JeuServeur, méthode reception, case PSEUDO,
dans la boucle où vous envoyez déjà le label et le message des anciens, ajoutez une ligne
pour envoyer aussi le label de la boule.
Avant d’aller plus loin on va faire un test. Mais si on lance tout de suite le test, comme
la boule est insérée en invisible, il n’y aura pas grand-chose à voir. Dans le constructeur
de la classe Boule, mettez temporairement le setVisible à true.

Faites un nouveau test complet avec un joueur. Normalement vous devriez voir apparaître
une boule dans le coin haut gauche de l’arène (aussi bien côté serveur que client).

En fait on n’a pas écrit grand-chose pour que la boule apparaisse et qu’elle soit envoyée
à tout le monde. C’est normal, les méthodes d’envoi et d’affichage étaient déjà écrites.
Bon, pensez à remettre à false le setVisible du JLabel dans la classe Boule.

8 2950 TP PA 00
La classe Attaque
Il est temps d’attaquer (sans mauvais jeu de mots) la partie la plus difficile : l’attaque.
Dans le package modele, créez la classe Attaque. Cette classe va exécuter un proces-
sus indépendant, donc faites la hériter de la classe Thread. Elle aura aussi besoin de
constantes donc faites la implémenter l’interface Global.
On va avoir besoin de l’attaquant et aussi d’une instance de JeuServeur pour gérer les
envois de la boule qui avance (entre autre). Déclarez en privé les propriétés attaquant
(type Joueur) et jeuServeur (type JeuServeur).
Créez le constructeur qui reçoit les paramètres nécessaires pour valoriser les 2 propriétés
privées. Puis, en fin de constructeur, appelez la méthode start() de la classe mère qui va
permettre de lancer le processus.
Écrivez la méthode publique run(). Dans la méthode, comme on va beaucoup travailler
sur la boule, pour simplifier l’écriture, déclarez une variable locale laboule de type Boule
et affectez lui la boule de l’attaquant. Il est aussi important de récupérer l’orientation de
l’attaquant au début du tir (de sorte que s’il change d’orientation pendant que la boule
avance, cela ne change pas l’orientation de la boule qui est en cours de trajectoire !).
Donc déclarez en local la variable orientation de type entier et affectez lui l’orientation
de l’attaquant (pour accéder à l’orientation du joueur, ajoutez rapidement le getter cor-
respondant dans la classe Joueur).
Ensuite, rendez visible cette boule (ou plutôt le JLabel correspondant).
Faites une boucle qui va permettre de boucler sur l’avancée de la boule (on va préférer
une boucle de type do/while pour éviter de tester une collision dès l’apparition de la
boule). Dans la boucle, la première étape va consister à faire avancer la boule. Pour cela, Travaux pratiques
on doit pouvoir modifier les posX et posY de la boule (propriétés qui se trouvent dans la
classe Objet). Allez rapidement ajouter les 2 setters nécessaires dans la classe Objet. Puis, Urban Marginal
de retour dans la méthode run(), faites un test sur l’orientation (votre variable locale). Si
l’orientation est GAUCHE, alors il faut soustraire LEPAS de posX (écrivez le en utilisant le Page 55
setter et le getter sur posX). Dans le cas contraire, il faut ajouter LEPAS.
Toujours dans la boucle, une fois la nouvelle position calculée, il faut afficher. Modifiez
la position du JLabel de la boule avec setBounds (vous savez faire) pour que la boule se
positionne aux coordonnées posX et posY.
Après l’affichage, il faut envoyer la nouvelle position à tous les autres joueurs. Sur jeu-
Serveur, appelez la méthode envoi en mettant en paramètre le label de la boule.
Il reste à compléter le test de fin de boucle. Pour le moment, on va boucler tant que la
boule ne sort pas de l’arène, donc tant que son posX est compris entre 0 et L_ARENE.
Écrivez le test.
Après la boucle, il ne reste plus qu’à rendre à nouveau invisible la boule. Pensez juste
après à envoyer à nouveau la boule à tous les joueurs (sinon elle sera invisible côté ser-
veur mais pas chez les clients).
Pas de test possible pour le moment car Attaque n’est jamais instanciée, mais ça ne sau-
rait tarder.

La gestion de l’attaque
Le déclenchement du tir
Dans la classe Boule, écrivez la méthode tireBoule qui reçoit en paramètre attaquant de
type Joueur. Cette méthode doit initialiser la position de départ de la boule et lancer le

8 2950 TP PA 00
tir. Dans la méthode, faites un test sur l’orientation de l’attaquant. S’il est tourné vers la
gauche (GAUCHE), alors affectez dans posX de la boule le posX de l’attaquant moins la
largeur de la boule, moins 1 (c’est une petite sécurité pour que la boule ne touche pas le
joueur). Pourquoi on enlève la largeur de la boule ? Pour positionner la boule à gauche
du joueur, sachant que les coordonnées sont toujours à partir du coin haut gauche. Dans
le sinon du test (donc orientation vers la droite), cette fois affectez à posX le posX de
l’attaquant + la largeur du personnage + 1.
Sorti du test, il faut aussi initialiser posY : affectez lui le posY de l’attaquant + la moitié
de la hauteur du personnage (pour que la boule soit positionné vers le milieu du per-
sonnage).
Il ne reste plus qu’à instancier la classe Attaque en lui envoyant les bons paramètres.
Maintenant, il faut appeler cette méthode tireBoule. Mais où ? Dans la classe Joueur,
méthode action, vous avez déjà le case TIRE qui est prêt, mais vide. Dans ce case, appelez
la méthode tireBoule sur la propriété boule, en envoyant this en paramètre.
Faites un nouveau test complet avec un joueur. Côté joueur, appuyez sur la barre d’espace.
Vous devriez voir assez rapidement une boule partir du joueur et aller vers la gauche ou la
droite (suivant l’orientation du joueur). Faites d’ailleurs le test dans les 2 sens. Contrôlez
que vous voyez bouger la boule dans les 2 arènes. Vous avez peut-être l’impression que
la boule ne part pas vraiment du joueur. Si vous appuyez plusieurs fois rapidement sur la
barre d’espace, vous la verrez peut-être mieux partir du joueur. Cependant, en appuyant
plusieurs fois successivement, il n’est pas impossible qu’au bout d’un moment (très rapi-
dement, en fait) vous obteniez une fenêtre vous signalant que l’ordi distant s’est déconnec-
té… Il ne vous reste plus qu’à tout fermer ! Vous remarquez au passage une liste d’erreurs
dans la console sur l’objet out : " java.io.IOException : stream active ".
Travaux pratiques

Urban Marginal Que s’est-il passé ? Pourquoi ça marche ainsi de façon un peu « bancale » ? On a 2 pro-
blèmes à résoudre : la vitesse de défilement, et le plantage final.
Page 56
La vitesse de la boule
Pour réguler la vitesse de la boule, il faut insérer une pause entre chaque affichage de
la boule. Cette pause ne doit pas être bloquante (d’autres processus doivent pouvoir
continuer à s’exécuter pendant ce temps). Du coup, même la pause doit être basée sur
un thread pour n’être effective que dans le processus où elle se trouve.
Dans la classe Attaque, créez la méthode Pause qui reçoit en paramètre milli de type
long et nano de type int. En effet, pour une pause on peut déterminer le nombre de
millisecondes et/ou de nanosecondes. Dans la méthode, appelez la méthode statique
sleep de la classe Thread, en lui envoyant les 2 paramètres. Cet appel va vous réclamer
un try/catch : ajoutez-le.
Dans la méthode run, juste après l’affichage, appelez votre méthode Pause en envoyant
en paramètre 10 millisecondes.
L’appel de méthodes extérieures dans un Thread
En fait, les threads sont délicats à manipuler et on touche ici un cas d’erreur classique des
threads. Votre thread qui permet de faire avancer la boule, fait appel à une méthode
qui est extérieure à lui pour envoyer le label de la boule à tous les joueurs (afin que son
avancée s’affiche dans toutes les arènes en même temps). Or, la méthode envoi peut
très bien être appelée en même temps par différents processus. Voilà pourquoi cette
liste d’erreurs " stream active " pour vous signaler que l’objet out est sollicité en même
temps par plusieurs processus. Pour éviter ce problème, il faudrait mettre une sorte

8 2950 TP PA 00
de protection sur l’appel de la méthode envoi de la classe Connection. Le but de cette
protection serait de forcer l’ordinateur à attendre la fin de l’exécution de la méthode,
avant de pouvoir la solliciter à nouveau, même par un autre processus. Eh bien cela est
possible : dans l’en-tête de la méthode, entre les mots « public » et « void », ajoutez le
mot « synchronized ». Vous remarquez qu’il apparaît en gras.
Faites un nouveau test complet avec un joueur. Cette fois, la boule va à une vitesse raison-
nable (si ce n’est pas le cas, modifiez dans le code le nombre de millisecondes de la pose
et recommencez) et surtout il n’y a plus de plantage lorsque vous utilisez plusieurs fois la
barre d’espace. Par contre, il se passe encore quelque chose de bizarre : quand vous utilisez
plusieurs fois rapidement la barre d’espace, la boule ne finit pas son trajet et recommence
un nouveau trajet.

Ce problème va être réglé en bloquant le tir du joueur si sa boule est déjà en cours de
trajectoire.
L’attente de la fin de la trajectoire entre 2 tirs
Cette condition va se mettre dans la classe Joueur, méthode action, case TIRE. Le but
est d’appeler tireBoule sous condition. Mais quelle condition ? Il suffit de contrôler si le
JLabel de la boule est visible ou non : utilisez la méthode isVisible() sur le JLabel de la
boule et n’appelez tireBoule que si le JLabel n’est pas visible.
Faites un nouveau test pour contrôler qu’en appuyant plusieurs fois sur la barre d’espace
alors que la boule est en cours de trajectoire, il ne se passe rien : la trajectoire se termine.
Profitez-en pour faire un test avec 2 joueurs et contrôlez que vous voyez bien la boule
circuler dans toutes les arènes. Essayez de bouger un personnage pendant que le boule
circule. Normalement cela doit fonctionner excepté qu’il y a des ralentissements notoires. Travaux pratiques
Cela vient de la pause qui est censée ne pas bloquer les autres processus mais provoque
tout de même un ralentissement. Urban Marginal

Il est possible qu’à ce stade, suivant la puissance de votre ordinateur, la boule soit un peu Page 57
lente. Rien ne vous empêche de changer la pause voire de la supprimer complètement.
Pour le moment vous pouvez la laisser, dans un esprit de tests, car cela va permettre de
mieux voir si les collisions fonctionnent (ce que l’on va traiter juste après) mais par la
suite, vous verrez que vous enlèverez la pause, en particulier quand on ajoutera le son
qui ralentit aussi l’exécution.
La collision de la boule contre un mur
Maintenant que la boule avance, il faut gérer les collisions. Il y a 2 types de collisions :
soit la boule tape un mur, et là elle va s’arrêter et disparaître, soit elle touche un joueur
et celui-ci sera blessé. Dans un premier temps, on va gérer les collisions contre les murs.
Dans une autre classe (Joueur), on avait déjà écrit une méthode pour gérer les collisions
contre les murs. Malheureusement il ne sera pas possible de réutiliser cette méthode (en
la mettant par exemple dans la classe Objet) parce que l’appel de la collision va se faire
dans le run(), or ce dernier ne peut prendre aucun paramètre. Ce qui suppose que la
nouvelle méthode que l’on va écrire, et qui devrait nécessiter de recevoir la collection de
murs, ne pourra pas non plus prendre cette collection en paramètre (comme on l’avait
fait dans la classe Joueur). Donc, la collection de murs (et globalement tout ce dont on
a besoin dans le run()) doit être mémorisé dans des propriétés privées de la classe, pour
être accessible dans toute la classe.
Donc déclarez en privé la propriété lesmurs (vous connaissez le type) et valorisez cette
propriété dans le constructeur, avec le paramètre correspondant que vous allez ajouter.

8 2950 TP PA 00
Cette modification des paramètres du constructeur va engendrer quelques modifications
en cascade : allez dans la classe Boule. Il y a une erreur lors de l’instanciation de la classe
Attaque. Il faudrait envoyer en paramètre la collection de murs, mais nous ne l’avons pas
ici. Donc ajoutez lesmurs en paramètre de la méthode tireBoule. Maintenant vous pou-
vez ajouter lesmurs en 3e paramètre de l’appel de Attaque. Allez dans la classe Joueur :
il y a bien sur maintenant une erreur dans la méthode action, sur l’appel de la méthode
tireBoule. Dans l’appel, ajoutez lesmurs en second paramètre (vous avez normalement
reçu la collection de murs dans les paramètres de la méthode action).
De retour dans la classe Attaque, écrivez la méthode privée booléenne toucheMur qui ne
reçoit aucun paramètre. Dans la méthode, suivez la même logique que la méthode équi-
valente dans la classe Joueur (excepté que vous utilisez la propriété privée lesmurs et non
le paramètre, et que la méthode toucheObjet est appelée sur la boule de l’attaquant).
Dans la méthode run(), le but étant d’arrêter la boule si elle touche un mur, ajoutez une
3e condition dans le while : il faut continuer à boucler tant qu’on ne touche pas un mur
(donc tant que l’appel de la méthode retourne false).

Faites un nouveau test complet avec un joueur. Essayez de tirer des boules dans différentes
directions et contrôlez qu’elles vont au bout quand il n’y a pas de mur, mais qu’elles
s’arrêtent contre les murs.

La boule touche un joueur


La logique va être assez similaire pour gérer les collisions contre un joueur, excepté qu’il
faudra savoir quel joueur a été touché pour ensuite afficher les images du joueur blessé,
puis éventuellement mort, et aussi diminuer sa vie.
Travaux pratiques
Pour pouvoir contrôler les collisions avec les autres joueurs, il faut bien sûr être en pos-
Urban Marginal session du dictionnaire de joueurs. En suivant la même logique que pour les murs, faites
en sorte de récupérer dans une propriété privée de la classe, le dictionnaire de joueurs.
Page 58 Pensez à faire en cascade toutes les modifications nécessaires.
Toujours dans la classe Attaque, écrivez la méthode privée toucheJoueur qui est très
semblable à celle qui se trouve dans la classe Joueur, excepté qu’elle ne retourne pas un
booléen mais le joueur touché, qu’elle ne reçoit aucun paramètre mais travaille avec la
propriété privée lesjoueurs, qu’elle ne contrôle pas si le joueur concerné est le joueur
actuel (normal puisque ce n’est pas une collision entre joueurs mais entre une boule et
un joueur), que la méthode toucheObjet est appelée sur la boule de l’attaquant, et enfin
qu’elle retourne non pas true ou false mais soit le joueur touché, soit null.
Dans la méthode run(), avant la boucle, déclarez la variable locale victime de type Joueur
et initialisez la à null. À la fin de la boucle, avant le test, affectez à victime l’appel de la
méthode toucheJoueur. À ce niveau là, si aucun joueur n’est touché, victime va rester à
null, sinon il contiendra le joueur touché. Dans le test du while, ajoutez une 4e condition
cette fois sur victime : on continue de boucler si victime est à null.

Faites un test avec 2 joueurs pour contrôler que lorsqu’un joueur tire sur un autre, la boule
s’arrête sur le joueur.

Le joueur est blessé


Lorsque le joueur est touché par une boule, il va falloir afficher successivement ses états
(les images) quand il est blessé. Même remarque quand il sera en train de mourir. Pour
cela, il faut savoir combien il y a d’images dans chaque catégorie. Allez dans l’interface

8 2950 TP PA 00
Global et, comme vous aviez mis la constante NBETATSMARCHE, ajoutez les constantes
NBETATSBLESSE et NBETATSMORT en les initialisant toutes les 2 à 2.
Dans la classe Attaque, méthode run(), après la boucle et avant de rendre invisible la
boule, faites un test sur victime. Si victime ne contient pas null, alors un joueur a été
touché (et il est dans victime). Dans ce cas, faites une boucle qui va de 1 à NBETATSBLESSE
et, dans la boucle, appelez la méthode affiche sur victime en envoyant en premier para-
mètre BLESSE (pour afficher les images du personnage blessé) et en second paramètre
l’indice de votre boucle. Ensuite, faites une pause de 80 millisecondes pour avoir le temps
de voir les images de l’animation du personnage blessé (vous ajusterez si ça ne vous
convient pas). Après la boucle, appelez à nouveau la méthode affiche sur victime, mais
cette fois en envoyant en paramètre MARCHE et 1 (pour remettre le personnage debout,
à la première position de marche, donc quand il ne bouge pas).

Faites un test avec 2 joueurs pour contrôler que lorsqu’un joueur est touché par une boule,
il « réagit » puis revient à la position normale. Contrôlez que tout s’affiche correctement
dans les 3 arènes.

La gestion des points de vie


À chaque fois qu’un joueur est touché, il doit perdre de la vie pendant que l’attaquant
en gagne. Pour cela, un peu comme la vie de départ (que l’on a initialisé à 10), il faut
fixer le gain et la perte de vie à chaque attaque.
Cette fois, on va travailler avec des constantes de classes plutôt que des constantes géné-
rales dans Global. Pourquoi ? Pour changer un peu et surtout parce que ces constantes
ne concernent que la classe Joueur.
Travaux pratiques
Dans la classe Joueur, en début de classes, vous allez déclarer 3 constantes de classes
(propriétés privées, statiques et finales) : Urban Marginal

maxvie (entier) = 10 // vie de départ pour tous les joueurs


Page 59
gain = 1 // gain de points lors d’une attaque
perte = 2 // perte de points lors d’une attaque

Du coup, dans le constructeur, changez l’initialisation de vie en lui affectant maxvie.


Écrivez la méthode publique gainVie qui ajoute gain à la vie. Écrivez la méthode perte-
Vie qui enlève perte à la vie, mais attention, la vie ne doit pas passer au dessous de 0.
Donc soit vous faites un test pour la mettre à 0 si après la diminution, elle est négative,
soit vous l’écrivez en une ligne en utilisant la méthode statique max de la classe Math.
Dans la correction vous aurez la deuxième solution.
Retournez dans la classe Attaque, méthode run(), vers la fin de la méthode, au début
du test si la victime n’est pas null, alors appliquez la méthode perteVie sur la victime
et la méthode gainVie sur l’attaquant. Pour que les modifications soient visibles, en fin
de test, comme vous avez affiché la victime, affichez l’attaquant avec les mêmes para-
mètres. On va aussi en profiter pour faire en sorte qu’une personne qui attaque ne reste
pas dans une position intermédiaire de marche mais soit tout simplement debout, donc
à la position 1. Du coup, la ligne que vous venez d’écrire, écrivez-la aussi en tout début
de méthode run().
Faites un test avec 2 joueurs pour contrôler que lorsqu’un joueur est touché par une boule,
il perd de la vie alors que l’autre en gagne. Contrôlez aussi que les joueurs (l’attaquant et
la victime) se redressent. Enfin, vérifiez que la perte de vie ne passe pas en dessous de 0.
En revanche, si le joueur dont la vie a atteint 0 essaye de tirer, il regagne de la vie ! Bien
évidemment on va éviter ce genre de choses en gérant la mort du joueur.

8 2950 TP PA 00
La mort du joueur
Il reste à gérer la mort du joueur.
Commençons par écrire la méthode booléenne publique estMort() dans la classe Joueur,
qui retourne vrai si la vie du joueur a atteint 0.
Dans la méthode run() de la classe Attaque, au niveau du test si la victime n’est pas null,
ajoutez un second test en contrôlant que la victime n’est pas déjà morte (sinon, inutile
bien sûr de faire toute la suite).
Dans ce même test, après la boucle de l’affichage de la victime blessée et avant les 2
affichages en position de marche, faites un test pour contrôler si la victime est morte
(en effet, elle a été blessée et sa vie vient peut-être de passer à 0). Dans ce cas, faites
une boucle similaire à la boucle précédente pour afficher les différents états du joueur
blessé, mais cette fois c’est le joueur mort. Attention, comme le joueur mort ne doit pas
ressusciter, déplacez l’affichage de la victime en position marche (qui est juste après)
dans le sinon du si que vous venez de faire. En effet, la victime doit se redresser que si
elle n’est pas morte.
Faites un test avec 2 joueurs. Essayez de tuer un joueur. Normalement vous devez voir
l’animation du joueur blessé puis mort, et le joueur reste au sol. Si vous continuez à tenter
de lui tirer dessus, il reste au sol et il ne se passe rien (excepté que la boule s’arrête sur lui).
En revanche, allez ensuite dans la frame du joueur mort, et essayez de le déplacer ou de le
faire tirer : tout d’un coup il ressuscite… aïe, il faut corriger ce petit bug.

Allez dans la classe JeuServeur, méthode reception, case ACTION. N’exécutez la ligne de
code (l’appel de la méthode action) que si le joueur concerné n’est pas déjà mort.
Travaux pratiques
Contrôlez que le petit bug a été corrigé. Cependant, vous remarquez qu’on n’a pas désac-
Urban Marginal tivé le t’chat pour un joueur mort. Vous pouvez tester. Maintenant, fermez la frame du
client mort, et essayez de déplacer l’autre joueur. Si vous regardez ce qui se passe dans la
Page 60 console du serveur, vous obtenez une liste d’erreurs sur l’objet out.

Ces erreurs ne sont pas bloquantes mais essayons de les comprendre. Allez dans la classe
Connection, méthode envoi. Cette méthode envoie sur le canal out. Si un client vient de
fermer sa frame, son canal d’écoute n’est plus actif et le canal out correspondant, côté
serveur, ne peut plus fonctionner et retourne une erreur. Il faut donc s’occuper de la
fermeture d’une frame.
Un joueur quitte le jeu
Depuis le début de la création de la classe Jeu, il était prévu la méthode abstraite decon-
nection. Il est temps de s’en occuper. Mais avant de la remplir, on va l’appeler. À quel
endroit ? Quand un ordinateur se déconnecte.
Allez dans la classe Connection, méthode run(). Quand il y a un problème de type IO sur
le canal d’entrée (in) c’est que l’ordinateur s’est déconnecté. D’ailleurs vous affichez le
message correspondant dans une messagebox et vous mettez à false inOk. Juste après,
appelez la méthode deconnection sur le jeu (obtenu avec le getter correspondant) de
l’objet lerecepteur (correctement transtypé en objet Controle. À l’appel de la méthode
deconnection, envoyez l’instance actuelle, donc this. Pour vous aider à écrire cette ligne,
vous pouvez vous inspirer de la ligne similaire que vous avez écrite juste un peu plus haut
pour appeler la méthode reception.
Il y a 2 types de déconnexion : soit c’est un joueur, et le jeu peut continuer pour les
autres, soit c’est carrément le serveur, et là tout s’arrête, bien sûr. On va pour le moment
s’occuper de la déconnexion d’un joueur.

8 2950 TP PA 00
Allez dans la classe Joueur. Créez la méthode publique departJoueur. Dans cette
méthode, rendez invisibles les 3 labels qui concernent le joueur (le personnage, le mes-
sage et la boule). Ensuite, comme tous les joueurs doivent être informés, envoyez (avec la
méthode envoi sur jeuServeur) les 3 labels en questions. Toutes ces lignes de code, entou-
rez-les d’un test pour contrôler que le label du personnage existe (qu’il est différent de
null). Car effectivement, si le joueur ferme la frame ChoixJoueur, les labels n’existent pas
encore et du coup les lignes de code de ce module vont provoquer des erreurs.
Dans la classe JeuServeur, vous allez enfin remplir la méthode redéfinie deconnection
qui reçoit en paramètre l’objet connection de joueur qui vient de se déconnecter. Dans
cette méthode, faites appel à la méthode departJoueur que vous venez d’écrire (bien
sûr, appliquée au joueur concerné). Il ne reste plus qu’à supprimer ce joueur du diction-
naire : pour cela utilisez la méthode remove sur le dictionnaire, en mettant connection
en paramètre.
Faites un test avec 2 joueurs. Fermez la frame d’un des 2 joueurs. Normalement il a dis-
paru des arènes restantes (de l’autre joueur et du serveur). Faites déplacer le joueur restant
et observez la console du serveur. Vous devez avoir 3 erreurs out mais pas plus (ces 3
erreurs viennent du fait que vous avez envoyé à tous les nouveaux labels invisibles, et dans
le lot il y a aussi le joueur qui vient de partir, et qui n’a pas pu recevoir ces labels. Mais
juste après, suite à la suppression du joueur dans le dictionnaire, il n’y a plus eu d’erreurs).
Le serveur s’arrête
Que se passe-t-il si le serveur s’arrête ? Le jeu doit s’arrêter et toutes les frames des
joueurs doivent se fermer. C’est donc très simple : allez dans la classe JeuClient, méthode
redéfinie deconnection, et mettez juste dans la méthode la ligne de code qui permet
d’arrêter l’application. Travaux pratiques
Démarrez un serveur et 2 clients puis fermez le serveur. Une première messagebox apparaît
vous signalant la fermeture de l’ordinateur distant, et après avoir cliqué que ok, la frame Urban Marginal
du client correspondant se ferme aussi. Juste après, vous obtenez la même messagebox
pour le second client, qui va aussi se fermer. Page 61

13. Le son
Difficile de faire un jeu sans y intégrer du son.

Le package son
Intégration du package
Cette fois, on ne va pas écrire le code de la classe qui s’occupe de jouer des sons.
Récupérez le zip " son " qui vous est fourni, et dézippez le dans le dossier outils qui se
trouve dans le dossier src de votre projet Urban Marginal. Un fois le fichier dézippé, sup-
primez le zip. Je vous rappelle que pour Java, un dossier est un package. Retournez dans
Eclipse. Pour le moment (si vous n’avez pas fermé Eclipse entre temps) vous ne voyez pas
le package son dans le package outils. C’est normal. Faites Fichier/actualiser. Cette fois,
dans outils, vous voyez 2 packages : connexion et son.
Le contenu du package
Ouvrez le package. Vous remarquez qu’il contient lui-même plusieurs packages et
classes. Ce package a été récupéré sur Internet. Il permet de jouer des sons de type MDI,
AU et certains WAV (pas trop lourds).

8 2950 TP PA 00
La classe Son
Dans ce package récupéré, j’ai rajouté une classe (que j’ai intégrée directement dans le
package) : c’est la classe Son. Ouvrez là. Elle est très courte et n’existe que dans un souci
de petite simplification d’écriture. Son constructeur permet d’initialiser un son en lui
envoyant le nom du fichier correspondant. Ensuite, il n’y a que 3 méthodes clairement
commentées.

L’utilisation des sons


Mises en constantes des noms des sons
Pour pouvoir modifier facilement les noms des fichiers, on va les enregistrer dans des
constantes de l’interface Global.
Voici la liste des constantes à intégrer dans Global :

CHEMINSONS = CHEMIN + "sons/"


SONPRECEDENT = CHEMINSONS + "precedent.wav" // sur le clic du bouton
précédent
SONSUIVANT = CHEMINSONS + "suivant.wav" // sur le clic du bouton suivant
SONGO = CHEMINSONS + "go.wav" // sur le clic du bouton go
SONWELCOME = CHEMINSONS + "welcome.wav" // à l’entrée de la frame ChoixJoueur
SONAMBIANCE = CHEMINSONS + "ambiance.wav" // son d’ambiance dans tout le
jeu

Les sons directs


La première frame (de connexion) ne comportera pas de sons. Les sons vont intervenir à
Travaux pratiques
partir de la frame du choix du joueur.
Urban Marginal Allez dans la classe ChoixJoueur et déclarez en propriétés privées : precedent, suivant, go
et welcome de type Son (sans oublier d’intégrer le package nécessaire).
Page 62
En fin de constructeur, valorisez chaque propriété de son en lui affectant une instance de
la classe Son et en envoyant en paramètre le son correspondant (le nom de la constante).
Le son welcome peut être joué tout de suite puisqu’il doit être joué à l’ouverture de la
frame. Donc, appliquez à la propriété welcome la méthode play().
Jouez le son precedent en début de méthode lblPrecedent_clic(). Faites de même pour
le son suivant. Dans la méthode lblGO_clic(), dans la partie du if où vous faites appel à
evenementVue, juste avant, jouez le son go.
Avant de faire un test, prenez le temps d’écouter les différents sons qui se trouvent dans
le dossier " media/sons " pour savoir quels sons vous devez entendre.
Faites un test avec un joueur : à l’ouverture de la frame du choix du joueur, vous devez
avoir le son " welcome ". En cliquant sur les boutons suivant et précédent, vous devez
aussi entendre les sons adéquats (qui sont proches mais un peu différents). Enfin, le clic
sur Go provoque un dernier son spécifique.
Les sons ordonnés par le serveur
Certains sons doivent être ordonnés par le serveur. Il y a 3 sons dans ce cas :
• fight : quand un joueur tire une boule. Pourquoi ne pas le faire côté client quand
le joueur appuie sur la barre d’espace ? Parce que si le joueur est mort, on ne peut
pas lui interdire d’appuyer sur espace par contre le serveur sait qu’il est mort et ne
demandera pas de jouer le son.
• hurt : quand un joueur est touché par une boule. Là effectivement, on n’a vraiment
aucun moyen de savoir directement côté client si le joueur est touché, donc c’est le
serveur qui doit ordonné de jouer le son.

8 2950 TP PA 00
• death : pour les mêmes raisons que le cas précédent, le son de la mort d’un joueur
ne peut être demandé que par le serveur.
Si vous allez dans la classe JeuClient, méthode reception, vous remarquez que les diffé-
rents messages reçus sont reconnus par rapport à leur classe (JPanel, JLabel, String). Il
faut donc garder la même logique et trouver un type spécifique au son. On ne pourra
pas envoyer directement un objet Son car il y aurait trop de choses à transférer (vous
avez vu que le package son contient beaucoup de classes) et de plus elles ne sont pas
toutes sérialisables. L’envoi d’un objet son serait aussi ridicule par la taille de l’objet
transféré alors qu’on a juste besoin de connaître le nom du son à jouer. Transférer le
nom du son, sous forme de String, pose un problème : le type String est déjà utilisé pour
la réception de message du t’chat. On pourrait faire 2 types de String et, comme côté
serveur, découper la chaîne pour la reconnaître. Je préfère garder la même logique de
différentes classes et envoyer cette fois un Integer. Il suffit alors d’associer à chaque son
un numéro. C’est très facile de le faire en stockant les sons dans un tableau.
Dans l’interface Global, ajoutez les 3 constantes FIGHT, HURT et DEATH de type Integer
en leur affectant respectivement 0, 1 et 2. Déclarez aussi un petit tableau contenant les
3 sons respectifs : voici la déclaration à écrire pour le tableau.
public static final String[]
SON = {"fight.wav", "hurt.wav", "death.wav"} ;
Allez dans la classe Arene. Il faut s’occuper de jouer les sons demandés. Commencez par
déclarer en privé un tableau de Son de la taille du tableau de constantes. Voici la syntaxe
pour déclarer un tel tableau :
private Son[] lessons = new Son[SON.length] ;
Travaux pratiques
Le tableau doit être rempli avec les sons correspondants : en fin de constructeur, com-
mencez par faire un test sur la propriété client. En effet, on ne va charger les sons que Urban Marginal
si c’est l’arène du client. Dans le if, faites un boucle sur la taille du tableau SON et, dans
chaque case du tableau lessons, affectez une instance de la classe Son en mettant en Page 63
paramètre : CHEMINSONS+SON[k] (en supposant que l’indice de la boucle est k).
Écrivez la méthode publique joueSon qui reçoit en paramètre un entier représentant le
numéro du son, et qui joue le son qui se trouve à cette position (correspondant au para-
mètre) dans le tableau lessons.
C’est le contrôleur qui va demander à l’arène de jouer un son. Dans la classe Controle,
méthode evenementJeuClient, ajoutez un test. Si l’ordre est " son " alors appelez la
méthode joueSon de l’arène en envoyant en paramètre info transtypé en Integer.
Dans la classe JeuClient, méthode reception, ajoutez encore un test : si l’objet info est
de type Integer, alors appelez evenementModele sur controle, en envoyant les bons
paramètres pour jouer un son.
Maintenant, côté serveur, voyons quand envoyer chacun des 3 sons.
Dans la classe Joueur, méthode action, case TIRE, dans le if, une boule va être tirée, juste
avant utilisez la méthode envoi sur jeuServeur pour envoyer FIGHT. Le son étant envoyé
à tous les clients, tout le monde l’entendra.
Dans la classe Attaque, méthode run, après la boucle, au début du test s’il y a une victime
qui n’est pas encore morte, envoyez le son HURT. Un peu plus bas, si la victime est morte,
envoyez le son DEATH.

8 2950 TP PA 00
Faites un test avec 2 joueurs pour tester les sons quand une boule est tirée, quand un joueur
est blessé et aussi quand il meurt. Si vous entendez parfois les sons un peu en double, c’est
normal vu qu’ils s’exécutent sur les 2 clients.

La musique d’ambiance
Enfin, ce serait bien de mettre une musique d’ambiance.
Dans la classe Arene, le constructeur, dans la condition sur le client, avant ou après la
boucle pour charger les sons, vous allez écrire la ligne suivante :
(new Son(SOMAMBIANCE)).playContinue() ;
Je vous fais utiliser un petit raccourci d’écriture. Effectivement, comme l’appel de ce son
ne se fait qu’une fois, les 3 lignes habituelles (déclaration, instanciation et lancement de
la musique) se fait en une seule ligne. Pas besoin de déclarer : on instancie la classe Son
en envoyant en paramètre le son d’ambiance et, sur cette instance, on applique directe-
ment la méthode playContinue. Cette méthode permet de boucler sur un son.
Lors du test, vous allez être surpris d’entendre le son se jouer dès la frame du choix du
joueur alors qu’on vient de le mettre dans la frame de l’arène. C’est normal : lorsqu’un
client se connecte, avant même de créer la frame du choix du joueur, l’arène est créée
même si elle n’est pas encore visible. Finalement ça nous arrange bien car le jeu com-
mence vraiment au moment du choix du joueur.
Faites un test avec 1 joueur : dès la frame du choix du joueur, vous avez la musique de
fond. Entrez dans l’arène, la musique continue. Si vous lancez un second joueur, vous
remarquerez que vous avez 2 musiques ce qui est normal car il y en a une par client. Il est
possible que vous remarquiez un certain ralentissement dans le déplacement des joueurs
Travaux pratiques et le tir de la boule.
Urban Marginal
Le problème des ralentissements
En ce qui concerne le tir de la boule, vous pouvez l’accélérer en enlevant la pause que
Page 64 vous aviez placée dans Attaque, au niveau de la boucle sur l’avancée de la boule (si vous
ne l’aviez pas déjà enlevée). Attention, n’enlevez pas la pause dans la boucle du person-
nage blessé ou du personnage qui est en train de mourir.
Le son d’ambiance ralentit considérablement l’exécution : si vous voulez mettre vos
propres sons, faites en sorte de les choisir les plus légers possibles.
L’ajout de plusieurs joueurs représente aussi un ralentissement tout à fait logique
puisque le serveur a beaucoup plus de choses à gérer. Pour améliorer les performances,
il faudrait déléguer des tâches aux clients. Java et tous les langages précompilés ont aussi
un petit handicap au niveau rapidité d’exécution, même si les machines virtuelles sont de
plus en plus performantes. Dans le domaine du jeu pur, pour le moment, aucun langage
ne peut supplanter complètement le C++ qui reste le plus rapide car le plus proche du
processeur. Cependant au niveau des jeux, Java s’impose tout de même dans les techno-
logies embarquées (jeux sur téléphones portables, par exemple) grâce à sa portabilité.

14. Le déploiement
L’application est terminée, mais jusqu’à maintenant vous ne l’avez testée que sur un seul
ordinateur et toujours en passant par Eclipse.

Lancer l’application sans Eclipse


On va déjà faire un test sans passer par Eclipse. Pour démarrer une application Java,
il faut faire appel à la machine virtuelle et lui préciser la classe qui contient le main.

8 2950 TP PA 00
Ouvrez une fenêtre d’invite de commandes (normalement vous y accédez en passant
par le menu « démarrer/tous les programmes/accessoires/invite de commande ». Dans la
fenêtre qui s’ouvre, il faut vous positionner dans le bon dossier (celui qui correspond au
dossier bin de votre projet. Si vous ne savez pas comment faire, commencez par repérer
sur quel disque (la lettre du disque) se trouve votre projet et tapez la lettre suivi de « : »
puis validez. Par exemple :

Ensuite, il faut accéder au bon dossier. Pour cela, dans l’explorateur, positionnez vous
sur le bon dossier (le dossier bin de Urban Marginal) et copiez le chemin complet qui se
trouve dans la barre d’adresse, tout en haut. De retour dans votre fenêtre d’invite de
commandes, tapez cd suivi d’un espace et de l’ouverture de guillemets, comme ceci :

Travaux pratiques
Dans la fenêtre, faites un clic droit et « copier ». Le chemin complet va se copier à côté
des guillemets. Il ne reste plus qu’à fermer les guillemets et valider. L’invite a alors chan- Urban Marginal
gé en tenant compte du chemin complet.
Vous pouvez alors lancer l’application en tapant ceci : Page 65

java controleur.Controle

Ainsi, vous sollicitez java qui est la machine virtuelle pour interpréter le code des classes,
et juste après vous avez mis le nom de la classe de démarrage de l’application, qui se
trouve dans le package controleur. Validez.
Il est possible que votre firewall s’active et vous demande s’il faut bloquer ou non cette
application : vous demandez de ne pas bloquer.
Si tout va bien, la frame de démarrage du jeu s’ouvre ! Mais, si vous cliquez sur Démarrer,
l’arène s’ouvre sans l’image de fond ni les murs… Pourquoi ? Eclipse a su retrouver le
dossier media car pour Eclipse, le projet se situe au niveau du dossier « Urban Marginal ».
En lançant le projet directement sans passer par Eclipse, les chemins sont basés à partir
du dossier bin. Donc 2 solutions : soit on déplace le dossier media, soit on change son
chemin dans le code. Faisons la seconde solution : dans l’interface Global, modifiez la
constante CHEMIN en mettant :
CHEMIN = "../media/"
Faites un test sous Eclipse : ce test va surtout servir à précompiler le code et donc modi-
fier le fichier Global.class. Cependant, en cliquant sur Demarrer, remarquez que c’est
cette fois sous Eclipse que vous ne voyez plus les images ! Arrêtez l’exécution.
De retour dans l’invite de commandes, relancez le test et cliquez sur Demarrer. Cette fois
tout marche bien. Pour lancer un client, vous êtes obligé d’ouvrir une nouvelle fenêtre
d’invite de commandes et de refaire les manipulations précédentes. Faites le et testez un
client. Vous pouvez de la même manière tester un second client.

8 2950 TP PA 00
Pour faciliter les tests, rien ne vous empêche de créer un petit fichier de commande (de
type bat sous dos, par exemple) qui contiendra les lignes de commandes nécessaires pour
lancer l’application.

Tester sur des ordinateurs différents


Pour réaliser le test sur des ordinateurs différents, copiez le dossier Urban Marginal sur
un autre ordinateur (en réalité seuls les dossiers bin et media sont nécessaires) et vous
pouvez ainsi tester à distance mais attention, il y a tout de même 3 règles à respecter :
• lorsque vous lancez un client, il faut cette fois taper l’adresse ip réelle du serveur ;
• si le serveur est derrière un routeur, contrôlez que le port 6666 est ouvert (vous
pouvez aussi changer de port d’écoute) ;
• les ordinateurs qui lancent l’application doivent avoir la machine virtuelle. Elle est
automatiquement installée avec Eclipse ou éventuellement d’autres logiciels qui
nécessitent la machine virtuelle, ou même juste le JDK que vous avez installé au tout
début de ce TP. Si la commande java n’est pas reconnue, alors la machine virtuelle
n’est pas présente. Si vous n’avez pas besoin d’installer le JDK, il faudra installer au
minimum le JRE (l’environnement d’exécution de java, qui bien sûr dépend de la
plate-forme). Vous pouvez télécharger le JRE sur le site de Sun :
http://java.com/fr/download/help/download_options.xml

15. Mini mémento Java


Travaux pratiques
Attention, en Java (comme en C++ et tous les langages inspirés du C), les majuscules et
Urban Marginal minuscules ont une importance : else est différent de Else et de ELSE. Cela est valable
aussi bien pour les mots réservés que pour les noms de variables.
Page 66 Pensez qu’Eclipse colorise le code : contrôlez systématiquement que ce que vous tapez se
met bien dans la couleur attendue (un mot réservé, par exemple, va être en gras).
Voici quelques correspondances entre l’algo et le langage Java :
Algo Java
// commentaire d’une ligne
/* commentaire
de plusieurs
lignes */
Les types : (dans le même ordre : )
entier, entier long, int ou Integer, long, float, char, String, boolean
réel, caractère, Remarque : Integer est une classe, int est un type simple. Tous les types qui
chaîne, booléen commencent avec une majuscule sont des classes.
privé : nomPropriété : private typePropriété nomPropriété ;
typePropriété exemple :
exemple : private String nom ;
privé : nom : chaîne
nomVariable : typeVariable nomVariable ;
typeVariable exemple :
exemple : int k ;
k : entier
nomVariable Å valeur nomVariable = valeur ;

8 2950 TP PA 00
Algo Java
signes de comparaison et logique : (dans le même ordre : )
=, <>, <, >, <=, >=, ==, !=, <, >, <=, >=, !, &&, ||
non, et, ou Remarque : attention de bien mettre == lors d’une comparaison (dans un
if, par exemple) car le = tout seul ne va pas provoquer d’erreur, mais ne
fera pas la même chose.
opérations : (dans le même ordre : )
+, -, *, /, div, mod +, -, *, /, /, %
Remarque : il n’y a pas de division entière (div) mais une division simple
avec une affectation dans une variable de type entier donne le même
résultat.
Quelques raccourcis d’écriture
(non obligatoires mais bien
pratiques) : A++
A Å A + 1 A += B
A Å A + B A--
A Å A – 1 A -= B
A Å A – B A *= B
A Å A * B A /= B
A Å A / B
si condition alors if (condition) {
action action ;
finsi }
Remarque : attention de ne pas oublier les parenthèses autour de la
condition (obligatoire pour toutes les conditions, que ce soit dans les if, Travaux pratiques
while etc…)
Urban Marginal
si condition alors if (condition) {
action1 action1 ;
Page 67
sinon }else{
action2 action2 ;
finsi }
cas de (variable) switch (variable) {
valeur1 : traitement1 case valeur1 :
... traitement1 ;
valeurN : traitementN break ;
fincas ...
case valeurN :
traitementN ;
break ;
}
Remarque : ne pas oublier le break en fin de chaque cas (sinon les cas
suivants sont traités)
tantque condition while (condition) {
action action ;
fintantque }
repeter do {
action action ;
jusqu’à condition }while(condition) ;
attention c’est la condition pour rester dans la boucle (while)

8 2950 TP PA 00
Algo Java
pour k de deb à fin for (k = deb ; k <= fin ; k++) {
action action ;
finpour }
privé, public, protégé private, public, protected
procedure nomProc void numProc (type1 param1, …, typeN paramN) {
(param1 : type1, …, déclarations ;
paramN : typeN) traitements ;
déclarations }
debut Remarque : void signale l’absence de type retourné, donc la procédure.
traitements Dans le cas d’un constructeur, il n’y a même pas void : on ne met rien
fin devant le nom de la méthode. En revanche une méthode de classe doit
être précédée de private, public ou protected.
fonction nomFonc (…) : typeRetour nomFonc (…) {
typeRetour déclarations ;
déclarations traitements ;
debut return valeur ;
traitements }
retourner valeur
fin
classe nomClasse public class nomClasse {
privé :
propriété : private typePropriété propriété
Travaux pratiques typePropriété
public : public nomClass(…) {
Urban Marginal nomClasse(…) // …
constructeur
Page 68 debut }
… public void methodeProc(…) {
fin …
methodeProc(…)
debut }
… public type methodeFonct(…) {
fin …
methodeFonct(…) :
type return valeur ;
debut }

retourner valeur
fin
classe nomClasse hérite public class nomClasse extends classeMere {
de classeMere

8 2950 TP PA 00

Vous aimerez peut-être aussi