Sunteți pe pagina 1din 33

Ralisation d'un jeu de bataille navale en C++

par khayyam90

Date de publication : 16/01/2007

Dernire mise jour :

Ce tutoriel va montrer comment raliser un jeu de bataille navale en rseau.


J'y expliquerai une architecture de classes permettant de s'abstraire
compltement du ct graphique, et de l'aspect rseau. Ce jeu de bataille
navale devra permettre de jouer en rseau la bataille navale et de changer
l'interface graphique. Prrequis : C++, un peu de SDL et un peu de Irrlicht.
Ralisation d'un jeu de bataille navale en C++ par khayyam90

A - Architecture globale
I - Architecture en couches
II - Ecriture abstraite du jeu
II-1 - Classe abstraite de la couche de prsentation
II-2 - Classes de gestion des donnes
II-3 - Ecriture un peu moins abstraite du jeu
B - Couche basse : le rseau
I - Les sockets
II - Echange des donnes au dbut du jeu
III - Les changes de donnes au cours du jeu
C - Couche haute : l'affichage SDL
I - Ecran de choix Client/Serveur
II - Ecran de placement des bateaux
III - Ecran du jeu proprement dit
IV - L'cran final
V - Quelques points retenir
D - Couche haute : l'affichage Irrlicht
I - Cration de l'interface Irrlicht
II - Ecran de choix Client/Serveur
III - Ecran de placement des bateaux
IV - Ecran du jeu proprement dit
V - Mise jour de l'cran
VI - Les crans finaux
VII - Lecture bloquante non bloquante
E - On revient au jeu dans sa globalit
I - Fonctionnement du jeu
II - Tlchargement
III - Remerciements

-2-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

A - Architecture globale

I - Architecture en couches

Si on veut pouvoir sparer les diffrents aspects du jeu, il va nous falloir une architecture en couches. Le jeu sera
donc organis en 3 couches : la couche rseau, la couche de manipulation des donnes et la couche d'affichage.
On peut mme voir cette architecture comme l'architecture OSI d'un rseau : les cinq premires couches
correspondent la couche rseau (on se base sur la couche 5 : socket), la couche 6 correspond la couche de
manipulation des donnes et la dernire couche (7, prsentation) s'occupe de l'interface graphique. Il suffira de
remplacer une couche par une autre pour changer un aspect du jeu. Par contre nous avons une contrainte : les
couches doivent tre dfinies comme des interfaces implmentes par les versions des couches.

Je vais dtailler la cration de deux versions de la couche de prsentation : une version en utilisant SDL et une
version utilisant Irrlicht.

Voici le schma de notre architecture :

La rflexion doit se faire sur la manipulation des donnes du jeu et non sur la manire de les afficher. Il faut
dterminer tous les tats dans lesquels le jeu pourra se trouver. La couche de prsentation permettra les
oprations suivantes :

choisir si le joueur veut crer une partie rseau ou bien en rejoindre une
positionner les bateaux sur la grille de jeu
choisir la case de la grille attaquer
afficher le rsultat du jeu

La classe abstraite pour notre couche de prsentation devra donc proposer ces diffrentes mthodes. Et elles
seront implmentes dans les classes filles. Ainsi, toute classe implmentant ces mthodes pourra tre utilise
comme couche de prsentation. On pourra tout fait imaginer une couche qui gre tout par des fichiers et qui
n'affiche rien. Ca n'influera pas le moins du monde.

II - Ecriture abstraite du jeu

-3-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

Le jeu doit pouvoir tre conu sans avoir tenir compte de la couche de prsentation. Tout ce qu'on a le droit de
faire, c'est d'appeler les mthodes virtuelles de l'interface.

crer la couche de prsentation comme drive de l'interface


choisir le mode client ou serveur
chacun positionne ses bateaux sur sa grille
selon ce mode client ou serveur, crer ou rejoindre une partie
tant que les deux joueurs ont encore des bateaux
lancer le jeu, avec des attaques tour de rle entre les joueurs
afficher le rsultat perdu ou gagn

II-1 - Classe abstraite de la couche de prsentation

Voici la description de l'interface pour la couche de prsentation :

class Gfx{
public:
virtual int choose_client_server() = 0;
virtual int positionner_bateaux(Flotte&) = 0;
virtual std::pair<int,int> choisir_attaque(Essais, Flotte)=0;
virtual void affiche_gagne()=0;
virtual void affiche_perdu()=0;
virtual void update(Essais, Flotte)=0;
virtual ~Gfx(){}
};

On remarquera que nulle part il n'est question de SDL ou de Irrlicht. C'est une classe abstraite. Les liens avec la
couche infrieure (la couche de manipulation des donnes) sont faits par les passages de paramtres de type
Essais et Flotte.

II-2 - Classes de gestion des donnes

Les deux classes Flotte et Essais sont les classes permettant de grer toutes les donnes du jeu. Flotte gre tous
les bateaux du joueur, leur position, leurs dgts. Tandis que Essais gre ce que le joueur a dj attaqu chez
l'adversaire et ce que l'adversaire a dj essay chez le joueur.

Le code de ces deux classes est trs classique, il ne contient que de la manipulation et du stockage d'informations
:

class Flotte{
public:
std::vector<Bateau> _lb;
void placer_aleatoirement();
int get_effectif();
int get_effectif_depart();
private:
int effectif_depart;
};

La classe Flotte contient une fonction intressante : placer_aleatoirement. Toujours dans un souci de simplicit je
place les bateaux alatoirement sur la grille. On se contente de chercher un arrangement des bateaux de telle
sorte qu'ils ne sortent pas de la grille et qu'ils ne se chevauchent pas.

La classe Bateau est toute aussi triviale. Elle stocke la position, l'orientation, la taille et les dgts d'un bateau ainsi
que les fonctions associes. J'ai choisi de ne stocker que la position du dbut d'un bateau. La taille et l'orientation

-4-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

nous permettent de dduire les autres positions.

class Bateau{
public:
Bateau();
Bateau(std::string&, std::pair<int,int>, int, bool);
// touch ou dans l'eau ?
bool isHit(std::pair<int,int>&);
// modifie un point du bateau. si tout est touch, on renvoie true = coul
bool setDegats(std::pair<int,int>&);
// accesseurs, modificateurs
// ...
private:
std::string _nom;
std::pair<int,int> _pos1;
// pour se souvenir de l'orientation du bateau
bool _horizontal;
int _taille;
std::vector<bool> _degats;
};

La classe Essais est un conteneur de tableaux, rien de trs extraordinaire :

