Création d'un jeu Pong en Pascal

Sous Lazarus/Free Pascal

Auteurs

| Laurent Dubettier-Grenier | Alcatîz |

Dans ce tutoriel, nous allons créer pas à pas un Pong à l'aide de l'environnement de développement Lazarus, basé sur le compilateur Free Pascal.

Commentez

- Titre : Création d'un jeu Pong en Pascal

- Auteurs : Laurent Dubettier-Grenier, Alcatîz

- Parution : 29 novembre 2013

- Licence : Le contenu de cet article est rédigé par Laurent DUBETTIER-GRENIER et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- Étape 1 : création de l'interface graphique

- 1e phase : créer un répertoire de travail

- 2e phase : ouvrir Lazarus

- 3e phase : création de l'application Pong

- 4e phase : création du fond d'écran noir

- 5e phase : dimensionnement de la fenêtre du jeu Pong

- 6e phase : gestion de l'arrêt du jeu

- Étape 2 : Création et gestion de la balle

- 1e phase : lancer le jeu

- 2e phase : créer la balle

- 3e phase : déplacer la balle

- 4e phase : automatiser le déplacement de la balle

- 5e phase : l'ordinateur gère le déplacement de la balle

- 6e phase : faire rebondir la balle sur les côtés

- Étape 3 : Gestion des rebonds

- 1e phase : calcul des coordonnées de déplacement de la balle

- 2e phase : préparation à la gestion des rebonds

- 3e phase : gestion du rebond latéral

- 4e phase : gestion des rebonds supérieur et inférieur

- Étape 4 : Création et gestion des raquettes

- 1e phase : création de la raquette droite

- 2e phase : gérer le déplacement de la raquette droite

- 3e phase : faire de même pour la raquette gauche

- 4e phase : limiter le déplacement des raquettes

- 5e phase : gestion du rebond sur la raquette

- Étape 5 : gestion du score

- 1e phase : création des variables

- 2e phase : initialisation des variables

- 3e phase : détection d'un échec et incrémentation du score

- 4e phase : affichage du score sur l'écran

- 5e phase : annoncer le vainqueur

- Étape 6 : Dessiner le filet

- 1e phase : déclaration de la procédure

- 2e phase : création de la procédure

- 3e phase : création du code

- 4e phase : appel de la procédure

- Étape 7 : Corriger les problèmes d'effacement

- 1e phase : déclaration des procédures

- 2e phase : création des procédures

- 3e phase : gestion de l'objet BitMap

- 4e phase : création du code de la procédure EnregistrerEcran

- 5e phase : création du code de la procédure RestaurerEcran

- 6e phase : appel des procédures

- Étape 8 : Écrire les instructions de jeu

- 1e phase : déclaration de la procédure

- 2e phase : création de la procédure

- 3e phase : création du code

- 4e phase : appel de la procédure

- Étape 9 : Jouer des sons

- 1e phase : télécharger les sons

- 2e phase : ajouter la bibliothèque de gestion du son

- 2e phase : déclaration des procédures de gestion du son

- 3e phase : création des procédures

- 4e phase : création du code des procédures

- 5e phase : appel des procédures

- Étape 10 : Améliorations possibles

- Licence de cette documentation

Étape 1 : création de l'interface graphique

Nous allons créer l'interface graphique en six phases :

- création du répertoire de travail ;

- lancement de Lazarus ;

- création de l'application Pong ;

- création du fond d'écran noir ;

- dimensionnement de la fenêtre du jeu Pong ;

- gestion de l'arrêt du jeu.

1e phase : créer un répertoire de travail

Créer un répertoire de travail nommé « Pong » dans votre espace personnel P:.

2e phase : ouvrir Lazarus

Lazarus se lance à partir du menu Démarrer : dans le dossier Lazarus, choisir Lazarus :

3e phase : création de l'application Pong

Créer l'application Pong en cliquant sur Fichier/Nouveau puis choisir Application dans le dossier Project :

Choisir Application.

Choisir ensuite Fichier/Enregistrer tout, et nommer le projet Pong : cela génère un fichier pong.lpi, qui contiendra le jeu Pong.

Bien vérifier que ce fichier est créé dans le dossier Pong créé précédemment sur votre espace personnel (P:).

Enregistrer ensuite le fichier .pas, qui contiendra le code Free Pascal du jeu, dans un fichier nommé unit_pong.pas, que vous enregistrerez toujours dans ce même répertoire de votre espace personnel.

Votre répertoire doit donc contenir plusieurs fichiers, dont les deux fichiers pong.lpi et unit_pong.pas (les extensions de fichier n'apparaîtront pas forcément).

Vérifier que tout fonctionne bien, en compilant et lançant cette application vide, en cliquant sur le bouton Exécuter (triangle vert en haut à gauche) ou en utilisant le raccourci clavier F9 : une fenêtre s'ouvre, identique à celle ci-dessous :

Fermer alors l'application créée.

Remarque : si vous regardez dans le répertoire du projet, un nouveau fichier nommé pong.exe vient d'être créé : il fait plus de 14 Mo…

Pour diminuer la taille de votre exécutable, il faut changer les options de compilation, et supprimer certaines options qui rajoutent beaucoup de code dans le fichier exécutable (pour le débogage) :

Cocher la case :
Projet --> Options du projet --> Options du compilateur --> Génération du code --> Style de l'unité --> Lien intelligent (-CX).

Décocher toutes les cases :
Projet --> Options du projet --> Options du compilateur --> Édition des liens --> Débogage.
Sauf Éliminer les symboles de l'exécutable (-Xs).

Cocher la case :
Projet --> Options du projet --> Options du compilateur --> Édition des liens --> Style de liens --> Lier intelligemment(-XX).

Si vous recompilez votre application (F9), le fichier exécutable obtenu devrait avoir une taille plus petite (2 Mo environ).

4e phase : création du fond d'écran noir

Sous Lazarus, cliquer sur la fenêtre Form1 : dans l'inspecteur d'objet (partie gauche de Lazarus), nous allons définir la couleur (noire) de la fenêtre.

Dans l'onglet Propriétés de l'inspecteur d'objet, choisir la propriété Color et la régler à noir (clBlack). Taper sur Entrée pour activer cette couleur.

Double-cliquer sur la fenêtre Form1, et regarder l'éditeur de source : du code vient d'apparaître à la fin de la page.

delphi

0

1

procedure TForm1.FormCreate(Sender: TObject);
begin
end;

Nous allons ajouter manuellement dans cette procédure la couleur de fond noire. C'est dans cette procédure, qui gère la création de l'application Pong, que nous indiquerons tous les paramètres généraux de notre application.

Rajouter entre begin et end; le code suivant :

Color:= clBlack;

Ne pas oublier le point-virgule en fin de ligne !

Le code devient donc :

delphi

0

1

procedure TForm1.FormCreate(Sender: TObject);
begin
Color:= clBlack;
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

5e phase : dimensionnement de la fenêtre du jeu Pong

Pour dimensionner la fenêtre de notre jeu, nous allons rajouter deux lignes dans le code (fenêtre Éditeur de source), de manière à ce que l'aire de jeu soit un rectangle de 600 par 800 pixels :

delphi

0

1

procedure TForm1.FormCreate(Sender: TObject);
begin
Color:= clBlack;
Height:= 600;
Width:= 800;
end;

Afin de faciliter la suite, nous allons stocker dans une constante les valeurs de largeur et de hauteur de l'aire de jeu :

delphi

0

1

const
HauteurJeu = 600;
LargeurJeu = 800;

Explication : nous affectons à la constante nommée HauteurJeu la valeur 600.

Quelques petites retouches esthétiques, pour centrer la fenêtre d'application dans l'écran et pour supprimer les bordures de fenêtre :

delphi

0

1

const
HauteurJeu = 600;
LargeurJeu = 800;
procedure TForm1.FormCreate(Sender: TObject);
begin
Color:= clBlack;
Height:= HauteurJeu;
Width:= LargeurJeu;
Position:= poScreenCenter;
BorderStyle:= bsNone;
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

Attention, il n'y a plus de possibilité de fermer l'application !

Pour la fermer, il faut cliquer sur le bouton stop (rouge) de Lazarus !

6e phase : gestion de l'arrêt du jeu

Pour régler ce petit problème, nous allons faire en sorte que l'application Pong se ferme lorsque l'on clique sur le bouton Échap (ESC), situé en haut à gauche du clavier.

Dans L'inspecteur d'objets, dans l'onglet Événements, nous allons choisir OnKeyPress, et cliquer sur les trois points qui suivent (…) :

Une nouvelle procédure est créé dans l'éditeur de source :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
end;

C'est cette procédure qui va permettre de gérer la fermeture de notre application Pong lorsque l'on presse la touche Échap.

Il faut rajouter, toujours dans l'éditeur de source, juste sous l'instruction existante Implementation, le code suivant :

delphi

0

1

Implementation
uses
LCLType;

Ceci indique à Lazarus qu'il doit charger la bibliothèque LCLType, contenant les éléments nécessaires à la bonne détection des caractères frappés sur le clavier.

Si l'on parcourait cette bibliothèque, nous trouverions que la touche du clavier Échap est codée par une valeur contenu dans la constante nommée VK_ESCAPE (pour info, c'est le nombre 27).

