XVI. Développement de l'écran de jeu perdu▲
Préparez le module clScreenGameOver de la même manière que le module clScreenMenu au chapitre Préparation du premier écran : le menu clScreenMenu.
Pensez à gérer l'affichage et la minuterie (10 images par seconde suffisent).
'------------------------------------------------------------------------
' Affichage de l'écran
'------------------------------------------------------------------------
Private Function ClScreen_DisplayScreen() As Boolean
' Dessine l'image sur le formulaire
oGdi.RepaintNoFormRepaint FormGame.Img
End Function
'------------------------------------------------------------------------
' Minuterie d'attente
'------------------------------------------------------------------------
Private Function ClScreen_ScreenWait() As Boolean
' Attente de 1000 / 10 = 100 ms pour affichage de 10 images/s
oGdi.Wait 100
End FunctionIl faut ensuite gérer l'enchaînement de l'écran de jeu.
Dans la fonction GameChangeScreen du module clGame, il faut ajouter l'enchainement vers l'écran de jeu.
Ajoutez un Case au Select.
' Changement pour le jeu perdu
Case "gameover"
Set oScreen = Nothing
Set oScreen = New ClScreenGameOverOn teste la valeur « gameover » car dans l'écran de jeu c'est cette valeur qu'on utilise lorsque le PacMan est mangé.
A l'initialisation de l'écran, on défini l'écran des scores en écran suivant par défaut.
' Ecran suivant par défaut
gNextScreen = "scores"A l'initialisation de l'écran, on écrit le texte de fin de jeu.
' Dessin du contenu statique
With oGdi
' Nouveau bitmap pour dessiner
.CreateBitmap .PointsToPixelsX(FormGame.Img.Width), .PointsToPixelsY(FormGame.Img.Height)
' Rempli l'image de gris
.FillColor RGB(150, 150, 150)
' Ecrit le texte jeu perdu en haut
.DrawText "JEU PERDU", 35, "Arial", 0, 20, .ImageWidth, 60, , , _
vbBlue, , vbYellow
' Sauvegarde l'image en mémoire
.ImageKeep
End WithA la mise à jour de l'écran, on écrit le score.
(On n'écrit pas le score à l'initialisation car on n'y a pas accès à l'objet oSharedData)
With oGdi
' Reprend l'image de la mémoire
.ImageReset
' Ecrit le score du joueur
.DrawText "SCORE : " & oSharedData.Score, 35, "Arial", 20, 80, .ImageWidth, 120, , 0, _
vbBlue, , vbYellow
End WithIl faut ensuite vérifier si on entre dans les meilleurs scores.
Pour l'instant les scores ne sont pas gérés.
On va créer dans le sous-dossier config un fichier scores.ini qui contiendra les 10 meilleurs scores.
[1]
name=initial
score=10
[2]
name=initial
score=10
[3]
name=initial
score=10
[4]
name=initial
score=10
[5]
name=initial
score=10
[6]
name=initial
score=10
[7]
name=initial
score=10
[8]
name=initial
score=10
[9]
name=initial
score=10
[10]
name=initial
score=10Pour chaque score il y a deux valeurs : le nom et le score.
Comme nous l'avions fait pour les options, nous allons créer des fonctions de gestion du fichier scores.ini.
'------------------------------------------------------------------------
' Lecture de la valeur du score numéro pNum
' pKey = name ou score
'------------------------------------------------------------------------
Public Function ReadScore(pNum As Long, pKey As String) As String
Dim lRet As Long
Dim lData As String
Dim lSize As Long
lData = Space$(8192)
lSize = 8192
lRet = GetPrivateProfileString(CStr(pNum), pKey, "", lData, lSize, ThisWorkbook.path & "\config\scores.ini")
If lSize > 0 Then
ReadScore = Left$(lData, lRet)
Else
ReadScore = ""
End If
End Function
'------------------------------------------------------------------------
' Ecriture de la valeur du score numéro pNum
'------------------------------------------------------------------------
Public Function WriteScore(pNum As Long, pName As String, pScore As Long) As Boolean
Dim lRet As Long
Dim lSize As Long
lRet = WritePrivateProfileString(pNum, "name", pName, ThisWorkbook.path & "\config\scores.ini")
lRet = WritePrivateProfileString(pNum, "score", CStr(pScore), ThisWorkbook.path & "\config\scores.ini")
WriteScore = (lRet <> 0)
End Function
'---------------------------------------------------------------------------------------
' Vérifie si on entre dans les scores
'---------------------------------------------------------------------------------------
Public Function GetPosHiScore(pScore As Long) As Long
Dim lCpt As Long
' Recherche la ligne où insérer le score si assez élevé
For lCpt = 1 To 10
If pScore > ReadScore(CStr(lCpt), "score") Then
GetPosHiScore = lCpt
Exit For
End If
Next
End Function
'---------------------------------------------------------------------------------------
' Mise à jour des HighScore
'---------------------------------------------------------------------------------------
Public Sub UpdateHiScore(pPosition As Long, pName As String, pScore As Long)
Dim lCpt As Long
' Si entrée dans les scores
' Décale les scores vers le bas
For lCpt = 10 To pPosition + 1 Step -1
WriteScore CStr(lCpt), ReadScore(CStr(lCpt - 1), "name"), ReadScore(CStr(lCpt - 1), "score")
Next
' Ajoute les infos du nouveau score
WriteScore CStr(pPosition), pName, CStr(pScore)
End Sub- ReadScore lit le nom ou le score numéro pNum.
- WriteScore écrit un score en position pNum.
- GetPosHiScore renvoit la position du score pScore dans les meilleurs scores.
- UpdateHiScore insère un nouveau score en position pNum.
Retour au module clScreenGameOver dans lequel nous avons besoin de déclarer deux nouvelles variables.
' Position dans les scores
Private gPosHiScore As Long
' Nom pour entrée dans les scores
Private gName As StringA l'ouverture de l'écran, on défini la valeur de gPosHiScore à -1 (la valeur de -1 nous indique qu'on doit déterminer la position, on ne le fera qu'une seule fois).
' Position dans les scores à déterminer
gPosHiScore = -1Ensuite dans la mise à jour de l'écran, on détermine la position dans les meilleurs scores.
Puis, toujours dans la mise à jour de l'écran, on écrit si nécessaire le texte indiquant au joueur qu'il doit entrer son nom.
Static sClignote As Long
' Position dans les scores
If gPosHiScore = -1 Then gPosHiScore = GetPosHiScore(oSharedData.Score)
With oGdi
' Reprend l'image de la mémoire
.ImageReset
' Ecrit le score du joueur
.DrawText "SCORE : " & oSharedData.Score, 35, "Arial", 0, 80, .ImageWidth, 120, , 0, _
vbBlue, , vbYellow
' Edition du nom pour entrée dans les scores
If gPosHiScore > 0 Then
oGdi.DrawText "Entrée dans les meilleurs scores", 25, "Arial", _
20, 140, .ImageWidth, 180, 0, , vbRed
oGdi.DrawText "Nom du joueur : " & gName & IIf(sClignote < 5, "_", " "), 25, "Arial", _
20, 200, .ImageWidth, 240, 0, , vbBlack
End If
sClignote = (sClignote + 1) Mod 10
End With
On utilise une variable statique (qui n'est pas remise à zéro à chaque exécution de la fonction) pour faire clignoter un curseur.
Le curseur est un caractère de soulignement affiché la moitié du temps.
Pour terminer, on va modifier la gestion des commandes ClScreen_UpdateCommand.
Les touches acceptées pour l'entrée du nom du joueur sont les touches de lettres (A à Z) et de chiffres (0 à 9).
La touche de retour arrière supprime le dernier caractère.
Dim lKey As Long
' Définition du nom pour les meilleurs scores
If gPosHiScore > 0 Then
' Touche appuyée
lKey = oCommand.KeyPressGetCode
' Test si touche = lettre ou chiffre
If (lKey >= vbKeyA And lKey <= vbKeyZ) Or (lKey >= vbKey0 And lKey <= vbKey9) Then
' Ajoute le caractère au nom
gName = gName & Chr(lKey)
End If
' Test si touche de retour arrière => suppression du dernier caractère
If lKey = vbKeyBack Then
If Len(gName) > 0 Then gName = Left(gName, Len(gName) - 1)
End If
End IfEnfin on bascule vers l'écran suivant sur validation (souris, clavier, ou joystick).
Si le joueur est entré dans les scores, on insère son score dans le fichier ini.
' Test si validation
If oCommand.TestKey(oSharedData.ToucheValid, 500) Or _
oCommand.TestJoypadButton(oSharedData.JoyButton, 500) Then
' Si meilleur score à sauvegarder
If gPosHiScore > 0 Then
Call UpdateHiScore(gPosHiScore, gName, oSharedData.Score)
End If
' Demande le changement d'écran
gChangeScreen = True
End IfMangez au moins 10 pastilles et allez à la rencontre d'un fantôme pour tester la mise à jour des scores.
XVII. Développement de l'écran de jeu gagné▲
Pour l'écran de jeu gagné, on va faire simple.
On copie-colle dans clScreenGameEnd le contenu du module clScreenGameOver.
Ensuite on modifie juste le texte du contenu statique :
' Ecrit le texte jeu gagné en haut
.DrawText "JEU GAGNE", 35, "Arial", 0, 20, .ImageWidth, 60, , , _
vbBlue, , vbYellowIl faut ensuite gérer l'enchaînement de l'écran.
Dans la fonction GameChangeScreen du module clGame, il faut ajouter l'enchainement vers l'écran de jeu gagné.
Ajoutez un Case au Select.
' Changement pour le jeu gagné
Case "gameend"
Set oScreen = Nothing
Set oScreen = New ClScreenGameEndXVIII. Développement de l'écran des scores▲
On ne va pas trop détailler la création de cet écran, la méthode est la même que pour les autres écrans.
L'écran suivant par défaut est l'écran des crédits :
' Ecran suivant par défaut
gNextScreen = "credits"Il faut ensuite gérer l'enchaînement de l'écran.
Dans la fonction GameChangeScreen du module clGame, il faut ajouter l'enchainement vers l'écran des scores.
Ajoutez un Case au Select.
Cet enchaînement est un peu particulier car si on affiche les scores à partir du menu, on souhaite revenir au menu.
On teste donc le type d'écran en cours avant de basculer vers l'écran suivant, puis on change le nom de l'écran suivant pour retourner au menu le cas échéant.
' Variable pour écran suivant
Dim lNextScreen As String
[...]
' Changement pour les scores
Case "scores"
If TypeOf oScreen Is ClScreenMenu Then
lNextScreen = "menu"
End If
Set oScreen = Nothing
Set oScreen = New clScreenScores
If lNextScreen <> "" Then oScreen.NextScreen = lNextScreenOn pense à changer d'écran sur validation.
' Test si validation
If oCommand.TestKey(oSharedData.ToucheValid, 500) Or _
oCommand.TestJoypadButton(oSharedData.JoyButton, 500) Then
' Demande le changement d'écran
gChangeScreen = True
End IfPetite différence pour cet écran : il est statique.
Donc il n'est pas nécessaire de mettre à jour l'écran à chaque itération de la minuterie.
On laisse donc vide les fonctions ClScreen_UpdateScreen et ClScreen_DisplayScreen.
Le contenu de l'écran sera dessiné et affiché à l'initialisation dans Class_Initialize.
Set oGdi = New clGdiplus ' Objet pour graphiques
Set oSound = New clSound ' Objet pour sons
Dim lX1 As Long, lY1 As Long ' Variables pour positionnement des textes
Dim lCpt As Long ' Compteur pour scores
' Ecran suivant par défaut
gNextScreen = "credits"
' Dessin du contenu statique
With oGdi
' Nouveau bitmap pour dessiner
.CreateBitmapForControl FormGame.img
' Rempli l'image de gris
.FillColor RGB(150, 150, 150)
' Ecrit le texte jeu gagné en haut
' Affiche les scores
.DrawText "Meilleurs scores", 40, , 2, 120 + 2, .ImageWidth + 2, 160 + 2, , , vbBlack, 150, , , , True
.DrawText "Meilleurs scores", 40, , 0, 120, .ImageWidth, 160, , , vbRed, , , , , True
.DrawLine 50, 165, .ImageWidth - 50, 165, vbBlack, 3, True, 150
lY1 = 175
lX1 = 50
For lCpt = 1 To 10
.DrawText ReadScore(CStr(lCpt), "name"), 30, , .ImageWidth / 2, lY1, , , 0, 2, vbBlue
.DrawText ReadScore(CStr(lCpt), "score"), 30, , .ImageWidth / 2 + 10, lY1, , , 2, 2, vbRed
lY1 = lY1 + 30
Next
' Dessine l'image sur le formulaire
oGdi.RepaintNoFormRepaint FormGame.img
End WithTestez l'affichage des scores en cliquant sur SCORE dans le menu.

XIX. Développement de l'écran des crédits▲
On crée l'écran comme on l'a fait pour tous les autres.
'***************************************************************************************
'* OBJET ECRAN DES CREDITS
'***************************************************************************************
Option Explicit
' Implémente l'interface de base des écrans
Implements ClScreen
' Objet pour commandes
Private oCommand As ClCommand
' Objet pour données partagées
Private oSharedData As ClSharedData
' Objet pour sons
Private oSound As ClSound
' Objet pour gdiplus
Private oGdi As ClGdiplus
' Indicateur pour changement d'écran
Private gChangeScreen As Boolean
' Nom de l'écran suivant
Private gNextScreen As String
'------------------------------------------------------------------------
' Initialisation de l'objet
' =>création des objets nécessaire à l'écran
'------------------------------------------------------------------------
Private Sub Class_Initialize()
Set oGdi = New ClGdiplus ' Objet pour graphiques
Set oSound = New ClSound ' Objet pour sons
' Ecran suivant par défaut
gNextScreen = "menu"
' Dessin du contenu statique
With oGdi
' Nouveau bitmap pour dessiner
.CreateBitmap .PointsToPixelsX(FormGame.Img.Width), .PointsToPixelsY(FormGame.Img.Height)
' Rempli l'image de gris
.FillColor RGB(150, 150, 150)
' Sauvegarde l'image en mémoire
.ImageKeep
End With
End Sub
'------------------------------------------------------------------------
' Implémentation des propriétés de l'interface
'------------------------------------------------------------------------
Private Property Get ClScreen_ChangeScreen() As Boolean
ClScreen_ChangeScreen = gChangeScreen
End Property
Private Property Set ClScreen_Command(RHS As ClCommand)
Set oCommand = RHS
End Property
Private Property Let ClScreen_NextScreen(RHS As String)
gNextScreen = RHS
End Property
Private Property Get ClScreen_NextScreen() As String
ClScreen_NextScreen = gNextScreen
End Property
Private Property Set ClScreen_SharedData(RHS As ClSharedData)
Set oSharedData = RHS
End Property
'------------------------------------------------------------------------
' Mise à jour des commandes
'------------------------------------------------------------------------
Private Function ClScreen_UpdateCommand() As Boolean
' Test si validation
If oCommand.TestKey(oSharedData.ToucheValid, 500) Or _
oCommand.TestJoypadButton(oSharedData.JoyButton, 500) Then
' Demande le changement d'écran
gChangeScreen = True
End If
End Function
'------------------------------------------------------------------------
' Mise à jour de l'écran
'------------------------------------------------------------------------
Private Function ClScreen_UpdateScreen() As Boolean
With oGdi
' Reprend l'image de la mémoire
.ImageReset
End With
End Function
'------------------------------------------------------------------------
' Affichage de l'écran
'------------------------------------------------------------------------
Private Function ClScreen_DisplayScreen() As Boolean
' Dessine l'image sur le formulaire
oGdi.RepaintNoFormRepaint FormGame.Img
End Function
'------------------------------------------------------------------------
' Minuterie d'attente
'------------------------------------------------------------------------
Private Function ClScreen_ScreenWait() As Boolean
' Attente de 1000 / 40 = 25 ms pour affichage de 40 images/s
oGdi.Wait 25
End FunctionL'écran suivant après les crédits est le menu.
On met à jour l'affichage 40 fois par secondes car on va afficher un texte déroulant.
Pour gérer un scrolling, on va d'abord dessiner le texte des crédits sur une image plus grande que l'image du jeu.
Ensuite on va dessiner cette image sur l'image principale à chaque itération de la minuterie.
En décalant l'image d'un pixel à chaque affichage, on obtiendra un défilement.
Pour que le texte s'enroule sans fin, on utilisera le paramètre gWrapY de l'objet oGdi.
' Dessin du contenu statique
With oGdi
[...]
' Nouvelle image pour les crédits (2 fois plus haute que l'image principale
With .ImgNew("credits", .ImageWidth, .ImageHeight * 2)
.DrawText "Tutoriel jeu en VBA", 40, , 0, 40, .ImageWidth, 90, , , vbYellow
.DrawText "Ecrit pour developpez.com", 30, , 0, 120, .ImageWidth, 160, , , vbYellow
.DrawText "Dessins des sprites avec Microsoft ® Paint", 30, , 0, 170, .ImageWidth, 210, , , vbYellow
.DrawText "Sons enregistrés avec le magnétophone Windows", 30, , 0, 220, .ImageWidth, 260, , , vbYellow
.DrawText "Auteur : arkham46", 30, , 0, 270, .ImageWidth, 310, , , vbYellow
.DrawText "Site Web : arkham46.developpez.com", 30, , 0, 320, .ImageWidth, 360, , , vbBlue
End With
End With' Variable pour décalage
Static gDecalage As Long
With oGdi
' Reprend l'image de la mémoire
.ImageReset
' Décalage de 1 pixels
gDecalage = gDecalage + 1
' Affecte le décalage
.WrapY = gDecalage
' On dessine l'image des crédits
.DrawImg "credits", 0, 0, .ImageWidth, .ImageHeight, , GdipSizeModeClip, GdipAlignBottomLeft
' On annule ensuite le décalage
.WrapY = 0
End WithL'image des crédits est dessinée avec un mode GdipSizeModeClip pour ne pas redimensionner l'image.
Elle est alignée en bas (GdipAlignBottomLeft) pour débuter un affichage qui commence en bas et qui remonte.
Puis dans la fonction GameChangeScreen du module clGame, il faut ajouter l'enchainement vers l'écran de crédits.
Ajoutez un Case au Select.
' Changement pour les crédits
Case "credits"
Set oScreen = Nothing
Set oScreen = New clScreenCreditsTestez l'affichage des crédits en cliquant sur CREDITS dans le menu.

XX. Lancement du jeu à l'ouverture d'Excel▲
Une toute dernière chose avant de nous quitter.
Il serait intéressant que le jeu se lance à l'ouverture du fichier Excel.
Pour cela on va afficher le formulaire au chargement du classeur.
Dans l'explorateur de projet à gauche, repérez le module ThisWorkbook.
Double-cliquez dessus et sélectionnez WorkBookdans les listes déroulantes en haut.
La procédure Workbook_Open est créée, on y insère le code d'affichage du formulaire.
'---------------------------------------------------------------------------------------
' Procédure exécutée à l'ouverture du classeur Excel
'---------------------------------------------------------------------------------------
Private Sub Workbook_Open()
' Affiche le formulaire de jeu
FormGame.Show
End SubFermez et rouvrez le classeur pour tester.