class Essais{
public:
Essais();
// renvoie le nombre de coups au but
int get_effectif_adverse();
// renvoie un boolen pour savoir si un pair est dans la map
bool is_in(const std::string&, std::pair<int,int>&);
// ajoute un lment dans un vector de la map
void add(const std::string&, std::pair<int,int>&);
// renvoie la taille d'un vector
int get_size(const std::string&);
// renvoie un lment de l'un des vectors
std::pair<int,int> get(const std::string&, int);
private:
// les donnes. plusieurs vectors rfrencs par leur nom
std::map<std::string, std::vector< std::pair < int,int > > > _d;
};

Je vous fais grce du code de ces fonctions, tout se retrouve dans l'archive contenant les fichiers sources.

Avec simplement ces classes, on peut crire tout le jeu (du moins les couches manipulation des donnes et
prsentation).

II-3 - Ecriture un peu moins abstraite du jeu

Je vous remets le schma gnral du jeu :

crer la couche de prsentation comme drive de l'interface


choisir le mode client ou serveur
chacun positionne ses bateaux sur sa grille

-5-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

selon ce mode client ou serveur, crer ou rejoindre une partie


tant que les deux joueurs ont encore des bateaux
lancer le jeu, avec des attaques tour de rle entre les joueurs
afficher le rsultat perdu ou gagn

Si on met a en C++, a va nous donner quelque chose comme :

// cration de la couche de prsentation


Gfx *ma_gui = new IrrInterface; // ici une interface Irrlicht
// choix client ou serveur
int server = ma_gui->choose_client_server();
switch(server){
case 0:{ // serveur
Server s;
// cration du serveur, attente du client, change d'informations
// ...
// le serveur attend que le client ait positionn ses bateaux
s.wait();
// le serveur positionne ses bateaux sur sa grille
if (ma_gui->positionner_bateaux(f)==1){ // on souhaite quitter
break;
}
// droulement du jeu, dtaill par la suite
// ...
// quelqu'un a perdu, on affiche un cran
// if perdu
ma_gui->affiche_perdu();
//else
ma_gui->affiche_gagne();
break;
}
case 1:{ // client
Client c;
// cration du client, connexion au serveur, change d'informations
// ...
// le client positionne ses bateaux sur sa grille
ma_gui->positionner_bateaux(f);
// le client informe le serveur qu'il a plac ses bateaux
c.ok();
// droulement du jeu, dtaill par la suite
// ...
// quelqu'un a perdu, on affiche un cran
// if perdu
ma_gui->affiche_perdu();
//else
ma_gui->affiche_gagne();
break;
}
case 2 : // quitter
break;
}
delete ma_gui;

Bien, arrivs ici, le jeu tourne (enfin, sur le papier), les donnes sont correctement mises jour (dans la classe de
manipulation des donnes - Flotte et Essais), on peut maintenant s'occuper un peu du rseau pour grer la couche
basse.

-6-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

-7-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

B - Couche basse : le rseau

I - Les sockets

Toute la communication rseau se fera au travers de sockets. Pour des raisons de simplicit, je me suis appuy
sur une classe de socket permettant d'utiliser facilement des objets strings. Chaque joueur va instancier sa couche
basse en client ou en serveur.

Voici la classe de base dont vont hriter les classes Client et Serveur :

class Joueur{
public:
Joueur(Gfx*);
void attaque(std::pair<int,int>, Essais&);
void defend(Flotte*, Essais*);
void wait();
void ok();
~Joueur();
// plus une fonction dfinie en thread
// ...
protected:
Gfx *_la_gui; // pointeur vers l'interface graphique
Socket *_s;
};

Les fonctions wait et ok servent juste synchroniser les deux joueurs. J'utilise des sockets bloquantes, on peut
donc synchroniser avec des simples envois/rceptions.

La fonction attaque envoie des coordonnes attaquer, attend la rponse de l'adversaire et met jour la structure
Essais passe par rfrence. Idem pour la fonction defend.

Chaque fois qu'un joueur appellera la fonction attaque, il faudra que l'adversaire soit en bloqu sur la rception
dans la fonction defend.

Les classes filles Client et Serveur sont sans surprise :

class Client : public Joueur{


public:
Client(Gfx*g):Joueur(g){};
// host port
int connect(const std::string& , int);
// filename
int connect(const std::string&);
Flotte get_game_data();
};

class Server : public Joueur{


public:
Server(Gfx *g):Joueur(g){};
int open_socket(int);
void wait_for_client();
Flotte get_game_data();
int send_game_data_to_client(Flotte&);
~Server();
private:
SocketServer *ze_socket;
};

-8-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

On remarque les fonctions get_game_data et send_game_data_to_client qui vont raliser l'change de donnes
au dbut du jeu. Cet change du serveur vers le client nous garantit que les deux joueurs joueront avec les mmes
donnes, choisies par le serveur. Les donnes changes sont simplement le nombre et la taille des bateaux
placer sur la grille.

Le serveur peut acqurir ces donnes de la manire qui lui plat, j'ai choisi de les lire depuis un simple fichier texte.

Les deux fonctions Client::connect permettent simplement de choisir l'adresse de connexion. La premire version,
spcifie en dur l'adresse et le port du serveur tandis que la seconde version va chercher ces donnes dans un
fichier texte.

II - Echange des donnes au dbut du jeu

L'change des informations du serveur vers le client au dbut du jeu est on ne peut plus classique : on se contente
d'envoyer le nom du bateau et sa taille, et ce pour chaque bateau. On pourrait amliorer l'change de donnes en
n'envoyant que le nombre de bateau de chaque type.

int Server::send_game_data_to_client(Flotte &f){


// on attend que le client soit prt recevoir
wait();
// il faut envoyer la flotte au client
std::vector<Bateau>::iterator i;
string a_envoyer;
// on envoie le nombre de lignes qu'on va envoyer
ostringstream oss; oss << f._lb.size(); a_envoyer = oss.str();
_s->SendLine(a_envoyer);
// on boucle sur tous les bateaux
for (i = f._lb.begin(); i!=f._lb.end(); i++){
ostringstream oss;
oss << i->get_taille();
// on envoie le nom du bateau et sa taille
a_envoyer = i->get_nom() + ":"+ oss.str();
cout << a_envoyer << endl;
_s->SendLine(a_envoyer);
}
cout << "Donnes envoyes au client" << endl;
cout << "########################" << endl;
return 0;
}

La fonction de rception, ct client est exactement la duale :