D'où le code suivant, à insérer dans la procédure FormKeyPress :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
If ord(Key) = VK_ESCAPE then
Close;
end;

Key est une variable de type caractère (char), renvoyée par la procédure FormKeyPress.

VK_ESCAPE est une constante contenant un nombre.

Pour comparer les deux, il faut convertir le caractère Key en sa valeur numérique : c'est ce que fait l'instruction Ord.

À chaque frappe d'un caractère sur le clavier (Événement), la procédure FormKeyPress se lance, et ce qui est situé entre begin et end; s'exécute.

L'instruction If … Then cherche à savoir si le caractère tapé sur le clavier est la touche Échap : si c'est le cas, l'application se ferme.

Vérifier le bon fonctionnement en appuyant sur F9 !

Étape 2 : Création et gestion de la balle

La création et la gestion de la balle s'effectue en six phases :

- lancement du jeu ;

- création de la balle ;

- déplacement de la balle ;

- automatisation du déplacement de la balle ;

- l'ordinateur gère le déplacement de la balle ;

- gestion des rebonds de la balle.

1e phase : lancer le jeu

Pour lancer le jeu, nous allons frapper la touche Espace du clavier.

La constante qui correspond à la touche Espace est nommée VK_SPACE et contient le nombre 32.

Dans la procédure FormKeyPress, nous allons rajouter le code suivant :

delphi

0

1

If Ord(Key) = VK_SPACE then
Begin
End;

2e phase : créer la balle

L'écran de l'application, qui mesure 600 pixels de hauteur et 800 pixels de largeur, a son origine dans le coin en haut à gauche. L'axe horizontal positif va vers la droite, l'axe vertical positif va vers le bas.

La balle va être créée à l'aide du code suivant :

delphi

0

1

Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(0,0,15,15);

Ceci va créer un carré blanc de 15 pixels par 15 pixels, situé à l'origine (coordonnées 0,0), c'est-à-dire en haut à gauche de l'application : (0,0) représente l'origine du carré, et (15,15) l'extrémité du carré.

Pour intégrer ceci, dans la procédure FormKeyPress, nous allons donc rajouter le code suivant :

delphi

0

1

If Ord(Key) = VK_SPACE then
Begin
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(0,0,15,15);
End;

Vérifier le bon fonctionnement en appuyant sur F9 !

Comme précédemment, nous allons créer une constante qui contient la valeur de taille de balle :

delphi

0

1

const
HauteurJeu = 600;
LargeurJeu = 800;
TailleBalle = 15;

Donc le code de la procédure FormKeyPress devient :

delphi

0

1

If Ord(Key) = VK_SPACE then
Begin
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(0,0,TailleBalle,TailleBalle);
End;

Pour créer un rectangle au centre de la fenêtre, il faut donc modifier ainsi le programme :

delphi

0

1

Canvas.Rectangle(400,300,415,315);

Ou plus précisément, en créant une variable de type entière correspondant à la moitié de la hauteur et une autre correspondant à la moitié de la largeur.

Pour cela, déclarer les variables dans la rubrique private de l'éditeur de source :

delphi

0

1

private
MiHauteur: integer;
MiLargeur: integer;

La procédure FormKeyPress devient donc :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
MiHauteur := HauteurJeu div 2;
MiLargeur := LargeurJeu div 2;
If ord(Key) = VK_ESCAPE then
Close;
If Ord(Key) = VK_SPACE then
Begin
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(MiLargeur, MiHauteur, MiLargeur + TailleBalle, MiHauteur
+ TailleBalle);
End;
end;

MiHauteur est une variable de type entier (integer). L'instruction div effectue une division de HauteurJeu et renvoie un résultat qui est un entier, compatible avec le type de MiHauteur.

Attention : lorsqu'on affecte une valeur à une constante, on utilise le signe « = » :

delphi

0

1

TailleBalle = 15;

Lorsqu'on déclare une variable on utilise le signe « : » :

delphi

0

1

MiHauteur: integer;

Lorsqu'on affecte une valeur à une variable, on utilise les signes « := » :

delphi

0

1

MiHauteur := HauteurJeu div 2;

Vérifier le bon fonctionnement en appuyant sur F9 !

3e phase : déplacer la balle

Pour déplacer la balle sur l'écran, il va falloir afficher la balle à un endroit, puis l'effacer et l'afficher à un autre endroit, et ainsi de suite, suffisamment rapidement pour que l'œil ne capte pas cela : cela donnera l'impression que la balle se déplace sur l'écran.

La balle va donc être affichée en blanc pendant 100 ms, puis effacée (affichée en noir), puis affichée en blanc un cran plus loin.