Flotte Client::get_game_data(){
// on avertit le serveur qu'on est prt
ok();
Flotte f;
string recu;
recu = _s->ReceiveLine();
istringstream iss(recu);
int nb_a_recevoir;
iss >> nb_a_recevoir;
// on boucle sur chaque bateau recevoir
for (int i=0; i<nb_a_recevoir; i++){
recu = _s->ReceiveLine();
cout << recu << endl;
// sparation des donnes pour crer la flotte
// on dcoupe la chane suivant le :
string nom;
string taille;

-9-
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

int itaille;
istringstream iss(recu);
std::getline( iss, nom, ':' );
std::getline( iss, taille, ':' );
istringstream staille(taille);
staille >> itaille;
// on rajoute les bateaux dans la flotte de dpart, ils ne sont pas positionns
Bateau b(nom, pair<int,int>(0,0), itaille, false);
f._lb.push_back(b);
}
cout << "Donnes du jeu reues" << endl;
cout << "#######################" << endl;
return f;
}

On remarquera que les coordonnes des bateaux ne sont pas envoyes. D'une part a n'est pas utile, mais aussi
le principe de la bataille navale et de ne pas dire o sont placs les bateaux :)

III - Les changes de donnes au cours du jeu

L'autre change qu'il peut y avoir dans le jeu de bataille navale concerne chaque action des joueurs. Chaque fois
qu'un joueur dcide d'attaquer une case, on envoie les coordonnes l'adversaire qui va renvoyer le rsultat.

void Joueur::attaque(pair<int,int> c, Essais &e){


string r;
// on passe le pair en 2 string et on les envoie
ostringstream oss1, oss2;
oss1 << c.first;
_s->SendLine(oss1.str());
oss2 << c.second;
_s->SendLine(oss2.str());
// on rcupre le rsultat de l'attaque
r = _s->ReceiveLine();
// on interprte
if (r == "dans l'eau\n")
e.add("dans l'eau", c);
else // touch ou coul
e.add("touches", c);
}

Et la fonction duale :

void Joueur::defend(Flotte &f, Essais &e){


string r;
// Rception des coordonnes
r = _s->ReceiveLine();
istringstream iss1(r);
int n1; iss1 >> n1;
r = _s->ReceiveLine();
istringstream iss2(r);
int n2; iss2 >> n2;
// Cration de la coordonne
pair<int,int> coup(n1,n2);
// est-on touch ?
int num = f._lb.size();
for (int i=0; i<num; i++){
if (f._lb[i].isHit(coup)){
// Est-ce que le bateau est coul ?
if (f._lb[i].setDegats(coup)){
r = f._lb[i].get_nom()+" coule";

- 10 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

_s->SendLine(r);
return;
}else{
// Sinon il est simplement touch !
r = "touche";
_s->SendLine(r);
return;
}
}
}
// Rien n'a t touch
_s->SendLine("dans l'eau");
e.add("ploufs adverses", coup);
}

Pour changer la couche basse, il suffira de rimplmenter les classes Socket et Joueur pour par exemple utiliser
SDL_net ou Qt au lieu de winsock comme c'est le cas actuellement.

Passons maintenant la partie la plus intressante : la couche haute dont je vais expliquer deux versions : SDL et
Irrlicht

- 11 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

C - Couche haute : l'affichage SDL

Vous pouvez dj trouver pas mal de documentation sur SDL sur internet : sur le site officiel libsdl.org ou bien ici
mme sur developpez.com.

Pour raliser une couche haute pour l'affichage, il nous faut implmenter les fonctions virtuelles de la classe
interface Gfx.

class SDLinterface : public Gfx{


public:
SDLinterface();
int choose_client_server();
int positionner_bateaux(Flotte&);
std::pair<int,int> choisir_attaque(Essais, Flotte);
void update(Essais, Flotte);
void affiche_gagne();
void affiche_perdu();
virtual ~SDLinterface();
private:
void dessiner_2_grilles(Essais, Flotte);
// tous les sprites
SDL_Surface *screen;
SDL_Surface *creer, *rejoindre, *quitter, *fond1, *placez, *instructions, *fond_grille,
*bateau, *interface_sdl,
*fond_jeu, *petit_bateau, *degats, *plouf, *a_vous, *en_attente, *gagne, *perdu;
};

On remarque qu'il y a toutes les fonctions de Gfx mais aussi des fonctions propres l'interface SDL. On remarque
aussi tous les attributs de type SDL_Surface * pour stocker tous les sprites que nous allons utiliser dans le jeu. Les
sprites sont chargs dans le contructeur et sont dtruits dans le destructeur.

I - Ecran de choix Client/Serveur

Je me suis appuy sur le tutoriel de fearyourself sur la ralisation de menus en SDL pour raliser le premier cran
du jeu : le choix entre le mode client et le mode serveur.

Pour rsumer :

on charge les sprites relatifs aux diffrents items du menu en spcifiant un indice de transparence
on positionne les sprites
on cre des rectangles aux dimensions et positions des sprites
lors d'un clic on vrifie si on se situe dans l'un de ces rectangles pour dclencher une procdure

int SDLinterface::choose_client_server(){
// on affiche un menu
SDL_BlitSurface(fond1, NULL, screen, NULL);
// on positionne les boutons
SDL_Rect r;
r.x = 160; r.y = 100; SDL_BlitSurface(creer, NULL, screen, &r);
SDL_Rect r0; r0.w = creer->w; r0.h=creer->h; r0.x=r.x; r0.y=r.y;
r.x = 97; r.y = 200; SDL_BlitSurface(rejoindre, NULL, screen, &r);
SDL_Rect r1; r1.w = rejoindre->w; r1.h=rejoindre->h; r1.x=r.x; r1.y=r.y;
r.x = 281; r.y = 400; SDL_BlitSurface(quitter, NULL, screen, &r);
SDL_Rect r2; r2.w = quitter->w; r2.h=quitter->h; r2.x=r.x; r2.y=r.y;
SDL_Flip(screen);
// on lance une boucle pour attraper un clic sur un bouton

- 12 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

bool clic_correct = false;


int numero_clique=-1;
SDL_Event event;
while (!clic_correct){
SDL_WaitEvent(&event);
switch(event.type){
case SDL_QUIT:
clic_correct = true;
numero_clique = 2;
break;
case SDL_MOUSEBUTTONUP:
if (event.button.x > r0.x && event.button.x < r0.x+r0.w && event.button.y > r0.y &&
event.button.y < r0.y+r0.h){
numero_clique = 0;
clic_correct = true;
}else
if (event.button.x > r1.x && event.button.x < r1.x+r1.w && event.button.y > r1.y &&
event.button.y < r1.y+r1.h){
numero_clique = 1;
clic_correct = true;
}else
if (event.button.x > r2.x && event.button.x < r2.x+r2.w && event.button.y > r2.y &&
event.button.y < r2.y+r2.h){
numero_clique = 2;
clic_correct = true;
}
break;
}
SDL_Flip(screen);
}
// 1 = client
// 0 = serveur
// 2 = quitter
return numero_clique;
}

Ce qui nous affiche :

- 13 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

II - Ecran de placement des bateaux

L'tape suivante est d'implmenter la fonction permettant de placer les bateaux sur la grille. J'ai choisi une grille
11x11 (allez savoir pourquoi). Une pression sur la touche A rarrange les bateaux sur la grille et une pression sur
Entre valide le choix et passe l'cran suivant. Je choisis d'afficher un sprite carr sur chaque position occupe
par un bateau.

int SDLinterface::positionner_bateaux(Flotte& f){


// on affiche la grille et le texte
SDL_BlitSurface(fond_grille, NULL, screen, NULL);
SDL_Rect r;
r.x = 150; r.y = 1; SDL_BlitSurface(placez, NULL, screen, &r);
r.x = 544; r.y = 75; SDL_BlitSurface(instructions, NULL, screen, &r);
SDL_Event event;
bool clic_correct = false;
while (!clic_correct){
SDL_WaitEvent(&event);
switch(event.type){
case SDL_QUIT:
clic_correct = true;
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
case SDLK_a:{
// on place alatoirement les bateaux sur la grille
f.placer_aleatoirement();
// afficher le rsultat du placement alatoire
SDL_BlitSurface(fond_grille, NULL, screen, NULL);
r.x = 150; r.y = 1; SDL_BlitSurface(placez, NULL, screen, &r);
r.x = 544; r.y = 75; SDL_BlitSurface(instructions, NULL, screen, &r);
// et boucler pour afficher tous les petits carrs des bateaux

- 14 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

for (int i=0; i<int(f._lb.size()); i++)


for (int j=0; j<f._lb[i].get_taille(); j++){
r.x = (f._lb[i].is_horizontal()==true) ? f._lb[i].get_pos().first
+ j : f._lb[i].get_pos().first;
r.y = (f._lb[i].is_horizontal()==false) ? f._lb[i].get_pos().second
+ j : f._lb[i].get_pos().second;
r.x*=48;
r.y*=48;
// la grille commence au point 7,63
r.x+=7;
r.y+=63;
SDL_BlitSurface(bateau, NULL, screen, &r);
}
break;
}
case SDLK_RETURN:
clic_correct = true;
break;
default:
break;
}
break;
}
SDL_Flip(screen);
}
return 0;
}

On remarquera les oprations directement sur le placement des sprites sur l'image pour les cler au pixel prs sur
la grille. Chaque sprite fait 48 pixels de ct. Et en effet, les coordonnes stockes dans la couche de manipulation
de donnes sont comprises entre 0 et 10, je dois les remettre l'chelle pour les faire coller au sprite.

Ce qui nous donne :

- 15 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

III - Ecran du jeu proprement dit

Cet cran permet au joueur de cliquer sur la case qu'il souhaite attaquer. Il permet aussi de visualiser d'un seul
coup d'oeil les endroits dj attaqus, les coups au but, et les coups que l'adversaire a dj faits.

On place gauche les bateaux du joueur et droite les bateaux de l'adversaire.

- 16 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

On se contente d'attendre un clic sur la grille de l'adversaire, de vrifier que la case clique n'a pas dj t
clique, puis on renvoie les coordonnes cliques.

On remarquera que les grilles de jeu n'ont pas la mme dimension que dans l'cran prcdent, les oprations pour
caler les sprites sur les pixels ne sont donc plus tout fait les mmes.

pair<int,int> SDLinterface::choisir_attaque(Essais e, Flotte f){


// on attend un clic sur une case, pas encore attaque
pair<int,int> endroit;
dessiner_2_grilles(e,f);
SDL_Rect r;
r.x = 280;
r.y = 140;
SDL_BlitSurface(a_vous, NULL, screen, &r);
SDL_Flip(screen);
SDL_Event event;
bool clic_correct = false;
while (!clic_correct){
SDL_WaitEvent(&event);
switch(event.type){
case SDL_QUIT:
clic_correct = true;
endroit.first = endroit.second = -1;
break;
case SDL_MOUSEBUTTONUP:
if ((event.button.x > 410) && (event.button.y > 200)){
endroit.first = event.button.x-410;
endroit.second = event.button.y-200;
endroit.first/= 35;
endroit.second/=35;
if (endroit.first >= TAILLE_GRILLE) endroit.first = TAILLE_GRILLE-1;
if (endroit.second >=TAILLE_GRILLE) endroit.second= TAILLE_GRILLE-1;

- 17 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

bool deja_tire = false;


// on vrifie qu'on n'a pas dj tir ici
deja_tire = e.is_in("dans l'eau", endroit) || e.is_in("touches", endroit);
if (deja_tire)
clic_correct = false;
else
clic_correct = true;
}
break;
}
SDL_Flip(screen);
}
// redessiner l'cran avec le "en attente"
update(e,f);
return endroit;
}

On remarque l'appel la fonction update qui met jour l'cran avec le rsultat de l'attaque (un coup dans l'eau ou
un bateau touch). Cette fonction se contente d'afficher tous les sprites.

IV - L'cran final

Le dernier cran affiche simplement si le joueur a perdu ou gagn. Puis on attend qu'il appuie sur une touche pour
quitter.

void SDLinterface::affiche_gagne(){
SDL_BlitSurface(fond1, NULL, screen, NULL);
SDL_Rect r;
r.x = 127;
r.y = 100;
SDL_BlitSurface(gagne, NULL, screen, &r);
SDL_Flip(screen);
SDL_Event event;
bool clic_correct = false;
while (!clic_correct){
SDL_WaitEvent(&event);
switch(event.type){
case SDL_QUIT:
clic_correct = true;
break;
case SDL_KEYDOWN:
clic_correct = true;
break;
}
}
}

L'affichage quand le joueur a perdu est sensiblement le mme, on change juste le sprite afficher.

- 18 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

V - Quelques points retenir

Tous les vnements sont directements grs dans la boucle d'affichage. C'est la raison pour laquelle on se
retrouve avec un gros switch dans chaque cran. Ca ne sera plus le cas dans l'affichage avec Irrlicht.
La scne est reconstruite chaque frame. A chaque frame on rajoute tous les sprites correspondant aux
coups dans l'eau, aux bateaux, aux dgts ... a ne sera plus le cas dans l'affichage avec Irrlicht.
L'interface graphique avec SDL consomme trs peu de ressources systmes, en effet, la boucle d'affichage
ne tourne que lorsque des vnements sont dtects.