Modifier la procédure FormKeyPress de la manière suivante :

delphi

0

1

If Ord(Key) = VK_SPACE then
Begin
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(MiLargeur, MiHauteur, MiLargeur + TailleBalle, MiHauteur
+ TailleBalle);
Sleep(100);
Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(MiLargeur, MiHauteur, MiLargeur + TailleBalle, MiHauteur
+ TailleBalle);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(MiLargeur + 20, MiHauteur + 20, MiLargeur + TailleBalle
+ 20, MiHauteur + TailleBalle + 20);
End;

Vérifier le bon fonctionnement en appuyant sur F9 !

4e phase : automatiser le déplacement de la balle

Nous allons créer une procédure qui dessine la balle.

Dans la section private, déclarer la procédure DessineBalle :

delphi

0

1

procedure DessineBalle (const XNew, YNew: Integer);

En positionnant le curseur sur ce que l'on vient d'écrire, et un tapant au clavier en même temps CTRL + SHIFT + C, le code de la procédure se crée automatiquement :

delphi

0

1

procedure TForm1.DessineBalle(const XNew, YNew: Integer);
begin
end;

Il reste à compléter la procédure avec le code suivant (à insérer entre begin et end) :

delphi

0

1

procedure TForm1.DessineBalle (const XNew, YNew: Integer);
Begin
Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(PosBalleX, PosBalleY, PosBalleX + TailleBalle,PosBalleY +
TailleBalle);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(XNew,YNew,Xnew + TailleBalle,YNew + TailleBalle);
PosBalleX:= XNew;
PosBalleY:= YNew;
End;

PosBalleX et PosBalleY sont des variables globales (valables dans tout le programme), qui contiennent la position courante de la balle.

Xnew et Ynew sont les positions futures de la balle (celle que l'on va dessiner dans la procédure DessineBalle).

Pour que cela fonctionne, il faut déclarer les variables utilisées PosBalleX et PosBalleY dans la section private, juste avant la procédure DessineBalle :

delphi

0

1

private
PosBalleX: Integer;
PosBalleY: Integer;
procedure DessineBalle (const XNew, YNew: Integer);

Puis il faut initialiser les variables, dans la procédure FormCreate, en rajoutant ceci (entre begin et end) :

delphi

0

1

PosBalleX:= 0;
PosBalleY:= 0;

La procédure FormCreate devient donc :

delphi

0

1

procedure TForm1.FormCreate(Sender: TObject);
begin
Color := clBlack;
Height := HauteurJeu;
Width := LargeurJeu;
Position := poScreenCenter;
BorderStyle := bsNone;
PosBalleX := 0;
PosBalleY := 0;
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

5e phase : l'ordinateur gère le déplacement de la balle

L'ordinateur va désormais gérer le déplacement de la balle. Pour cela il faut ajouter un timer (onglet System/TTimer).

Un timer, c'est comme un chronomètre, qui décompte le temps, et qui va permettre à intervalle régulier de déclencher un événement (le déplacement de la balle).

Sélectionner TTimer et cliquer dans la fenêtre Form1 pour faire apparaître le timer.

Nous allons changer son nom Timer1 en tmrMvtBalle (dans l'inspecteur d'objet, onglet Propriétés, propriété Name).

Nous allons aussi passer la propriété Enabled à False : le timer ne sera actif que lorsque le jeu débutera.

Enfin, nous réglons l'intervalle de temps du timer sur 100 ms (à la place de 1000) : inspecteur d'objet, onglet Propriétés, propriété Interval.

Sélectionner l'onglet Événements, et activer l'événement OnTimer (cliquer sur les trois points).

La procédure correspondante (TForm1.tmrMvtBalle) est automatiquement créée.

Nous allons maintenant faire se déplacer la balle automatiquement, en modifiant comme suit la procédure TForm1.FormKeyPress :

delphi

0

1

if ord(Key) = VK_SPACE then
Begin
DessineBalle (MiLargeur,MiHauteur);
tmrMvtBalle.Enabled:= True;
end;

Lorsqu'on lance le jeu (appui sur barre d'espace), le timer se met en route, et la procédure DessineBalle est activée : la balle est dessiné au centre de l'écran.

Pour avoir un déplacement automatique selon l'axe x (horizontal) de la balle, la procédure TForm1.tmrMvtBalle devient :

delphi

0

1

procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Begin
DessineBalle (PosBalleX + 10, PosBalleY);
End;

A chaque impulsion du timer, la fonction DessineBalle sera activée, et la balle sera redessinée sur l'écran avec un changement de position de 10 suivant l'axe x.

Vérifier le bon fonctionnement en appuyant sur F9 !

Le problème est que la balle sort de l'écran.

6e phase : faire rebondir la balle sur les côtés

Ajouter une variable pour gérer la direction de la balle : Direction.

Il faut rajouter ceci après le mot clé private :

delphi

0

1

private
…
PosBalleX: Integer;
PosBalleY: Integer;
Direction: Integer;

Puis il faut initialiser cette variable dans la procédure TForm1.FormCreate :

delphi

0

1

procedure TForm1.FormCreate(Sender: TObject);
begin
…
PosBalleX := 0;
PosBalleY := 0;
Direction := 1;
end;

Il faut ensuite modifier la procédure TForm1.tmrMvtBalleTimer :

delphi

0

1

procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Begin
DessineBalle (PosBalleX + 10 * Direction, PosBalleY);
End ;
Enfin, il faut gérer la valeur de Direction, pour faire rebondir la balle de chaque coté.
procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Begin
DessineBalle (PosBalleX + 10 * Direction, PosBalleY);
If (PosBalleX<=0) or (PosBalleX >= (LargeurJeu - TailleBalle)) then
Direction:= Direction * -1;
End;

Si la balle touche à gauche : PosBalleX <= 0.

Si la balle touche à droite : PosBalleX >= (LargeurJeu - TailleBalle), ceci pour tenir compte de l'épaisseur de la balle (rappel : TailleBalle = 15 pixels).

Lorsque la balle touche un des bords latéraux, on change la direction de déplacement en multipliant par (-1) la variable Direction.

Vérifier le bon fonctionnement en appuyant sur F9 !

Étape 3 : Gestion des rebonds

La gestion des rebonds de la balle s'effectue en quatre phases :

- calcul des coordonnées de déplacement de la balle ;

- préparation à la gestion des rebonds ;

- gestion du rebond latéral ;

- gestion des rebonds supérieur et inférieur.

1e phase : calcul des coordonnées de déplacement de la balle

(x1, y1) est la position initiale de la balle, (x2, y2) est la position finale de la balle.

La balle se déplace selon un angle de déplacement nommé Direction, et à une vitesse de déplacement nommée Vitesse.

2e phase : préparation à la gestion des rebonds

Nous allons créer et initialiser une variable Vitesse, qui permettra de gérer facilement la vitesse de déplacement de la balle.

Comme d'habitude, on déclare la variable dans la partie private :

delphi

0

1

private
PosBalleX: Integer;
PosBalleY: Integer;
Direction: Integer;
Vitesse: Integer;
Et on l'initialise dans la procédure FormCreate :
procedure TForm1.FormCreate(Sender: TObject);
begin
PosBalleX := 0;
PosBalleY := 0;
Direction := 25;
Vitesse := 20;
end;

Ne pas oublier de changer la valeur de Direction.

Sous Lazarus, le sinus et le cosinus se calculent pour un angle exprimé en radians.

Donc, pour calculer le cosinus de l'angle Direction, je dois écrire l'équation suivante :

delphi

0

1

cos(Pi / 180 * Direction)

Trunc est une instruction qui renvoie la partie entière d'un résultat :

Trunc (12,56) renvoie 12.

En effet, Xchange et Ychange étant des entiers, ils ne peuvent stocker qu'un résultat de type entier.

3e phase : gestion du rebond latéral

Voici une figure permettant de déterminer l'angle de rebond latéral :

La balle arrive sur le mur avec un angle égal à Direction. L'angle de rebond sera donc égal à Direction + (90 - Direction) * 2.

Il faut donc modifier la procédure tmrMvtBalle de la manière suivante :

delphi

0

1

procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Var
XChange: Integer;
YChange: Integer;
begin
XChange:= trunc(cos(Pi / 180 * Direction) * Vitesse);
YChange:= trunc(sin(Pi / 180 * Direction) * Vitesse);
DessineBalle (PosBalleX + XChange,PosBalleY + YChange);
If (PosBalleX<=0) or (PosBalleX >= (LargeurJeu - TailleBalle)) then
Direction:= Direction + (90 - Direction) * 2 ;
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

Vous pouvez faire plusieurs essais, en changeant la valeur de Direction, ainsi qu'éventuellement celle de Vitesse.

4e phase : gestion des rebonds supérieur et inférieur

Voici une figure permettant de déterminer l'angle de rebond supérieur et inférieur :

La balle arrive sur le mur avec un angle égal à Direction. L'angle de rebond sera donc égal à Direction + (180 - Direction) * 2.

Il faut donc modifier la procédure tmrMvtBalle ainsi :

delphi

0

1

procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Var
XChange: Integer;
YChange: Integer;
begin
XChange:= trunc(cos(Pi / 180 * Direction) * Vitesse);
YChange:= trunc(sin(Pi / 180 * Direction) * Vitesse);
DessineBalle (PosBalleX + XChange,PosBalleY + YChange);
If (PosBalleX<=0) or (PosBalleX >= (LargeurJeu - TailleBalle)) then
Direction:= Direction + (90 - Direction) * 2
else if (PosBalleY<=0) or(PosBalleY >= (HauteurJeu - TailleBalle)) then
Direction:= Direction + (180 - Direction) *2
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

Étape 4 : Création et gestion des raquettes

La création et la gestion des raquettes s'effectue en cinq phases :

- création de la raquette droite ;

- gestion du déplacement de la raquette droite ;

- création et gestion du déplacement de la raquette gauche ;

- limiter les déplacements verticaux des raquettes ;

- gestion du rebond sur la raquette.

1e phase : création de la raquette droite

Nous avons besoin de stocker dans une constante la hauteur de la raquette (RaqHauteur), et dans une autre constante la valeur du décalage de la raquette (DecalageRaquette).

Déclarer cette constante dans la zone const :

delphi

0

1

RaqHauteur = 80;
DecalageRaquette = 30;

De même déclarer la variable PosRaqDrY dans la zone private :

delphi

0

1

PosRaqDrY: Integer;

Initialiser la variable PosRaqDrY dans la procédure FormCreate :

delphi

0

1

PosRaqDrY:=0;

Déclarer la nouvelle procédure dans la zone private :

delphi

0

1

procedure DessineRaquetteDroite (const YNew: Integer);

Puis construire la procédure (avec les touches CTRL + SHIFT + C).

Compléter la procédure DessineRaquetteDroite de la manière suivante :

delphi

0

1

procedure TForm1.DessineRaquetteDroite (const YNew: Integer);
const
RaqDrXPos = LargeurJeu - DecalageRaquette - TailleBalle;
begin
Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(RaqDrXPos, PosRaqDrY, RaqDrXPos + TailleBalle, PosRaqDrY +
RaqHauteur);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(RaqDrXPos , YNew, RaqDrXPos + TailleBalle, YNew +
RaqHauteur);
PosRaqDrY:= YNew;
end;

La procédure FormKeyPress devient :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
if ord(Key) = VK_ESCAPE then
Close;
if ord(Key) = VK_SPACE then
Begin
DessineBalle (MiLargeur, MiHauteur);
tmrMvtBalle.Enabled:=True;
DessineRaquetteDroite (MiHauteur);
End;
end;

Vérifier cette phase en appuyant sur F9 !

2e phase : gérer le déplacement de la raquette droite

Les touches M et K définissent le déplacement de la raquette droite verticalement.

Cela se fait dans la procédure FormKeyPress :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
if ord(Key) = VK_ESCAPE then
Close;
if ord(Key) = VK_SPACE then
Begin
DessineBalle (MiLargeur, MiHauteur);
tmrMvtBalle.Enabled:=True;
DessineRaquetteDroite (MiHauteur);
end;
if Key in ['m', 'M'] then
DessineRaquetteDroite(PosRaqDrY + 20);
if Key in ['k', 'K'] then
DessineRaquetteDroite( PosRaqDrY - 20);
End ;

Vérifier cette phase en appuyant sur F9 !

3e phase : faire de même pour la raquette gauche

De même déclarer la variable PosRaqGaY dans la zone private :

delphi

0

1

PosRaqGaY: Integer;

Initialiser la variable PosRaqGaY dans la procédure FormCreate :

delphi

0

1

PosRaqGaY:=0;

Déclarer la nouvelle procédure dans la zone private :

delphi

0

1

procedure DessineRaquetteGauche (const YNew: Integer);

Puis construire la procédure (CTRL + SHIFT + C), et compléter le code :

delphi

0

1

procedure TForm1.DessineRaquetteGauche (const YNew: Integer);
const
RaqGaXPos = DecalageRaquette;
begin
Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(RaqGaXPos, PosRaqGaY, RaqGaXPos + TailleBalle, PosRaqGaY
+ RaqHauteur);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle( RaqGaXPos , YNew, RaqGaXPos + TailleBalle, YNew +
RaqHauteur);
PosRaqGaY:= YNew;
end;

La procédure FormKeyPress devient :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
if ord(Key) = VK_ESCAPE then
Close;
if ord(Key) = VK_SPACE then
Begin
DessineBalle (MiLargeur, MiHauteur);
tmrMvtBalle.Enabled:=True;
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche (MiHauteur);
End;
End;

Vérifier cette phase en appuyant sur F9 !

Régler le déplacement de la raquette gauche.

Les touches F et S définissent le déplacement de la raquette gauche verticalement.