- 19 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

D - Couche haute : l'affichage Irrlicht

L'affichage via le moteur 3D Irrlicht va ressembler l'affichage par SDL, il va implmenter les mmes fonctions.
Pour une prsentation du moteur 3D Irrlicht, vous pouvez consulter mon tutoriel sur Irrlicht

I - Cration de l'interface Irrlicht

La cration demande la construction des objets Irrlicht : le device pour afficher l'couteur d'vnements, la camra,
le chargement des meshs et la cration de la skybox.

IrrInterface::IrrInterface(){
// fullscreen
_device = createDevice( video::EDT_OPENGL,core::dimension2d<s32>(800, 600), 32, false);
_device->setWindowCaption(L"Bataille navale");
_driver = _device->getVideoDriver();
_smgr = _device->getSceneManager();
_camera = _smgr->addCameraSceneNode(0, core::vector3df(0,200,-300),
core::vector3df(0,30,-150), -1);
_receiver = new IrrEvent(this); // cration de l'couteur d'vnements
_device->setEventReceiver(_receiver);
// on cre l'interface graphique pour positionner des widgets
_env = _device->getGUIEnvironment();
// cration de la police d'affichage
_skin = _env->getSkin();
_skin->setSize(EGDS_BUTTON_HEIGHT , 100);
_font = _env->getFont("../data/irr/font.bmp");
if (_font){
_skin->setFont(_font);
}
// crer les noeuds des bateaux - ie chargement du mesh
_mcube = _smgr->getMesh("../data/irr/sphere.3ds");
// on cr la skybox
_smgr->addSkyBoxSceneNode(
NULL,
_driver->getTexture("../data/irr/sd_dn.jpg"),
_driver->getTexture("../data/irr/sd_lf.jpg"),
_driver->getTexture("../data/irr/sd_rt.jpg"),
NULL,
_driver->getTexture("../data/irr/sd_bk.jpg"));
}

L'couteur d'vnements est un objet important dans cette interface. En fonction de l'tat du jeu, il dispatche
l'vnement qu'il reoit dans une fonction ddie :

bool IrrEvent::OnEvent(SEvent event){


switch (_ecran){
case ECRAN_CHOIX_CLIENT_SERVEUR:
return EventChoixClientServeur(event);
case ECRAN_POSITIONNEMENT:
return EventChoixPositionnement(event);
case ECRAN_CHOIX_ATTAQUE:
return EventChoixAttaque(event);
case ECRAN_FINI:
return EventFini(event);
default :
return false;
}
}

II - Ecran de choix Client/Serveur

- 20 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

L'cran est trs simple : une image en arrire plan et 3 boutons pour choisir si on veut crer une partie (serveur),
en rejoindre une (client) ou bien quitter.

Je cre des objets dans une interface graphique gre par Irrlicht par l'appel _env->addButton( ... ) ou
_env->addImage( ... ) puis on lance la boucle d'affichage.