Cela se fait dans la procédure FormKeyPress :

delphi

0

1

procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
if ord(Key) = VK_ESCAPE then
Close;
if ord(Key) = VK_SPACE then
Begin
DessineBalle (MiLargeur, MiHauteur);
tmrMvtBalle.Enabled:=True;
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche (MiHauteur);
end;
if Key in ['m', 'M'] then
DessineRaquetteDroite(PosRaqDrY + 20);
if Key in ['k', 'K'] then
DessineRaquetteDroite( PosRaqDrY - 20);
if Key in ['f', 'F'] then
DessineRaquetteGauche( PosRaqGaY + 20);
if Key in ['s', 'S'] then
DessineRaquetteGauche(PosRaqGaY - 20);
End;

Vérifier cette phase en appuyant sur F9 !

4e phase : limiter le déplacement des raquettes

Il faut modifier le code de la procédure DessineRaquetteDroite :

delphi

0

1

procedure TForm1.DessineRaquetteDroite (const YNew: Integer);
const
RaqDrXPos = LargeurJeu - DecalageRaquette - TailleBalle;
begin
if (YNew <= 0) or (YNew + RaqHauteur >= HauteurJeu) then
Exit;
Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(RaqDrXPos, PosRaqDrY, RaqDrXPos + TailleBalle, PosRaqDrY +
RaqHauteur);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(RaqDrXPos , YNew, RaqDrXPos + TailleBalle, YNew +
RaqHauteur);
PosRaqDrY:= YNew;
end;
Faire de même pour la procédure DessineRaquetteGauche :
procedure TForm1.DessineRaquetteGauche (const YNew: Integer);
const
RaqGaXPos = DecalageRaquette;
begin
if (YNew <= 0) or (YNew + RaqHauteur >= HauteurJeu) then
Exit;
Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(RaqGaXPos, PosRaqGaY, RaqGaXPos + TailleBalle, PosRaqGaY
+ RaqHauteur);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle( RaqGaXPos , YNew, RaqGaXPos + TailleBalle, YNew +
RaqHauteur);
PosRaqGaY:= YNew;
end;

Vérifier cette phase en appuyant sur F9 !

5e phase : gestion du rebond sur la raquette

Il faut rajouter un test dans la procédure tmrMvtBalleTimer.

Pour la raquette droite :

delphi

0

1

else if
(PosBalleX + TailleBalle >= LargeurJeu - DecalageRaquette - TailleBalle) and
(PosBalleY >= PosRaqDrY) and
(PosBalleY + TailleBalle <= PosRaqDrY + RaqHauteur) then
Direction:= Direction + (90 - Direction) * 2

Vérifier cette phase en appuyant sur F9 !

Et faire de même pour la raquette gauche :

delphi

0

1

else if
(PosBalleX <= 30 + 15) and
(PosBalleY >= PosRaqGaY) and
(PosBalleY + 15 <= PosRaqGaY + 80) then
Direction:= Direction + (90 - Direction) * 2;

Le code complet de la procédure est donc :

delphi

0

1

procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Var
XChange: Integer;
YChange: Integer;
begin
Xchange := trunc(cos(Pi / 180 * Direction) * Vitesse);
Ychange := trunc(sin(Pi / 180 * Direction) * Vitesse);
DessineBalle (PosBalleX + XChange,PosBalleY + Ychange);
If (PosBalleX<=0) or (PosBalleX >= (LargeurJeu - TailleBalle)) then
Direction:= Direction + (90 - Direction) * 2
else if (PosBalleY<=0) or(PosBalleY >= (HauteurJeu - TailleBalle)) then
Direction:= Direction + (180 - Direction) *2
else if
(PosBalleX + TailleBalle >= LargeurJeu - DecalageRaquette - TailleBalle)
and
(PosBalleY >= PosRaqDrY) and
(PosBalleY + TailleBalle <= PosRaqDrY + RaqHauteur) then
Direction:= Direction + (90 - Direction) * 2
else if
(PosBalleX <= DecalageRaquette + TailleBalle) and
(PosBalleY >= PosRaqGaY) and
(PosBalleY + TailleBalle <= PosRaqGaY + RaqHauteur) then
Direction:= Direction + (90 - Direction) * 2;
end;

Vérifier cette phase en appuyant sur F9 !

Étape 5 : gestion du score

La gestion du score se fait en plusieurs étapes :

- créer les variables contenant le score ;

- initialisation des variables quand le jeu débute ;

- détection d'un échec et incrémentation du score ;

- affichage du score sur l'écran ;

- quand le score d'un joueur atteint 10, annoncer le vainqueur.

1e phase : création des variables

Dans la section private, ajouter les deux variables suivantes :

delphi

0

1

ScoreGauche: integer;
ScoreDroit: integer;

2e phase : initialisation des variables

Ajouter le code suivant dans la procédure FormCreate :

delphi

0

1

ScoreGauche := 0;
ScoreDroit := 0;

3e phase : détection d'un échec et incrémentation du score

La détection se fait dans la procédure tmrMvtBalle : lorsque la balle va trop à gauche (PosBalleX <= 0), on incrémente le compteur du joueur de droite, et lorsque la balle va trop à droite (PosBalleX >= LargeurJeu - TailleBalle), on incrémente le compteur du joueur de gauche.

L'incrémentation se fait à l'aide de l'instruction Inc.

Exemple :

delphi

0

1

Inc (ScoreGauche);

Il faut ensuite relancer la balle pour un nouveau service : ce sera fait par une procédure appelée NouveauService.

Il faut la déclarer dans la section private :

delphi

0

1

procedure NouveauService;

Avec CTRL + SHIFT + C, on crée le code suivant :

delphi

0

1

procedure TForm1.NouveauService;
begin
end;

Dans cette procédure, nous allons intégrer le code nécessaire au service d'une nouvelle balle.

Il faut d'abord arrêter le timer (la balle n'a plus besoin de bouger temporairement) :

delphi

0

1

tmrMvtBalle.Enabled:= False;

Il faudra penser à le remettre en route à la fin de la procédure.

On redessine la balle et les raquettes droite et gauche :

delphi

0

1

DessineBalle (MiLargeur, MiHauteur);
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche (MiHauteur);

Il faut ensuite afficher le score, ce qui sera fait par une autre procédure nommée AfficheScore.

Une petite pause (1s) sera nécessaire pour bien visualiser le score à l'écran :

delphi

0

1

sleep(1000);

Donc le code de la procédure NouveauService sera le suivant :

delphi

0

1

procedure TForm1.NouveauService;
begin
tmrMvtBalle.Enabled:= False;
DessineBalle (MiLargeur, MiHauteur);
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche (MiHauteur);
AfficheScore;
sleep(1000);
tmrMvtBalle.Enabled:= True;
end;

La procédure NouveauService sera appelée dans la procédure tmrMvtBalle, de la manière suivante :

delphi

0

1

If (PosBalleX<=0) then
begin
Inc (ScoreDroit);
NouveauService;
end
else if (PosBalleX >= (LargeurJeu - TailleBalle)) then
begin
Inc (ScoreGauche);
NouveauService;
end

Donc la procédure NouveauService complète est la suivante :

delphi

0

1

procedure TForm1.NouveauService;
begin
If (ScoreDroit >= 10) OR (ScoreGauche >= 10) then
AnnoncerGagnant
else
begin
tmrMvtBalle.Enabled:= False;
DessineBalle (MiLargeur,MiHauteur);
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche(MiHauteur);
AfficheScore;
sleep(1000);
tmrMvtBalle.Enabled:= True;
end;
end;

4e phase : affichage du score sur l'écran

C'est l'objectif de la procédure AfficheScore.

Il faut déclarer la procédure dans la section private :

delphi

0

1

procedure AfficheScore;

puis cliquer sur CTRL + SHIFT + C pour créer le code de la procédure :

delphi

0

1

procedure TForm1.AfficheScore;
begin
end;

Pour afficher le score, nous utiliserons l'instruction Canvas.TextOut de la manière suivante :

delphi

0

1

Canvas.TextOut (MiLargeur - 50, 30, IntToStr(ScoreGauche));

Cela affiche un texte, un peu avant le milieu de l'écran (à la position 400 - 50 pixels en x), un peu plus bas que le sommet de l'écran du jeu (30 pixels).

L'instruction IntToStr convertit le contenu de ScoreGauche (qui en un nombre entier) en une chaîne de caractères (String) : l'instruction Canvas.TextOut n'affiche en effet que des caractères, pas des nombres.

De même, pour le score de droite :

delphi

0

1

Canvas.TextOut (MiLargeur + 50, 30, IntToStr(ScoreDroit));

Afin d'avoir un affichage visible, nous ajouterons avant l'instruction Canvas.TextOut les instructions suivantes :

delphi

0

1

Canvas.Font.Name:= 'Courier New';
Canvas.Font.Size:= 24;
Canvas.Font.Style:=[fsBold];
Canvas.Font.Color:= clWhite;
Canvas.Brush.Color:= clBlack;

Ceci sélectionnera une police « Courier New », de taille 24, en gras, de couleur blanche, sur fond noir.

Pour afficher le score sur l'écran, il faut rajouter la ligne suivante au début du jeu (procédure FormKeyPress) et à chaque nouveau service (procédure NouveauService) :

delphi

0

1

AfficheScore;

Voici la modification dans la procédure FormKeyPress :

delphi

0

1

If Ord(Key) = VK_SPACE then
Begin
DessineBalle(MiLargeur, MiHauteur);
tmrMvtBalle.Enabled:=True;
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche (MiHauteur);
AfficheScore;
End;

Et voici la modification dans la procédure NouveauService :

delphi

0

1

else
begin
tmrMvtBalle.Enabled:= False;
DessineBalle (MiLargeur,MiHauteur);
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche(MiHauteur);
AfficheScore;
sleep(1000);
tmrMvtBalle.Enabled:= True;
end;

Vérifier cette phase en appuyant sur F9 !

5e phase : annoncer le vainqueur

Le test pour savoir s'il y a un vainqueur se fait dans la procédure NouveauService. S'il y a un vainqueur, il faudra gérer cela, sinon, le jeu continue.

delphi

0

1

If (ScoreDroit >= 10) OR (ScoreGauche >= 10) then
AnnoncerGagnant
else
begin
tmrMvtBalle.Enabled:= False;
DessineBalle (MiLargeur,MiHauteur);
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche(MiHauteur);
AfficheScore;
sleep(1000);
tmrMvtBalle.Enabled:= True;
end;

Dans la phase de débogage du jeu, il peut être judicieux de limiter le score à 3.

Il faut donc déclarer la procédure AnnoncerGagnant dans la zone private :

delphi

0

1

procedure AnnoncerGagnant;

puis cliquer sur CTRL+SHIFT+C pour créer le code correspondant à cette procédure :

delphi

0

1

procedure TForm1.AnnoncerGagnant;
Begin
End;

Nous allons ajouter entre Begin et End le code suivant (formatage du texte) :

delphi

0

1

Canvas.Font.Name:= 'Courier New';
Canvas.Font.Size:= 48;
Canvas.Font.Style:=[fsBold];
Canvas.Font.Color:= clWhite;
Canvas.Brush.Color:= clBlack;

Puis, suivant le gagnant, nous allons écrire le mot « Gagnant » sous le score :

delphi

0

1

if ScoreGauche >= 10 then
Canvas.TextOut (MiLargeur - 50 - Canvas.TextWidth('Gagnant'), 100, 'Gagnant')
else
Canvas.TextOut (MiLargeur + 50, 100, 'Gagnant');

L'instruction Canvas.TextWidth('Gagnant') calcule la largeur du mot « Gagnant » (afin de le décaler correctement par rapport au centre de l'écran).

Il faudrait faire pareil pour l'affichage du score de gauche.

Vérifier cette phase en appuyant sur F9 !

Étape 6 : Dessiner le filet

Pour dessiner le filet, nous allons créer une boucle de programmation qui dessine une ligne pointillée verticale au centre de l'écran.

1e phase : déclaration de la procédure

Dans la section private, déclarer la procédure :

delphi

0

1

procedure DessineFilet;

2e phase : création de la procédure

Avec le curseur sur la ligne créée en phase 1, taper CTRL + SHIFT + C pour créer le code de la procédure :

delphi

0

1

procedure TForm1.DessineFilet;
Begin
End;

3e phase : création du code

delphi

0

1

procedure TForm1.DessineFilet
var
VarY: integer;
Begin
Canvas.Brush.Color := ClBlack;
Canvas.Rectangle (0,0,LargeurJeu,HauteurJeu);
Canvas.Pen.Color:= clWhite;
VarY:= 0;
While VarY <= HauteurJeu do
Begin
Canvas.Line(MiLargeur, VarY, MiLargeur, VarY + 20);
Inc (VarY, 40);
End;
Canvas.Pen.Color:= clBlack;
End;

La procédure efface complètement l'écran (Canvas.Rectangle) puis dessine en blanc des pointillés de longueur 20 pixels espacés de 40 pixels.

4e phase : appel de la procédure

Dans la procédure FormKeyPress, ajouter les instructions manquantes, pour afficher le filet au début du jeu :

delphi

0

1

If Ord(Key) = VK_SPACE then
Begin
DessineFilet;
AfficheScore;
DessineBalle(MiLargeur, MiHauteur);
tmrMvtBalle.Enabled:=True;
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche (MiHauteur);
End;

Il faut aussi afficher le score et le filet à chaque nouveau service :

delphi

0

1

procedure TForm1.NouveauService;
begin
tmrMvtBalle.Enabled:= False;
DessineFilet;
AfficheScore;
If (ScoreDroit >= 10) OR (ScoreGauche >= 10) then
AnnoncerGagnant
else
begin
DessineBalle (MiLargeur,MiHauteur);
DessineRaquetteDroite (MiHauteur);
DessineRaquetteGauche(MiHauteur);
sleep(500);
tmrMvtBalle.Enabled:= True;
end;
end;