int IrrInterface::choose_client_server(){
// mettre une interface pour choisir le mode
_receiver->_ecran = IrrEvent::ECRAN_CHOIX_CLIENT_SERVEUR;
_receiver->_fini = IrrEvent::PAS_FINI;
IGUIImage *img = _env->addImage(
_driver->getTexture("../data/irr/bataille_navale.jpg"),
irr::core::position2d<int>(0,0));
IGUIButton *b1 = _env->addButton(core::rect<s32>(300,100,500,200), 0, 101, L"Creer une partie");
IGUIButton *b2 = _env->addButton(core::rect<s32>(300,250,500,350), 0, 102, L"Rejoindre une
partie");
IGUIButton *b3 = _env->addButton(core::rect<s32>(300,400,500,500), 0, 103, L"Quitter");
while(_device->run() && _receiver->_fini != IrrEvent::FINI){
_driver->beginScene(true, true, video::SColor(255,100,100,100));
_env->drawAll();
_driver->endScene();
// on ralenti le framerate !!!!
Sleep(30);
}
// on retire les objets graphiques
b1->setVisible(false);
b2->setVisible(false);
b3->setVisible(false);
img->setVisible(false);
// si l'utilisateur a ferm la fentre, on quitte
if (!_device->run())
return 2;
switch (_receiver->_etat){
case IrrEvent::QUITTER:
return 2;
case IrrEvent::CREER:
return 0; // serveur
case IrrEvent::REJOINDRE:
return 1; // client
default :{
cerr << "code de retour inconnu pour choix du client/serveur" << endl;
return 2;
}
}
}

La boucle d'affichage tourne tant que la fentre n'a pas t ferme et que le drapeau _receiver->fini n'est pas
positionn FINI. Ce drapeau est mis jour par la fonction de gestion des vnements. Ds qu'un clic sur l'un des
boutons est dtect, le drapeau est mis FINI et la boucle d'affichage peut s'arrter pour passer l'cran suivant.

Le framerate est contrl simplement par un Sleep(), qui n'est pas des plus lgants. Irrlicht permet de calculer
automatiquement le taux de rafrachissement : int irr::video::IVideoDriver::getFPS(). Il suffit ensuite d'ajuster un
Sleep la bonne dure selon la vitesse d'affichage que vous souhaitez. Et bien sr si vous voulez obtenir le
meilleur de votre machine, ne mettez pas de Sleep du tout.

La gestion des vnements est trs simple : chaque vnement on regarde s'il s'agit d'un vnement sur
l'interface graphique, puis on regarde le numro de l'objet qui a dclench l'vnement et on agit en consquence.

bool IrrEvent::EventChoixClientServeur(SEvent event){

- 21 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

// on teste selon l'id de l'objet qui a appel le onEvent


switch(event.EventType){
case EET_GUI_EVENT : {
s32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = _parent->get_device()->getGUIEnvironment();
switch(event.GUIEvent.EventType){
case EGET_BUTTON_CLICKED:{
switch(id){
case 101: // serveur
_etat = CREER;
_fini = FINI;
return true;
case 102: // client
_etat = REJOINDRE;
_fini = FINI;
return true;
case 103 : // quitter
_etat = QUITTER;
_fini = FINI;
return true;
default:
cerr << "id d'objet inconnu dans l'cran client/serveur" << endl;
return false;
}
}
default:
return false;
}
}
default :
return false;
}
return false;
}

III - Ecran de placement des bateaux

- 22 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

Cet cran affiche les deux grilles de jeu et propose un bouton pour placer les bateaux sur la grille du joueur ainsi
qu'un bouton pour valider le choix. Les vnements traits sont donc les clics sur les boutons et les pressions des
touches directionnelles du clavier pour dplacer la camra.

Tout d'abord, je cre les deux grilles de jeu. Pour cela, je cre deux meshs qui vont onduler pour simuler de l'eau.
C'est l'une des fonctions proposes par Irrlicht rapide et simple mettre en place :

int IrrInterface::positionner_bateaux(Flotte &f){


_receiver->_ecran = IrrEvent::ECRAN_POSITIONNEMENT;
creer_tous_les_noeuds(f);
// on met un bouton positionner et un bouton valider
IGUIButton *b1 = _env->addButton(core::rect<s32>(550,420,750,470), 0, 104, L"Placer les
bateaux");
IGUIButton *b2 = _env->addButton(core::rect<s32>(550,500,750,550), 0, 105, L"OK");
IGUIStaticText *t1 = _env->addStaticText(L"Placez vos bateaux",
core::rect<s32>(200,10,500,50));
t1->setOverrideColor(video::SColor(0,255,255,0));
// on place la flotte
_mflotte = _smgr->addHillPlaneMesh("flotte",
core::dimension2d<f32>(40,40),
core::dimension2d<s32>(11,11), 0, 0,
core::dimension2d<f32>(0,0),
core::dimension2d<f32>(100,100));
_smgr->getMeshManipulator()->makePlanarTextureMapping(_mflotte->getMesh(0), 0.025f);
_nflotte1 = _smgr->addWaterSurfaceSceneNode(_mflotte->getMesh(0), 3.0f, 300.0f, 30.0f);
_nflotte1->setPosition(core::vector3df(0,0,0));
_nflotte1->setName((wchar_t*)("flotte1"));
_nflotte1->setMaterialTexture(0, _driver->getTexture("../data/irr/stones.jpg"));
_nflotte1->setMaterialTexture(1, _driver->getTexture("../data/irr/water.jpg"));
_nflotte1->setMaterialType(video::EMT_REFLECTION_2_LAYER);
_nflotte2 = _smgr->addWaterSurfaceSceneNode(_mflotte->getMesh(0), 3.0f, 300.0f, 30.0f);
_nflotte2->setPosition(core::vector3df(0,0,0));
_nflotte2->setName((wchar_t*)("flotte2"));
_nflotte2->setMaterialTexture(0, _driver->getTexture("../data/irr/stones.jpg"));
_nflotte2->setMaterialTexture(1, _driver->getTexture("../data/irr/water.jpg"));
_nflotte2->setMaterialType(video::EMT_REFLECTION_2_LAYER);
_nflotte2->setPosition(core::vector3df(480,0,0));

Le mesh reprsentant l'eau comporte deux textures, qui sont les textures fournies dans les exemples du SDK
Irrlicht. On remarquera juste que j'ai rajout une bordure noire pour afficher les limites des cases sur l'eau.

- 23 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

Dans le fond on aperoit la skybox, avec elle aussi les textures fournies dans le SDK Irrlicht. La camra tant
toujours oriente dans la mme direction, j'ai pu me permettre de ne pas charger le ct arrire ni le ct
suprieur. A la diffrence de l'interface avec SDL, j'ai choisi d'afficher les bateaux en une seule fois et non pas petit
bout par petit bout. Je prends un mesh (reprsentant une sphre) que j'tire pour qu'il ait la bonne taille, puis je le
place sur l'eau.

_receiver->_fini = IrrEvent::PAS_FINI;
while (_device->run() && _receiver->_fini != IrrEvent::FINI){
_driver->beginScene(true, true, video::SColor(255,100,100,100));
_smgr->drawAll();
_env->drawAll();
_driver->endScene();
// on attend que le IrrEvent ait attrap un clic sur le bouton
if (_receiver->_etat == IrrEvent::BATEAUX_POSITIONNES){
// on enlve les bateaux
f.placer_aleatoirement();
// on affiche les nouveaux bateaux
afficher_flotte(f); // la flotte de bateaux, hein
_receiver->_etat = IrrEvent::ATTENTE_VALIDATION;
}
Sleep(30);
}
b1->setVisible(false);
b2->setVisible(false);
t1->setVisible(false);
_txt = _env->addStaticText(L" A vous de jouer", core::rect<s32>(100,10,400,100));
_txt->setOverrideColor(video::SColor(0,255,255,0));
if (_receiver->_fini == IrrEvent::FINI){
return 0;
}
else{
cerr << "le device s'est ferm pendant le positionnement des bateaux" << endl;
return 1; // on ferme

- 24 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

}
}

La fonction de gestion des vnements est lgrement diffrente : les vnements attraper ne sont pas les
mmes.

bool IrrEvent::EventChoixPositionnement(SEvent event){


// on teste selon l'id de l'objet qui a appel le onEvent
switch(event.EventType){
case EET_GUI_EVENT : {
s32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = _parent->get_device()->getGUIEnvironment();
switch(event.GUIEvent.EventType){
case EGET_BUTTON_CLICKED:{
switch(id){
case 104: // placer les bateaux
_etat = BATEAUX_POSITIONNES;
return true;
case 105: // valider le placement de bateaux
if (_etat==ATTENTE_VALIDATION){
_fini = FINI;
}
return true;
// tout autre bouton cliqu
default:
return false;
}
return false;
}
default :
return false;
}
}
// vnement clavier
case EET_KEY_INPUT_EVENT:{
bougerClavier(event);
return true;
}
default :
return false;
}
}

Ce qui nous donne :

- 25 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

Et ds que le joueur a valid son choix, on passe l'tape suivante : le jeu proprement dit.

IV - Ecran du jeu proprement dit

Dans cet cran, il nous faut reprer les clics sur la grille de droite (reprsentant le jeu de l'adversaire) puis reprer
les coordonnes spatiales correspondant ces clics. Irrlicht va lancer un rayon depuis la camra jusqu'aux
coordonnes pointes par la souris, puis dterminer l'intersection avec le mesh de l'eau. Irrlicht dcompose le
mesh de l'eau en octree pour acclrer la recherche d'intersection.

std::pair<int,int> IrrInterface::choisir_attaque(Essais, Flotte){


_receiver->_ecran = IrrEvent::ECRAN_CHOIX_ATTAQUE;
_receiver->_fini = IrrEvent::PAS_FINI;
_receiver->_etat = IrrEvent::ATTENTE_CHOIX; // initialisation
_txt->setText(L" A vous de jouer");
pair<int,int> retour;
while (_device->run() && _receiver->_fini != IrrEvent::FINI){
_driver->beginScene(true, true, video::SColor(255,100,100,100));
_smgr->drawAll();
_env->drawAll();
_driver->endScene();
// on vrifie qu'on a cliqu
if (_receiver->_etat == IrrEvent::CHOIX_FAIT){
// on fait du picking pour trouver les coordonnes cliques sur la flotte
// on a cliqu sur la flotte
// on rcupre les coordonnes du clic sur l'cran, dans le onEvent

- 26 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

// on lance un rayon depuis la camra jusque la position 3D du curseur ->


getRayFromScreenCoordinates
core::line3d< f32 > line =
_smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(
core::position2d<s32> (_mouse.X, _mouse.Y), _camera);
// on a un rayon de la camra vers le point point
// et on rcupre l'intersection avec la flotte
core::vector3df intersection;
core::triangle3df tri;
_nflotte2->setTriangleSelector(_selector);
scene::ISceneNode *selected =
_smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(line);
if (selected != NULL){
string s((char*)(selected->getName()));
if (s == "flotte2"){
// si on a cliqu quelque part sur le slector
if (_smgr->getSceneCollisionManager()->getCollisionPoint(
line, _selector, intersection, tri)){
retour.first = int(intersection.X - 240);
retour.second = int(intersection.Z + 235);
retour.first /=40;
retour.second /=40;
// on vrifie qu'on n'a pas dj tir ici
if (find (_dans_leau.begin(), _dans_leau.end(), retour) == _dans_leau.end()
&&
find (_mesdegats.begin(), _mesdegats.end(), retour) ==
_mesdegats.end()){
if (retour.first < 0 || retour.first >10)
cerr << "soucis dans choisir_attaque
x : " << retour.first << endl;
if (retour.second < 0 || retour.second >10)
cerr << "soucis dans choisir_attaque
z : " << retour.second << endl;
_receiver->_fini = IrrEvent::FINI;
}
}else
// on a cliqu ailleurs que sur le slecteur
_receiver->_etat = IrrEvent::ATTENTE_CHOIX;
}else
// on a cliqu ailleurs que la flotte2
_receiver->_etat = IrrEvent::ATTENTE_CHOIX;
}else
// on n'a cliqu sur aucun noeud
_receiver->_etat = IrrEvent::ATTENTE_CHOIX;
}else
// on n'a pas cliqu
Sleep(30);
}
// on refait juste un affichage pour changer le texte
_txt->setText(L" En attente du joueur ...");
_txt->draw();
if (_receiver->_fini == IrrEvent::FINI)
return retour;
// si on arrive l, c'est que la fentre a t ferme
cerr << "le device a t ferm pendant le choix de l'attaque" << endl;
return pair <int,int>(-1,-1);
}

De la mme manire que dans l'interface avec SDL, il nous fait convertir toutes les coordonnes spatiales pour les
ramener dans un intervalle [0;10], d'o les multiplications par 40 et les ajouts de 240,235...

La fonction d'coute d'vnements associe cet cran est trs simple. Elle modifie le drapeau _etat lors d'un clic
et elle garde jour les coordonnes du curseur stockes dans l'interface Irrlicht.

- 27 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

bool IrrEvent::EventChoixAttaque(SEvent event){


switch(event.EventType){
case EET_KEY_INPUT_EVENT:{
bougerClavier(event);
return true;
}
case EET_MOUSE_INPUT_EVENT:{
_parent->set_mouse(event.MouseInput.X,event.MouseInput.Y);
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN){
if (_etat == ATTENTE_CHOIX)
_etat = CHOIX_FAIT;
}
return true;
}
default :
return false;
}
return false;
}

V - Mise jour de l'cran

Aprs chaque appel rseau, l'tat du jeu peut avoir chang. Et d'ailleurs, il y a souvent un objet rajouter dans la
scne : un indicateur de coup dans l'eau ou bien un indicateur de dgts. Ces indicateurs sont simplement des
sphres blanches ou rouges positionnes aux positions correspondantes. C'est le rle de la fonction update.

void IrrInterface::update(Essais e, Flotte f){


// il faut vrifier que tout Essais est bien prsent
// il ne peut y avoir que le dernier lment de Essais qui risque d'tre manquant
int nb_eau = e.get_size("dans l'eau");
int nb_degats = e.get_size("touches");
int nb_plouf = e.get_size("ploufs adverses");
//ce que j'ai mis dans l'eau
if (nb_eau!=0){
pair<int,int> coup = e.get("dans l'eau", nb_eau-1); // coup est dans (0,10)
// si le dernier lment dans l'eau est dj prsent, alors c'est bon, sinon ...
if (find(_dans_leau.begin(), _dans_leau.end(), coup) == _dans_leau.end()){
scene::ISceneNode *n = _smgr->addAnimatedMeshSceneNode(_mcube);
n->setScale(core::vector3df(2,2,2));
n->setMaterialTexture( 0, _driver->getTexture("../data/irr/blanc.jpg") );
// on place le noeud
n->setPosition( core::vector3df(coup.first*40+240 + 10, 0, coup.second*40-235 + 10));
_dans_leau.push_back(coup);
}
}
// les coups que l'adversaire a mis dans l'eau
// idem pour _ploufs_adverses
// ...
// ce que j'ai touch chez l'adversaire
// idem pour _touches
// ...
// ce que le joueur a touch chez moi, les donnes sont stockes dans les bateaux
// idem part que les donnes ne sont pas dans Essais mais dans Flotte
// ...
// on fait 2 affichages pour voir le rsultat immdiat
_driver->beginScene(true, true, video::SColor(255,100,100,100));
_smgr->drawAll();
_env->drawAll();
_driver->endScene();
_driver->beginScene(true, true, video::SColor(255,100,100,100));
_smgr->drawAll();
_env->drawAll();
_driver->endScene();
}

- 28 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

On remarquera qu'on fait 2 affichages la fin de la fonction pour mettre jour l'cran. Pour une raison qui
m'chappe, un seul affichage ne suffit pas.

VI - Les crans finaux

Voil, il nous reste afficher les deux crans Perdu/Gagn. Ces crans sont on ne peut plus simples. Il y a une
simple image en arrire plan et un couteur d'vnement qui va arrter le jeu sur un clic. Voici quand mme le
code :

void IrrInterface::affiche_gagne(){
_receiver->_ecran = IrrEvent::ECRAN_FINI;
_receiver->_fini = IrrEvent::PAS_FINI;
_smgr->clear();
IGUIImage *img = _env->addImage(
_driver->getTexture("../data/irr/gagne.jpg"),
irr::core::position2d<int>(0,0));
while (_device->run() && _receiver->_fini != IrrEvent::FINI){
_driver->beginScene(true, true, video::SColor(255,100,100,100));
_env->drawAll();
_driver->endScene();
Sleep(50);
}
}

bool IrrEvent::EventFini(SEvent event){


switch(event.MouseInput.Event){
case EMIE_LMOUSE_PRESSED_DOWN:
_fini = FINI;
return true;

- 29 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

default:
return true;
}
return false;
}

VII - Lecture bloquante non bloquante

Les plus attentifs auront remarqu que lorsqu'un joueur est en attente de l'autre joueur, son interface est
compltement gele, lecture bloquante oblige. Ca n'est pas trs grave dans le cas de notre interface SDL puisque
celle ci est statique, mais c'est davantage problmatique dans le cas de l'interface Irrlicht. En effet, il faut que les
objets mobiles continuent bouger, il faut que l'eau continue onduler. La lecture socket doit donc devenir une
opration non bloquante. J'ai choisi de raliser a par un thread.

La fonction defend (puisque c'est surtout dans celle-ci que a se passe) va crer un thread pour s'occuper de la
rception. Ainsi, ds que la fonction defend sera appele, elle va rendre la main la fonction suivante, qui dans
notre cas est la fonction d'attente. C'est le thread, qui reste bloqu sur la lecture, qui mettra jour un boolen pour
terminer la fonction d'attente. Ainsi, on peut continuer avoir une boucle d'affichage dans la fonction d'attente, tout
en tant bloqu par la lecture de la socket.

Je n'ai pas utilis d'objets systmes tels que des mutex ou des smaphores mais un simple boolen. En effet, seul
le thread modifiera le boolen. La fonction d'attente ne fera que le lire. Et dans le cas d'un accs concurrent, tout
ce qu'on risque c'est que la boucle d'attente tourne une fois de trop, donc une frame de plus.

Vous pouvez bien videmment grer le thread de la manire qui vous plat. J'ai choisi d'utiliser la mthode dcrite
dans la FAQ C++ pour appeler une fonction membre comme thread. Il ne s'agit que d'un point mineur. Pour plus de
prcisions, consultez la faq ou le code source, dans Joueur::ThreadLauncher.

- 30 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

- 31 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

E - On revient au jeu dans sa globalit

Maintenant qu'on a la couche haute, la couche basse et la couche de manipulation des donnes, on peut crire le
fil directeur du jeu plus en dtail :

Gfx *ma_gui = new IrrInterface;


// choix client ou serveur
int server = ma_gui->choose_client_server();
switch(server){
case 0:{ // serveur
cout << "mode serveur dmarr" << endl;
Server s;
// ouvrir la socket
s.open_socket(1000);
// attendre le client
s.wait_for_client();
// rcuprer les donnes du jeu
Flotte f = s.get_game_data();
// envoyer les donnes du jeu au client
s.send_game_data_to_client(f);
// le serveur attend que le client ait positionn ses bateaux
s.wait();
// le serveur positionne ses bateaux sur sa grille
if (ma_gui->positionner_bateaux(f)==1){ // on souhaite quitter
break;
}
Essais e;
// dbut du jeu
// tant qu'il nous reste des bateaux
// et qu'on n'a pas touch tous ceux de l'adversaire
while (f.get_effectif() > 0 && f.get_effectif_depart()-e.get_effectif_adverse()>0){
pair<int,int> coup = ma_gui->choisir_attaque(e,f);
if (coup==pair<int,int>(-1,-1)) // on quitte
break;
// traitement du tir
s.attaque(coup, e);
ma_gui->update(e,f);
// on a gagn
if (f.get_effectif_depart()-e.get_effectif_adverse() ==0)
break;
s.defend(&f,&e);
ma_gui->attendre();
ma_gui->update(e,f);
}
// quelqu'un a perdu, on affiche un cran
if (f.get_effectif() == 0){
ma_gui->affiche_perdu();
cout << "termin : perdu" << endl;
}else
if (f.get_effectif_depart()-e.get_effectif_adverse() == 0){
ma_gui->affiche_gagne();
cout << "termin : gagn" << endl;
}
break;
}
case 1:
// le dual pour le client
// ...
delete ma_gui;
}

- 32 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale
Ralisation d'un jeu de bataille navale en C++ par khayyam90

Et il vous suffit de changer la premire ligne par l'instanciation d'une autre interface pour avoir tout fait un autre
jeu d'un point de vue graphique. Ensuite pour rendre les deux interfaces vraiment indpendantes, on peut tout
fait les compiler sparment et les utiliser au travers de bibliothques dynamiques dll/so.

I - Fonctionnement du jeu

Pour fonctionner correctement, toutes les dll doivent tre accessibles depuis l'excutable. Il faut aussi que les
donnes du jeu soient accessibles. Dans l'archive des sources, ce rpertoire est accd via ../data. Si vous
changez l'emplacement de l'excutable, assurez-vous que ce rpertoire est toujours accessible. Au besoin,
recompilez les sources avec un autre chemin.

Une partie doit tre cre avant qu'un client ne tente de la rejoindre. Ca peut paratre vident, mais a va mieux en
le disant. Si vous jouez sur des rseaux spars assurez-vous de rediriger le port utilis pour faire une redirection
de port. Par dfaut, le port utilis est le port 1000.

Le joueur qui rejoint la partie est le premier placer ses bateaux. Pendant qu'il les place, le serveur est bloqu en
attente. Puis c'est au serveur de placer ses bateaux. Le serveur est le premier jouer.

II - Tlchargement

Si vous souhaitez juste excuter le jeu, il vous faut tlcharger les DLLs, les binaires et les donnes du jeu.
L'archive des sources n'est utile que si vous voulez consulter le code et/ou le modifier. Le code source est distribu
sous licence GPL.

Les dlls : Irrlicht.dll + SDL.dll + SDL_Image.dll + libpng12.dll 826 Ko


Les sources + le projet Code::Blocks 32 Ko
Les binaires Windows en version SDL et Irrlicht 581 Ko
Les donnes du jeu, ncessaires l'excution 1 Mo

III - Remerciements

Je remercie Julp et fearyourself pour leur relecture et Lon Brelle, qui m'a permis d'utiliser l'une de ses crations :

- 33 -
Copyright 2006 - Pierre Schwartz. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes,
documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E
de domages et intrts.
http://khayyam.developpez.com/articles/cpp/jeux/bataille-navale

S-ar putea să vă placă și