Vérifier cette phase en appuyant sur F9 !

Étape 7 : Corriger les problèmes d'effacement

Lorsque la balle circule sur le terrain, il apparaît un problème d'effacement de l'écran (effacement du score, du filet…). Voici comment gérer cela.

Le principe est de sauvegarder la portion d'écran où sera la balle (procédure EnregistrerEcran), puis de déplacer la balle et de restaurer l'écran (procédure RestaurerEcran) comme il était à l'endroit que vient de quitter la balle.

Cela se réalise à l'aide d'un objet BitMap qui stocke le contenu de la portion d'écran correspondant à la balle, le tout en six phases :

- déclaration des procédures ;

- création des procédures ;

- gestion de l'objet BitMap ;

- création du code de la procédure EnregistrerEcran ;

- création du code de la procédure RestaurerEcran ;

- appel des procédures.

1e phase : déclaration des procédures

Dans la zone private, déclarer les deux procédures suivantes :

delphi

0

1

procedure RestaurerEcran (const VarX,VarY: integer);
procedure EnregistrerEcran (const VarX,VarY: integer);

2e phase : création des procédures

Créer le code des deux procédures (CTRL + SHIFT + C) :

delphi

0

1

procedure TForm1.RestaurerEcran (const VarX, VarY: integer);
Begin
End;
procedure TForm1.EnregistrerEcran (const VarX, VarY: integer);
Begin
End;

3e phase : gestion de l'objet BitMap

Déclarer l'objet Bitmap dans la zone private (avant les déclarations de procédures) :

delphi

0

1

SauveEcran: TBitMap;

À la fin de la procédure FormCreate, rajouter ceci :

delphi

0

1

SauveEcran := TBitMap.Create;
SauveEcran.SetSize(TailleBalle, TailleBalle);

Il faut penser à créer la procédure de destruction de l'objet BitMap.

Pour cela, il faut aller dans l'inspecteur d'objet, sélectionner l'onglet Événements, et choisir l'événement OnDestroy (cliquer sur les trois points à coté pour créer la procédure).

Puis compléter le code comme ci dessous (entre begin et end) :

delphi

0

1

procedure TForm1.FormDestroy(Sender: TObject);
Begin
SauveEcran.Free;
End;

4e phase : création du code de la procédure EnregistrerEcran

Compléter le code de la procédure EnregistrerEcran :

delphi

0

1

procedure TForm1.EnregistrerEcran (const VarX, VarY: integer);
Begin
SauveEcran.Canvas.CopyRect(
Rect(0, 0, TailleBalle, TailleBalle),
Canvas,
Rect(VarX, VarY, VarX+TailleBalle, VarY+TailleBalle));
End;

5e phase : création du code de la procédure RestaurerEcran

Compléter le code de la procédure RestaurerEcran :

delphi

0

1

procedure TForm1.RestaurerEcran (const VarX, VarY: integer);
Begin
Canvas.Draw (VarX, VarY, SauveEcran);
End;

6e phase : appel des procédures

Ajouter le code suivant au début de la procédure DessineBalle :

delphi

0

1

RestaurerEcran(PosBalleX, PosBalleY);
EnregistrerEcran(XNew, Ynew);

Ainsi, on restaure l'écran tel qu'il était avant que la balle passe, et on sauve l'écran là où sera la balle.

Enfin, effacer les deux lignes suivantes :

delphi

0

1

Canvas.Brush.Color:=clBlack;
Canvas.Rectangle(PosBalleX, PosBalleY, PosBalleX + TailleBalle,PosBalleY + TailleBalle);

La procédure DessineBalle devient donc :

delphi

0

1

procedure TForm1.DessineBalle(const XNew, YNew: Integer);
begin
RestaurerEcran(PosBalleX, PosBalleY);
EnregistrerEcran(XNew, Ynew);
Canvas.Brush.Color:=clWhite;
Canvas.Rectangle(XNew,YNew,Xnew + TailleBalle,YNew + TailleBalle);
PosBalleX:= XNew;
PosBalleY:= YNew;
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

Étape 8 : Écrire les instructions de jeu

Cette étape se fera en quatre phases :

- déclaration de la procédure ;

- création de la procédure ;

- création du code de la procédure ;

- appel de la procédure.

1e phase : déclaration de la procédure

Dans la section private, déclarer la procédure :

delphi

0

1

procedure AfficherInstructions;

2e phase : création de la procédure

Avec le curseur sur la ligne créée en phase 1, taper CTRL + SHIFT + C pour créer le code de la procédure :

delphi

0

1

procedure TForm1. AfficherInstructions;
Begin
End;

3e phase : création du code

Compléter le code de la procédure AfficherInstructions :

delphi

0

1

procedure TForm1. AfficherInstructions;
Begin
Canvas.Font.Name:= 'Courier New';
Canvas.Font.Size:= 24;
Canvas.Font.Style:=[fsBold];
Canvas.Font.Color:= clWhite;
Canvas.Brush.Color:= clBlack;
Canvas.Rectangle (140, 240, 700, 450);
Canvas.Pen.Color:= clBlack ;
Canvas.TextOut (150, 250, '<Espace> pour démarrer');
Canvas.TextOut (150, 300, '<Echap> pour quitter');
Canvas.TextOut (150, 350, '<F> et <S> raquette gauche');
Canvas.TextOut (150, 400, '<M> et <K> raquette droite');
End;

4e phase : appel de la procédure

La procédure AfficherInstructions sera appelée à la fin de la procédure AnnoncerGagnant (juste avant le end;) :

delphi

0

1

AfficherInstructions;

Afin d'afficher ce texte une fois aussi au début du jeu, nous allons utiliser un événement particulier de la feuille de jeu nommé OnPaint.

Cliquer sur la feuille de jeu (TForm1), aller dans l'inspecteur d'objet, onglet Événement, et cliquer sur OnPaint (cliquer sur …) : ceci crée une procédure TForm1.OnPaint.

delphi

0

1

Procedure TForm1.OnPaint (Sender : Tobject);
Begin
DessineFilet;
AfficherInstructions;
DessinerRaquetteDroite(MiHauteur);
DessinerRaquetteGauche(MiHauteur);
OnPaint:= nil;
End;

L'instruction OnPaint:= nil est là pour empêcher la réexécution de cette procédure lorsque le jeu à commencé.

Vérifier le bon fonctionnement en appuyant sur F9 !

Étape 9 : Jouer des sons

Cette étape se fera en six phases :

- télécharger les sons ;

- ajouter la bibliothèque nécessaire à la gestion du son ;

- déclaration des procédures de gestion du son ;

- création des procédures ;

- création du code des procédures ;

- appel des procédures.

1e phase : télécharger les sons

Pour télécharger les sons, rendez-vous sur cette page :

http://www.clubengineer.org/examples/pong-sounds.zip

Une fois décompressé, vous disposez de trois fichiers sons, que nous allons renommer ainsi :

- bounce.wav → rebond.wav ;

- miss-left.wav → perdu-gauche.wav ;

- miss-right.wav → perdu-droite.wav.

Il faut placer les fichiers sons au même endroit que le fichier programme (répertoire Pong).

2e phase : ajouter la bibliothèque de gestion du son

Pour que votre programme puisse lire du son, il faut ajouter une bibliothèque qui gère le son (sous Windows) : MMSystem.

Dans la section implementation, nous avions déjà rajouté une bibliothèque : LCLType.

À la suite, rajouter MMSystem, pour avoir ceci :

delphi

0

1

implementation
uses
LCLType, MMSytem;

2e phase : déclaration des procédures de gestion du son

Dans la section private, déclarer les procédures :

delphi

0

1

procedure JouerSonRebond;
procedure JouerSonPerduGauche;
procedure JouerSonPerduDroit;

3e phase : création des procédures

En se positionnant sur le nom de la procédure et en cliquant sur CTRL + SHIFT + C, les trois procédures sont créés :

delphi

0

1

procedure TForm1.JouerSonRebond;
Begin
End;
procedure TForm1.JouerSonPerduGauche;
Begin
End;
procedure TForm1.JouerSonPerduDroit;
Begin
End;

4e phase : création du code des procédures

Pour jouer un son, l'instruction est la suivante :

delphi

0

1

PlaySound ('fichierson.wav', 0, SND_ASYNC);

SND_ASYNC indique que le jeu peut continuer pendant que le son est joué : si on mettait SND_SYNC, le jeu ferait une pause le temps de la lecture du son.

Nos trois procédures deviennent donc :

delphi

0

1

procedure TForm1.JouerSonRebond;
Begin
PlaySound ('rebond.wav', 0, SND_ASYNC);
End;
procedure TForm1.JouerSonPerduGauche;
Begin
PlaySound ('perdu-gauche.wav', 0, SND_ASYNC);
End;
procedure TForm1.JouerSonPerduDroit;
Begin
PlaySound ('perdu-droit.wav', 0, SND_ASYNC);
End;

5e phase : appel des procédures

L'appel de la procédure JouerSonPerduGauche doit se faire chaque fois que la balle passe derrière la raquette gauche (PosBalleX <= 0) ; l'appel de la procédure JouerSonPerduDroit doit se faire chaque fois que la balle passe derrière la raquette droite (PosBalleX >= (LargeurJeu - TailleBalle)).

Nous allons donc appeler les procédures JouerSonPerduGauche et JouerSonPerduDroit dans la procédure tmrMvtBalle.

delphi

0

1

If (PosBalleX<=0) then
Begin
JouerSonPerduGauche;
Inc(ScoreDroit);
NouveauService;
End
else if (PosBalleX >= (LargeurJeu - TailleBalle)) then
Begin
JouerSonPerduDroit;
Inc (ScoreGauche);
NouveauService;
Direction:= Direction + (90 - Direction) * 2;
End

L'appel de la procédure JouerSonRebond doit se faire pour chaque rebond de la balle sur les cotés haut ou bas du terrain et sur les raquettes gauche ou droite.

Au final, la procédure tmrMvtBalleTimer devient :

delphi

0

1

procedure TForm1.tmrMvtBalleTimer(Sender: TObject);
Var
XChange: Integer;
YChange: Integer;
begin
Xchange := trunc(cos(Pi / 180 * Direction) * Vitesse);
Ychange := trunc(sin(Pi / 180 * Direction) * Vitesse);
DessineBalle (PosBalleX + XChange,PosBalleY + Ychange);
If (PosBalleX<=0) or (PosBalleX >= (LargeurJeu - TailleBalle)) then
begin
Direction:= Direction + (90 - Direction) * 2;
JouerSonRebond;
end
else if (PosBalleY<=0) or(PosBalleY >= (HauteurJeu - TailleBalle)) then
begin
Direction:= Direction + (180 - Direction) *2;
JouerSonRebond;
end
else if
(PosBalleX + TailleBalle >= LargeurJeu - DecalageRaquette - TailleBalle) and
(PosBalleY >= PosRaqDrY) and
(PosBalleY + TailleBalle <= PosRaqDrY + RaqHauteur) then
begin
Direction:= Direction + (90 - Direction) * 2;
JouerSonRebond;
end
else if
(PosBalleX <= DecalageRaquette + TailleBalle) and
(PosBalleY >= PosRaqGaY) and
(PosBalleY + TailleBalle <= PosRaqGaY + RaqHauteur) then
begin
Direction:= Direction + (90 - Direction) * 2;
JouerSonRebond;
end;
If (PosBalleX<=0) then
begin
JouerSonPerduGauche;
Inc (ScoreDroit);
NouveauService;
end ;
else if (PosBalleX >= (LargeurJeu - TailleBalle)) then
begin
JouerSonPerduDroit;
Inc (ScoreGauche);
NouveauService;
end
end;

Vérifier le bon fonctionnement en appuyant sur F9 !

Étape 10 : Améliorations possibles

Ce TP est maintenant terminé.

Néanmoins, il existe encore de nombreux bugs dans ce programme :

- parfois, la balle tape sur la raquette et enlève un morceau de raquette ;

- parfois, la balle rebondit de multiples fois derrière la raquette ;

- …

Des améliorations du jeu sont aussi possibles :

- rendre aléatoires l'angle et le côté de service de la balle ;

- ajouter des niveaux de jeu, de manière à le rendre plus difficile graduellement ;

- ajouter une gestion à long terme des scores des joueurs, de manière à avoir une liste des meilleurs joueurs ;

- …

Pour rendre le jeu plus difficile, il est possible de jouer sur les paramètres suivants :

- grandeur de l'aire de jeu ;

- vitesse de la balle ;

- taille de la balle ;

- largeur des raquettes.

À vous désormais de poursuivre le développement de ce jeu, sous licence libre si possible !

Une licence libre reconnue en Europe est la licence CeCILL, crée par le CEA, le CNRS et l'INRIA.

www.cecill.info

Ce TP est lui-même sous licence Creative Commons BY SA :

http://creativecommons.org/licenses/by-sa/3.0/deed.fr

Licence de cette documentation

Ce TP de création sous Lazarus/Free Pascal du jeu Pong est sous licence

Creative Commons BY SA :

http://creativecommons.org/licenses/by-sa/3.0/deed.fr

Ce TP a été crée par Laurent DUBETTIER-GRENIER en Novembre 2013,
dans un but d'initiation à la programmation d'élèves de secondes.

Pour contacter l'auteur :

laurent[point]dubettier-grenier[arobase]ac-grenoble.fr

C'est une adaptation/traduction libre basée sur le tutoriel vidéo en anglais créé par le site

clubengineer.org

http://www.clubengineer.org/16-online-lessons/pascal/68-game-programming-in-pascal-pong