IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Tutoriel Gdi+ : programmez un jeu de Pacman complet en VBA

Image non disponible


précédentsommairesuivant

XIV. Bilan à mi-parcours

Faisons un petit bilan pour y voir plus clair.

Nous avons terminé :

  • l'écran de menu ;
  • l'écran des options.

Il nous reste :

  • l'écran des scores ;
  • l'écran des crédits ;
  • les écrans de fin de partie (gagné / perdu) ;
  • et l'écran de jeu !

On constate après tout ce temps que nous n'avons pas encore vraiment commencé le développement du jeu.
Pour l'instant on ne voit pas franchement que nous sommes en train de créer un PacMan.

Par contre tout est prêt pour continuer le développement dans de bonnes conditions.

Pour la suite du tutoriel, nous allons enfin commencer le développement de l'écran de jeu.

XV. Développement de l'écran de jeu

L'écran de jeu est développé dans le module clScreenGame.
Ce sera bien entendu le plus complexe des écrans du jeu.

XV-A. Création de l'écran de jeu

Préparez le module clScreenGame 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 (40 images par seconde pour le jeu).

Affichage et minuterie dans clScreenGame
Sélectionnez
'------------------------------------------------------------------------
' 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 Function

Il 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.

Enchainement vers écran de jeu dans clGame
Sélectionnez
' Changement pour le jeu
        Case "game"
            Set oScreen = Nothing
            Set oScreen = New ClScreenGame

On teste la valeur « game » car dans l'écran de menu c'est cette valeur qu'on utilise lors du click sur Commencer le jeu.

XV-B. Chargement du tableau de jeu

On a défini le tableau de jeu dans la feuille Jeu.

Ce tableau fait 28 colonnes et 30 lignes.
On va stocker les valeurs de ce tableau dans une variable du module clScreenGame.

Déclaration du tableau en en-tête de clScreenGame
Sélectionnez
' Tableau de jeu
Private gTableau(0 To 27, 0 To 29) As String

Puis on charge les données de la feuille Jeu dans ce tableau.

Chargement du tableau dans l'initialisation de clScreenGame au début de Class_Initialize
Sélectionnez
' Compteurs
Dim lCptY As Integer
Dim lCptX As Integer
' Charge les données du tableau
With ThisWorkbook.Sheets("Jeu")
    For lCptX = 1 To 28
        For lCptY = 1 To 30
            gTableau(lCptX - 1, lCptY - 1) = .Cells(lCptY, lCptX).Value
        Next
    Next
End With

XV-C. Affichage du tableau de jeu

Le tableau de jeu est dessiné en fonction du contenu des cases, à base de lignes et d'arcs de cercle.
Pour rappel, le tableau de jeu a été chargé dans gTableau à partir des données de la feuille Jeu.

Pour chaque case, on va lire les valeurs des cases adjacentes pour déterminer le type de trait à dessiner.

Créons donc une petite fonction de lecture du contenu d'une case.

Lecture du contenu d'une case
Sélectionnez
'---------------------------------------------------------------------------------------
' Lecture du contenu d'une case
'---------------------------------------------------------------------------------------
' pX,pY : Coordonnées de la case
' La fonction renvoie le contenu de la case dans un string
' Si on dépasse le tableau, renvoit ""
'---------------------------------------------------------------------------------------
Private Function GetValue(pX As Integer, pY As Integer) As String
    On Error Resume Next
    ' Lecture de la case dans le tableau représentant le niveau
    GetValue = gTableau(pX, pY)
    If Err.Number <> 0 Then GetValue = ""    ' Renvoit une chaîne vide si erreur
End Function

Cette fonction renvoit le contenu de la case de coordonnées pX, pY.
Si les coordonnées n'appartiennent pas au tableau, une chaîne vide est renvoyée.
Ceci nous permettera de nous passer d'un test pour vérifier si la case est bien contenue dans le tableau.

Ensuite, pour dessiner les murs sur l'image, il nous faut connaître le centre de la case.

Calcul la position sur l'image en fonction de la position dans le tableau
Sélectionnez
'---------------------------------------------------------------------------------------
' Calcul la position sur l'image en fonction de la position dans le tableau
'---------------------------------------------------------------------------------------
' pX,pY : Coordonnées dans le tableau
' pXOut,pYOut : Coordonnées sur l'image en pixel
'---------------------------------------------------------------------------------------
Private Sub GetCenter(pX As Currency, pY As Currency, pXOut As Long, pYOut As Long)
    pXOut = gBordure + (pX + 0.5) * gTx
    pYOut = gBordure + (pY + 0.5) * gTy
End Sub

Le centre d'une case est déterminé en fonction de la taille des cases et de la bordure.
On a donc besoin de déclarer ces variables, qui seront ensuite calculées à l'initialisation de l'écran.

Déclaration des variables en en-tête de module
Sélectionnez
' Taille d'une case
Private gTx As Long, gTy As Long
' Bordure en pixels
Private gBordure As Long

A l'initialisation de l'écran, nous allons dessiner le contenu statique : c'est-à-dire le tableau de jeu.

Déclarations pour le dessin du contenu statique dans Class_Initialize de clScreenGame
Sélectionnez
' Position du centre de la case
Dim lX As Long
Dim lY As Long
' Valeur des cases adjacente
Dim lUp As String
Dim lDown As String
Dim lRight As String
Dim lLeft As String
' Valeur de la case
Dim lValue As String
Dessin du contenu statique dans Class_Initialize de clScreenGame
Sélectionnez
' Dessine le contenu statique
With oGdi
    ' Nouveau bitmap pour dessiner
    .CreateBitmapForControl FormGame.Img
    ' Dessin lissé pour les ellipses/lignes ...
    .SmoothingMode = GdipSmoothingAntialias
    ' Début et fin de traits arrondis
    .LineStart = LineCapRound
    .LineEnd = LineCapRound
    ' Bordure de 5 pixels
    gBordure = 5
    ' Taille des cases
    gTx = (.ImageWidth - 2 * gBordure) \ 28
    gTy = (.ImageHeight - 2 * gBordure) \ 30
    ' On s'assure que la taille des cases est un multiple de 2
    gTx = gTx - (gTx Mod 2)
    gTy = gTy - (gTy Mod 2)
    ' Fond noir
    .FillColor vbBlack
    ' On dessine les murs
    For lCptY = 0 To 29    ' 30 lignes
        For lCptX = 0 To 27    ' 28 colonnes
            ' Calcul du centre de la case de coordonnéex lcptx,lcpty
            GetCenter CSng(lCptX), CSng(lCptY), lX, lY
            ' Lecture du contenu de la case
            lValue = GetValue(lCptX, lCptY)
            ' Lecture du contenu des cases adjacentes
            lRight = GetValue(lCptX + 1, lCptY)
            lLeft = GetValue(lCptX - 1, lCptY)
            lUp = GetValue(lCptX, lCptY - 1)
            lDown = GetValue(lCptX, lCptY + 1)
            Select Case lValue
            ' Il y a un mur dans la case
            Case "X"
                ' Mur horizontal
                If lRight = "X" And lLeft = "X" And (Not lUp = "X" Or Not lDown = "X" Or Not lRight = "X") Then
                    .DrawLine lX - gTx / 2, lY, lX + gTx / 2, lY, RGB(0, 0, 255), 3
                End If
                ' Mur vertical
                If lUp = "X" And lDown = "X" And (Not lRight = "X" Or Not lLeft = "X") Then
                    .DrawLine lX, lY - gTy / 2, lX, lY + gTy / 2, RGB(0, 0, 255), 3
                End If
                ' Mur en haut et à droite mais pas ailleurs
                If lUp = "X" And lRight = "X" And lLeft <> "X" And lDown <> "X" Then
                    .DrawEllipse lX + gTx / 2, lY - gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , 180, -90
                End If
                ' Murs en coin (arrondis)
                ' Mur en haut et à gauche mais pas ailleurs
                If lUp = "X" And lLeft = "X" And lRight <> "X" And lDown <> "X" Then
                    .DrawEllipse lX - gTx / 2, lY - gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , 90, -90
                End If
                ' Mur en bas et à droite mais pas ailleurs
                If lDown = "X" And lRight = "X" And lLeft <> "X" And lUp <> "X" Then
                    .DrawEllipse lX + gTx / 2, lY + gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , -180, 90
                End If
                ' Mur en bas et à gauche mais pas ailleurs
                If lDown = "X" And lLeft = "X" And lRight <> "X" And lUp <> "X" Then
                    .DrawEllipse lX - gTx / 2, lY + gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , -90, 90
                End If
            Case Else
                ' La case ne contient pas un mur mais est dans un angle formé par deux murs
                ' On dessine un coin arrondi
                ' Mur en haut et à droite mais pas sur les deux autres côtés à la fois
                If lUp = "X" And lRight = "X" And Not (lLeft = "X" And lDown = "X") Then
                    .DrawEllipse lX + gTx / 2, lY - gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , 0, -90
                End If
                ' Mur en haut et à gauche mais pas sur les deux autres côtés à la fois
                If lUp = "X" And lLeft = "X" And Not (lRight = "X" And lDown = "X") Then
                    .DrawEllipse lX - gTx / 2, lY - gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , -90, -90
                End If
                ' Mur en bas et à droite mais pas sur les deux autres côtés à la fois
                If lDown = "X" And lRight = "X" And Not (lLeft = "X" And lUp = "X") Then
                    .DrawEllipse lX + gTx / 2, lY + gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , 0, 90
                End If
                ' Mur en bas et à gauche mais pas sur les deux autres côtés à la fois
                If lDown = "X" And lLeft = "X" And Not (lRight = "X" And lUp = "X") Then
                    .DrawEllipse lX - gTx / 2, lY + gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , 90, 90
                End If
            End Select
        Next
    Next
End With

On commence par créer une image avec CreateBitmap.
Cette image est remplie de noir, le jeu se déroulant sur fond noir.
La propriété DrawSmooth permet de lisser les dessins de traits.
Les propriétés LineStart et LineEnd définissent le type de début et fin de trait.
La bordure est définie à 5 pixels (pour éviter que le dessin soit « collé » aux bords de l'image).
Puis on calcule la taille de chaque case, en fonction de la taille de l'image, du nombre de cases, et de la bordure.
Notez qu'on s'assure que la taille des cases soit un multiple de 2 pour éviter des décalages dus à des arrondis.
Puis on dessine chaque mur (case contenant « X ») en fonction du contenu des cases adjacentes.

Pour chaque cas rencontré, on peut faire un petit dessin pour mieux visualiser le type de mur à dessiner.
Par exemple pour le troisième cas avec un mur dans la case.

Code du troisième cas avec mur dans la case
Sélectionnez
' Mur en haut et à droite mais pas ailleurs
                If lUp = "X" And lRight = "X" And lLeft <> "X" And lDown <> "X" Then
                    .DrawEllipse lX + gTx / 2, lY - gTy / 2, gTx / 2, gTy / 2, _
                                      1, -1, RGB(0, 0, 255), 3, , 180, -90

S'il y a un mur en haut et un mur à droite, mais pas de mur sur les autres cases adjacentes :

Image non disponible

On doit donc dessiner un arc centré sur le point (lX + gTx / 2, lY - gTy / 2), qui démarre à 180° et qui parcours 90°.
On procède de même pour gérer tous les cas possibles.

On obtient enfin le dessin des murs.

Image non disponible

Ensuite on dessine les pastilles.
Les pastilles sont dessinées dans le contenu statique, même si elles devront disparaître de l'affichage.
En effet, on ne va pas redessiner les pastilles à chaque affichage.
On supprimera les pastilles de ce contenu statique en noircissant la case au fur et à mesure qu'elles seront mangées.

Pour l'occasion on crée une variable en-tête de module qui contient le nombre de pastilles du niveau.
Cette variable est initialisée au chargement de l'écran avec le nombre de pastilles dessinées.
Puis elle sera décrémentée durant le jeu.
Une fois la variable égale à zéro, cela signifiera que toutes les pastilles auront été mangées.

Déclaration en en-tête de module clScreenGame
Sélectionnez
' Nombre de pastilles
Private gNbPastilles As Integer

Les pastilles sont représentées par des ellipses blanches.
Les super-pastilles ont un rayon plus important.

Dessin des pastilles dans Class_Initialize de clScreenGame, avant le End With
Sélectionnez
' On dessine les pastilles
    ' Pas de pastilles sur les bords du niveau, on commence à 1 et on termine 1 case avant la fin
    ' Initialise le décompte de pastille du niveau
    gNbPastilles = 0
    ' Boucle sur le tableau
    For lCptY = 1 To 28
        For lCptX = 1 To 26
            ' Calcul du centre de la case pour y placer la pastille
            GetCenter CSng(lCptX), CSng(lCptY), lX, lY
            ' Contenu de la case
            lValue = GetValue(lCptX, lCptY)
            Select Case lValue
                ' Il y a une pastille dans la case
            Case "o"
                ' Ellipse de deux pixels de rayon
                .DrawEllipse lX, lY, 2, 2, 1, vbWhite, vbWhite
                gNbPastilles = gNbPastilles + 1
                ' Il y a une super-pastille dans la case
            Case "O"
                ' Ellipse de quatre pixels de rayon
                .DrawEllipse lX, lY, 4, 4, 1, vbWhite, vbWhite
                gNbPastilles = gNbPastilles + 1
            End Select
        Next
    Next
    ' On conserve le dessin du niveau avec ses murs et ses pastilles
    .ImageKeep

Notez qu'on conserve ce contenu statique avec un appel à ImageKeep une fois le dessin terminé.

On obtient le dessin des murs et des pastilles.

Image non disponible

XV-D. Chargement des ressources

A l'initialisation de l'écran, nous devons charger les ressources, c'est-à-dire les images et les sons.

Chargement des images et sons
Sélectionnez
' Chargement des images du PacMan
Dim lCpt As Integer
With oGdi.ImgNew("pac0")
    .LoadFile ThisWorkbook.Path & "\images\pac0.png"
    .Resize gTx
End With
For lCpt = 1 To 3
    With oGdi.ImgNew("pacg" & lCpt)
        .LoadFile ThisWorkbook.Path & "\images\pacg" & lCpt & ".png"
        .Resize gTx
    End With
    With oGdi.ImgNew("pach" & lCpt)
        .LoadFile ThisWorkbook.Path & "\images\pach" & lCpt & ".png"
        .Resize gTx
    End With
    With oGdi.ImgNew("pacd" & lCpt)
        .LoadFile ThisWorkbook.Path & "\images\pacd" & lCpt & ".png"
        .Resize gTx
    End With
    With oGdi.ImgNew("pacb" & lCpt)
        .LoadFile ThisWorkbook.Path & "\images\pacb" & lCpt & ".png"
        .Resize gTx
    End With
Next
' Chargement des images des fantômes en état "normal"
With oGdi.ImgNew("ghost1")
    .LoadFile ThisWorkbook.Path & "\images\fantome.png"
    With oGdi.ImgClone("ghost1", "ghost2")
        .ReplaceColor vbRed, vbGreen
        .Resize gTx
    End With
    With oGdi.ImgClone("ghost1", "ghost3")
        .ReplaceColor vbRed, vbYellow
        .Resize gTx
    End With
    .Resize gTx
End With
' Chargement de l'images des fantômes  en état "effrayé"
With oGdi.ImgNew("ghostscared")
    .LoadFile ThisWorkbook.Path & "\images\fantomeeffraye.png"
    .ReplaceColor vbBlack, vbBlack, , 0
End With
' Chargement de l'images des yeux de fantômes pour le retour au centre
With oGdi.ImgNew("yeux")
    .LoadFile ThisWorkbook.Path & "\images\yeux.png"
    .ReplaceColor vbBlack, vbBlack, , 0
End With
' Chargement des sons
oSound.OpenSound ThisWorkbook.Path & "\sons\aie.wav", "aie"
oSound.OpenSound ThisWorkbook.Path & "\sons\aouch.wav", "aouch"
oSound.OpenSound ThisWorkbook.Path & "\sons\blip.wav", "blip"

Les images sont chargées dans l'objet oGdi.
Chaque image est identifiée par une chaîne de caractères.
On redimensionne au chargement chaque image à la taille d'une case (avec le paramètre pWidth = gTx).
Pour les fantômes, on génère trois images, en modifiant la couleur rouge pour obtenir 3 fantômes de couleur différente.

Attention à modifier la couleur avant le redimensionnement qui, avec le lissage de l'image va un peu mélanger les couleurs.
Les sons sont chargés dans l'objet oSound, identifiés également par une chaîne de caractères.

XV-E. Création de du module objet clSprite

Nous allons dans le jeu avoir à gérer deux types d'objets : le PacMan et les fantômes.
Ces objets sont similaires, ce sont tout simplement des sprites.

Les données du Pacman et de chaque fantôme seront stockées dans un objet de type clSprite qui contient :

  • leur position ;
  • leur mouvement ;
  • le mouvement suivant ;
  • leur vitesse ;
  • un compteur d'image (utilisé pour l'animation du PacMan et pour les couleurs de fantômes).

Créez donc un module de classe nommé clSprite.

Code du module clSprite
Sélectionnez
'***************************************************************************************
'*                             CLASSE POUR SPRITE
'***************************************************************************************
Option Explicit
            
' Position
Public X As Currency
Public Y As Currency
' Mouvement (g,d,h,b, ou vide si arrêté)
Public Mvt As String
' Mouvement en attente (entre deux cases)
Public MvtSuivant As String
' Numéro de l'image
Public Img As Integer
' Vitesse
Public Vitesse As Currency

On utilise pour la position et la vitesse un type de données Currency car en VBA ce type permet de gérer correctement des valeurs non entières.
Les types Single ou Double ne conviendraient pas car VBA arrondi ces valeurs.

Ainsi un sprite en position X = 10.4 se trouvera entre la colonne 10 et 11.
Lorsque le sprite atteint la position 11, on sait que le sprite est au centre d'une colonne.

Les mouvements sont des lettres :

  • g pour gauche ;
  • d pour droite ;
  • h pour haut ;
  • b pour bas.

Il n'y a pas de mouvement possible en diagonale.

On utilise une variable MvtSuivant pour stocker le mouvement suivant.
Ainsi lorsque le joueur appuie sur une touche, on détecte le mouvement demandé que l'on stocke dans cette variable.
Le sprite continue son chemin jusqu'à atteindre une case « pleine » (positions X et Y entières) et on modifie alors son mouvement.
De cette manière le joueur peut demander le déplacement du sprite sans avoir à appuyer sur la touche de direction pile au moment où le sprite atteint le centre d'une case.

XV-F. Gestion du Pacman

Le Pacman est donc un objet de type clSprite.
Déclarez cet objet dans le module clScreenGame

Déclaration de l'objet PacMan
Sélectionnez
' Objet Pacman
Private oPacman As clSprite

A l'initialisation de l'écran de jeu, on va créer l'objet Pacman et lui donner ses paramètres par défaut.

Initialisation de l'objet PacMan dans Class_Initialize du module clScreenGame
Sélectionnez
' Initialisation du Pacman
Set oPacman = New clSprite
' Pac immobile par défaut
oPacman.Mvt = ""
oPacman.MvtSuivant = ""
' Initialise la position de départ
oPacman.X = 14
oPacman.Y = 22
' Image initial du Pac
oPacman.Img = 0
' Vitesse initiale du Pac
oPacman.Vitesse = 0.1

Au début du jeu, le PacMan est immobile.
Il n'y a pas de mouvement.
Par contre on initialise sa vitesse qui est d'un dixième de case.
On le place au départ vers le bas au centre (colonne 14, ligne 22).

Pour dessiner le PacMan à l'écran, on va placer le code de dessin dans la fonction ClScreen_UpdateScreen.
On commence par restaurer le contenu statique, puis on affichage l'image du PacMan.
L'affichage d'une image se fait à l'aide de la fonction DrawImg de l'objet oGdi.
On utilise la fonction GetCenter pour calculer la position du centre du PacMan, puis on affiche l'image.
(Notez qu'on a défini les paramètres de GetCenter en Currency pour pouvoir passer des paramètres non entiers).

Affichage du PacMan dans ClScreen_UpdateScreen du module clScreenGame
Sélectionnez
' Position sur le dessin
Dim lX As Long, lY As Long
' Affichage
' Reprend l'image de la mémoire
oGdi.ImageReset
' Calcule le centre du PacMan
GetCenter oPacman.X, oPacman.Y, lX, lY
' Dessine le PacMan
oGdi.DrawImg "pac" & IIf(oPacman.Img=0, "", oPacman.Mvt) & oPacman.Img  , lX, lY, , , , GdipSizeModeautosize

Dans la fonction DrawImg, le paramètre GdipSizeModeautosize signifie qu'on affiche l'image sans redimensionnement et centrée sur les deux premières coordonnées.

L'image du PacMan à afficher est fonction de son mouvement.

Si le PacMan est en mouvement (paramètre Mvt non vide, différent de "") alors l'image du PacMan est pac[Mvt][Img].

Si le PacMan est immobile, l'image est pac0.

Par exemple pour un mouvement vers la droite, les images seront :

  • pac0 ;
  • pacd1 ;
  • pacd2 ;
  • pacd3.

Si on affiche le formulaire, on visualise alors le PacMan, sagement immobile.

Image non disponible

Il faut maintenant tester l'appui de touche afin de modifier le mouvement du PacMan.

Test des commandes dans ClScreen_UpdateCommand du module clScreenGame
Sélectionnez
Dim lDown As Boolean, lUp As Boolean, lLeft As Boolean, lRight As Boolean
' Test directions du joystick
oCommand.TestJoypadDir lLeft, lRight, lUp, lDown
' Test des commandes de déplacement du Pacman
If oCommand.TestKey(oSharedData.ToucheBas) Or lDown Then
    oPacman.MvtSuivant = "b"
End If
If oCommand.TestKey(oSharedData.ToucheDroite) Or lRight Then
    oPacman.MvtSuivant = "d"
End If
If oCommand.TestKey(oSharedData.ToucheGauche) Or lLeft Then
    oPacman.MvtSuivant = "g"
End If
If oCommand.TestKey(oSharedData.ToucheHaut) Or lUp Then
    oPacman.MvtSuivant = "h"
End If

Comme prévu, on modifie le paramètre MvtSuivant de l'objet Pacman.

Dans la fonction de mise à jour de l'écran, on modifie la position du PacMan en fonction de son mouvement.
Puis on teste si le PacMan est sur une case « pleine » (positions entière) pour remplacer le mouvement en cours par le mouvement suivant.
On effectue le déplacement des sprites avant leur dessin, donc en début de fonction.

Déplacement du Pacman dans ClScreen_UpdateScreen (au début) du module clScreenGame
Sélectionnez
' Déplacement du PacMan en fonction de son mouvement et sa vitesse
Select Case oPacman.Mvt
    Case "d"
        oPacman.X = oPacman.X + oPacman.Vitesse
    Case "g"
        oPacman.X = oPacman.X - oPacman.Vitesse
    Case "h"
        oPacman.Y = oPacman.Y - oPacman.Vitesse
    Case "b"
        oPacman.Y = oPacman.Y + oPacman.Vitesse
End Select
' Si le PacMan atteint une case "pleine" => modifie le mouvement en fonction du mouvement suivant demandé
If oPacman.X = Int(oPacman.X) And oPacman.Y = Int(oPacman.Y) Then
    ' Mouvement suivant
    oPacman.Mvt = oPacman.MvtSuivant
End If

Il manque l'animation du PacMan.

Animation du Pacman dans ClScreen_UpdateScreen du module clScreenGame
Sélectionnez
' Animation du PacMan
' On tourne sur 4 images
If oPacman.Mvt = "" Then
    ' Si PacMan immobile => image 0
    oPacman.Img = 0
Else
    oPacman.Img = (oPacman.Img + 1) Mod 4
End If

Le déplacement du PacMan fonctionne correctement, sauf qu'il peut traverser les murs.

Il faut donc tester s'il y a un mur dans la direction du mouvement suivant et annuler le mouvement si nécessaire.
On empêche également que le PacMan ne traverse la porte au centre d'où les fantômes sortent.

Et enfin on en profite pour gérer le passage d'un côté à l'autre du jeu si le Pacman est sur une case contenant « P » (pour simplifier le tutoriel, on met les valeurs du passage « en dur » = colonne 27 ou 0).

On a besoin de déclarer quelques variables qui contiendront le contenu des cases.

Déclarations pour valeurs des cases dans ClScreen_UpdateScreen
Sélectionnez
' Valeur des cases et des alentours
Dim lCase As String
Dim lUp As String
Dim lDown As String
Dim lRight As String
Dim lLeft As String
Empêche le Pacman de traverser les murs
Sélectionnez
' Si le PacMan atteint une case "pleine" => modifie le mouvement en fonction du mouvement suivant demandé
If oPacman.X = Int(oPacman.X) And oPacman.Y = Int(oPacman.Y) Then
    ' Contenu des cases
    lCase = GetValue(oPacman.X, oPacman.Y)
    lRight = GetValue(oPacman.X + 1, oPacman.Y)
    lLeft = GetValue(oPacman.X - 1, oPacman.Y)
    lUp = GetValue(oPacman.X, oPacman.Y - 1)
    lDown = GetValue(oPacman.X, oPacman.Y + 1)
    ' Passage d'un côté à l'autre du jeu
    If lCase = "P" Then
        If oPacman.X = 0 Then
            oPacman.X = 27
        ElseIf oPacman.X = 27 Then
            oPacman.X = 0
        End If
    End If
    ' Mouvement suivant
    oPacman.Mvt = oPacman.MvtSuivant
    ' Teste si le mouvement suivant est possible
    If oPacman.Mvt = "d" And lRight = "X" Then
        oPacman.Mvt = ""
    End If
    If oPacman.Mvt = "g" And lLeft = "X" Then
        oPacman.Mvt = ""
    End If
    If oPacman.Mvt = "b" And (lDown = "X" Or lDown = "_") Then
        oPacman.Mvt = ""
    End If
    If oPacman.Mvt = "h" And lUp = "X" Then
        oPacman.Mvt = ""
    End If
End If

Le Pacman peut maintenant être déplacé correctement dans tout le jeu.

XV-G. Gestion des pastilles

Les pastilles sont mangées par le PacMan.
Lorsqu'une pastille est mangée, on noirci la case en noir sur le contenu statique puis on resauvegarde ce contenu.

Il faut donc déplacer l'appel à la fonction ImageReset en début de fonction ClScreen_UpdateScreen, après les déclarations.

En effet on va :

  • d'abord restaurer le contenu statique ;
  • puis tester si le PacMan mange une pastille et la supprimer de l'affichage ;
  • et enfin on dessinera les sprites.
A déplacer en début de fonction ClScreen_UpdateScreen du module clScreenGame
Sélectionnez
' Reprend l'image de la mémoire
oGdi.ImageReset

Lorsque le Pacman atteint une case pleine, on va alors tester si c'est une pastille et la supprimer de l'affichage.

On supprime également la pastille du tableau, et on joue un son.
Et enfin on décrémente le compteur de pastille et on ajoute 1 au score.

Pour le score, il faut ajouter une variable partagée dans clSharedData.

Déclaration du score dans clSharedData
Sélectionnez
' Score
Public Score As Long
Suppression d'une pastille dans clScreenGame
Sélectionnez
' Si le PacMan atteint une case "pleine" => modifie le mouvement en fonction du mouvement suivant demandé
If oPacman.X = Int(oPacman.X) And oPacman.Y = Int(oPacman.Y) Then
    ' Contenu des cases
    lCase = GetValue(oPacman.X, oPacman.Y)
    lRight = GetValue(oPacman.X + 1, oPacman.Y)
    lLeft = GetValue(oPacman.X - 1, oPacman.Y)
    lUp = GetValue(oPacman.X, oPacman.Y - 1)
    lDown = GetValue(oPacman.X, oPacman.Y + 1)
    ' Mange une pastille
    If lCase = "o" Or lCase = "O" Then
        ' Calcule le centre de la pastille
        GetCenter oPacman.X, oPacman.Y, lX, lY
        ' Noirci la case
        oGdi.DrawRectangle lX - gTx / 2, lY - gTy / 2, lX + gTx / 2, lY + gTy / 2, vbBlack, vbBlack
        ' Supprime la pastille du tableau
        gTableau(oPacman.X, oPacman.Y) = ""
        ' Joue un son
        oSound.PlaySound "blip"
        ' Décrémente le compteur de pastille
        gNbPastilles = gNbPastilles - 1
        ' Incrémente le score
        oSharedData.Score = oSharedData.Score + 1
        ' Sauvegarde le contenu "statique"
        oGdi.ImageKeep
    End If
[...]

Pour visualiser le score, on ajoute un code de dessin à la fin de la fonction ClScreen_UpdateScreen.

Affichage du score à la fin de ClScreen_UpdateScreen
Sélectionnez
' Affiche le score
oGdi.DrawText "Score : " & oSharedData.Score, 20, , 0, 0, _
    oGdi.ImageWidth, oGdi.ImageHeight, 2, 2, vbRed, , _
    RGB(255, 255, 100)

Le Pacman peut maintenant manger les pastilles, et on voit le score en bas à droite s'incrémenter à chaque pastille.

Image non disponible

XV-H. Gestion des fantômes

Les fantômes sont également des objets de type clSprite.
Mais il y a plusieurs fantômes, dont le nombre est défini dans les options.
Nous allons donc déclarer et initialiser une collection de fantômes.

Déclaration de la collection de fantômes en en-tête du module clScreenGame
Sélectionnez
' Collection de fantômes
Private oCollFantomes As Collection
Déclarations nécessaires dans Class_Initialize de l'écran de jeu
Sélectionnez
' Compteur
Dim lCpt As Long
' Objet Fantome
Dim lFantome As clSprite
Initialisation de la collection de fantômes à l'initialisation de l'écran de jeu dans Class_Initialize
Sélectionnez
' Initialisation des fantômes
Set oCollFantomes = New Collection
If oCollFantomes Is Nothing Then
    For lCpt = 1 To oSharedData.NbFantomes
        Set lFantome = New clSprite
        ' Position initiale des fantôme
        lFantome.X = Int(11 + Rnd * 6) ' Colonne entre 11 et 16
        lFantome.Y = 14 ' Ligne 14
        ' Image (on tourne sur trois images pour changer la couleur)
        lFantome.img = 1 + (lCpt Mod 3)
        ' Vitesse initiale des fantômes
        lFantome.Vitesse = 0.1
        ' Ajout à la collection
        oCollFantomes.Add lFantome
    Next
End If

Si on affiche le formulaire on obtient une erreur d'exécution '91' : Variable objet ou variable de bloc With non définie.
Cette erreur est levée lorsqu'on essaye de lire le nombre de fantôme de l'objet oSharedData.
En effet à l'initialisation de l'écran, l'objet oSharedData n'est pas encore affecté.
Et malheureusement VBA ne permet pas de passer des paramètres au constructeur de la classe.
Il faut alors déplacer ce code d'initialisation des fantômes dans la fonction ClScreen_UpdateScreen, au début après les déclarations.
On vérifie que la collection de fantômes n'est pas initialisée afin de ne le faire qu'une seule fois.

Déclarations à déplacer dans la fonction ClScreen_UpdateScreen
Sélectionnez
' Compteur
Dim lCpt As Long
' Objet Fantome
Dim lFantome As clSprite
Initialisation de la collection de fantômes, à déplacer dans la fonction ClScreen_UpdateScreen
Sélectionnez
' Initialisation des fantômes
If oCollFantomes Is Nothing Then ' une seule initialisation
    Set oCollFantomes = New Collection
    For lCpt = 1 To oSharedData.NbFantomes
        Set lFantome = New clSprite
        ' Position initiale des fantôme
        lFantome.X = Int(11 + Rnd * 6) ' Colonne entre 11 et 16
        lFantome.Y = 14 ' Ligne 14
        ' Image (on tourne sur trois images)
        lFantome.Img = 1 + (lCpt Mod 3)
        ' Vitesse initiale des fantômes
        lFantome.Vitesse = 0.1
        ' Ajout à la collection
        oCollFantomes.Add lFantome
    Next
End If

On va ensuite gérer le déplacement et l'affichage des fantômes après le déplacement et l'affichage du Pacman dans la fonction ClScreen_UpdateScreen.
Etant donné qu'il y a plusieurs fantômes, on doit faire une boucle pour gérer chaque fantôme de la collection.

Comme pour le Pacman, on va d'abord déplacer le fantôme en fonction de sa vitesse.
Puis si le fantôme est sur une case « pleine » (positions entières), on recherche le mouvement à affecter au fantôme.
Dans le cas des fantômes, on n'utilise pas le paramètre MvtSuivant qui n'aurait pas d'intérêt.

Le mouvement des fantômes doit répondre aux règles suivantes :

  • Le fantôme doit se rapprocher du PacMan => on recherche donc l'axe où la distance entre le fantôme et le PacMan est la plus grande (X ou Y).
  • Si le fantôme est sur la porte de sortie (repérée "_"), alors il se déplace vers le haut (afin d'éviter de sortir et rentrer sans cesse).
  • Le fantôme ne peut pas rentrer par la porte au centre de l'écran (repérée »_"), il ne peut que sortir.
  • Si on n'a pas pû déterminer le déplacement optimal du fantôme, on lui donne un mouvement aléatoire.
  • Le fantôme ne peut pas se déplacer sur un mur ("X »), ou sur un des 2 passages ("P").

Et enfin on dessine le fantôme, de la même manière qu'on a dessiné le PacMan.
Pour un fantôme, l'image est ghost1, ghost2 ou ghost3 en fonction du paramètre Img du fantôme.

Déclaration nécessaires au début de ClScreen_UpdateScreen
Sélectionnez
' Variable pour numéro de fantôme
Dim lCalc as long
Gestion des déplacements des fantômes dans ClScreen_UpdateScreen
Sélectionnez
' Gestion des fantômes
For Each lFantome In oCollFantomes
    ' Déplacement des fantômes
    Select Case lFantome.Mvt
        Case "d"
            lFantome.X = lFantome.X + lFantome.Vitesse
        Case "g"
            lFantome.X = lFantome.X - lFantome.Vitesse
        Case "h"
            lFantome.Y = lFantome.Y - lFantome.Vitesse
        Case "b"
            lFantome.Y = lFantome.Y + lFantome.Vitesse
    End Select
    ' Test si le fantôme est sur une case "pleine"
    If Int(lFantome.X) = lFantome.X And Int(lFantome.Y) = lFantome.Y Then
        ' Lecture du contenu des cases aux alentours du fantôme
        lRight = GetValue(lFantome.X + 1, lFantome.Y)
        lLeft = GetValue(lFantome.X - 1, lFantome.Y)
        lUp = GetValue(lFantome.X, lFantome.Y - 1)
        lDown = GetValue(lFantome.X, lFantome.Y + 1)
        ' Pas de mouvement par défaut
        lFantome.Mvt = ""
        ' Recherche du mouvement pour se rapprocher du PacMan
        ' Si distance X plus grand que distance Y
        If Abs(lFantome.X - oPacman.X) > Abs(lFantome.Y - oPacman.Y) Then
            If lFantome.X < oPacman.X And lRight <> "X" And lRight <> "P" Then
                lFantome.Mvt = "d"
            ElseIf lFantome.X >= oPacman.X And lLeft <> "X" And lLeft <> "P" Then
                lFantome.Mvt = "g"
            End If
        End If
        ' Si distance Y plus grand que distance X ou que le cas précédent n'a pas donné de mouvement possible
        If lFantome.Mvt = "" Then
            If lFantome.Y < oPacman.Y And lDown <> "X" And lDown <> "_" Then
                lFantome.Mvt = "b"
            ElseIf lFantome.Y >= oPacman.Y And lUp <> "X" Then
                lFantome.Mvt = "h"
            End If
        End If
        ' Si on est sur la porte de sortie => déplacement vers le haut
        If GetValue(lFantome.X, lFantome.Y) = "_" Then lFantome.Mvt = "h"
        ' Si fantôme immobile => mouvement aléatoire pour le lancer
        If lFantome.Mvt = "" Then
            lCalc = 1 + Int(Rnd * 4)
            Select Case lCalc
                Case 1: lFantome.Mvt = "d"
                Case 2: lFantome.Mvt = "g"
                Case 3: lFantome.Mvt = "h"
                Case 4: lFantome.Mvt = "b"
            End Select
        End If
        ' Teste si le mouvement est possible, sinon on l'annule
        If lFantome.Mvt = "d" And (lRight = "X" Or lRight = "P") Then
            lFantome.Mvt = ""
        End If
        If lFantome.Mvt = "g" And (lLeft = "X" Or lLeft = "P") Then
            lFantome.Mvt = ""
        End If
        If lFantome.Mvt = "b" And (lDown = "X" Or lDown = "_") Then
            lFantome.Mvt = ""
        End If
        If lFantome.Mvt = "h" And lUp = "X" Then
            lFantome.Mvt = ""
        End If
    End If
    ' Calcule le centre du fantôme
    GetCenter lFantome.X, lFantome.Y, lX, lY
    ' Dessine le fantôme
    oGdi.DrawImg "ghost" & lFantome.Img, lX, lY, , , , GdipSizeModeautosize
Next

Testez l'affichage du jeu, on voit bien les fantômes se déplacer.
Les fantômes sont plutôt stupides dans leur déplacement, mais il ne faudrait pas les rendre trop intelligents si on veut que le jeu soit jouable.

XV-I. Super-pastille : fantômes effrayés

Un élément essentiel du jeu est le changement d'état des fantômes.

Lorsque le Pacman mange une super-pastille (repérée par un « O » majuscule), les fantômes doivent changer d'état :

  • ils deviennent bleus ;
  • ils se déplacent plus lentement et fuient le PacMan ;
  • ils sont mangeables.

Cet état est temporaire, on le fait durer 15 secondes.

Ajoutons un compteur pour l'état effrayé pour les objets fantômes

Ajout de l'état effrayé au module clSprite
Sélectionnez
' Etat effrayé = temps restant
Public Scared As long

Lorsque le PacMan mangera une super-pastille, on initialise ce compteur.
On fait ce test après le premier test sur l'arrivé sur une pastille.

Initialisation du compteur d'état effrayé dans ClScreen_UpdateScreen
Sélectionnez
' Mange une pastille
    If lCase = "o" Or lCase = "O" Then
 [...] ' Test déjà écrit, ajouter le test de super-pastille après celui-ci
    End If
    ' Mange une super-pastille
    If lCase = "O" Then
        ' Pour chaque fantôme
        For Each lFantome In oCollFantomes
            ' Passe le fantôme à l'état effrayé durant 15 secondes
            ' On multiplie par 40 car 40 images par secondes
            lFantome.Scared = 40 * 15
        Next
    End If

Ensuite dans le code de gestion des fantômes, on décrémente le compteur s'il est supérieur à zéro.

Décrémente le compteur d'état effrayé dans ClScreen_UpdateScreen
Sélectionnez
' Gestion des fantômes
For Each lFantome In oCollFantomes
    ' Décrémente le compteur d'état effrayé
    If lFantome.Scared > 0 Then lFantome.Scared = lFantome.Scared - 1
[...]

Et on modifie la recherche de mouvement des fantômes pour les éloigner du PacMan s'ils sont effrayés.

Modifie la recherche de mouvement du fantôme dans ClScreen_UpdateScreen
Sélectionnez
' Pas de mouvement par défaut
        lFantome.Mvt = ""
        ' Recherche du mouvement pour se rapprocher du PacMan
        If lFantome.Scared = 0 Then
            ' Si distance X plus grand que distance Y
            If Abs(lFantome.X - oPacman.X) > Abs(lFantome.Y - oPacman.Y) Then
                If lFantome.X < oPacman.X And lRight <> "X" And lRight <> "P" Then
                    lFantome.Mvt = "d"
                ElseIf lFantome.X >= oPacman.X And lLeft <> "X" And lLeft <> "P" Then
                    lFantome.Mvt = "g"
                End If
            End If
            ' Si distance Y plus grand que distance X ou que le cas précédent n'a pas donné de mouvement possible
            If lFantome.Mvt = "" Then
                If lFantome.Y < oPacman.Y And lDown <> "X" And lDown <> "_" Then
                    lFantome.Mvt = "b"
                ElseIf lFantome.Y >= oPacman.Y And lUp <> "X" Then
                    lFantome.Mvt = "h"
                End If
            End If
        ' Ou pour s'éloigner si le fantome est en état effrayé
        Else
            ' Si distance X plus petite que distance Y
            If Abs(lFantome.X - oPacman.X) < Abs(lFantome.Y - oPacman.Y) Then
                If lFantome.X > oPacman.X And lRight <> "X" And lRight <> "P" Then
                    lFantome.Mvt = "d"
                ElseIf lFantome.X <= oPacman.X And lLeft <> "X" And lLeft <> "P" Then
                    lFantome.Mvt = "g"
                End If
            End If
            ' Si distance Y plus petite que distance X ou que le cas précédent n'a pas donné de mouvement possible
            If lFantome.Mvt = "" Then
                If lFantome.Y > oPacman.Y And lDown <> "X" And lDown <> "_" Then
                    lFantome.Mvt = "b"
                ElseIf lFantome.Y <= oPacman.Y And lUp <> "X" Then
                    lFantome.Mvt = "h"
                End If
            End If
        End If

Et enfin modifiez le code de dessin des fantômes pour afficher l'image fantomeeffraye si nécessaire.

Modification du dessin des fantômes ClScreen_UpdateScreen
Sélectionnez
' Dessine le fantôme
    oGdi.DrawImg "ghost" & IIf(lFantome.Scared > 0, "scared", lFantome.Img), lX, lY, , , , GdipSizeModeautosize

Les fantômes sont maintenant bleus durant 15 secondes lorsque le PacMan mange une super-pastille.
Et ils s'éloignent au lieu de se rapprocher.
Par contre leur vitesse n'est pas modifiée.

Lors de la modification de la vitesse il faut bien faire attention.
En effet on teste l'arrivée du sprite sur une case en testant si les positions sont entières.
En modifiant la vitesse, on risque de ne plus arriver à des valeurs entières.

Par exemple :

La position X est égale à 9.95 et on modifie la vitesse de 0.05 à 0.1.
La position suivante vaudra alors 10.05 sans passer par la valeur entière 10.
Il faut donc ajuster la position en fonction de la vitesse définie.
Pour modifier la position lors de la modification de la vitesse, on va transformer la variable publique Vitesse en propriété.

Modification de la déclaration de Vitesse dans clSprite
Sélectionnez
Private gVitesse As Currency
Propriété pour lecture et écriture de la vitesse dans clSprite
Sélectionnez
' Propriété pour lecture et écriture de la vitesse
Public Property Get Vitesse() As Currency
Vitesse = gVitesse
End Property
Public Property Let Vitesse(pVitesse As Currency)
gVitesse = pVitesse
' Si vitesse non nulle
If gVitesse <> 0 Then
    ' Déplace légérement le sprite pour se repositionner sur un multiple de la vitesse
    X = Int(X) + Int((X - Int(X)) / gVitesse) * gVitesse
    Y = Int(Y) + Int((Y - Int(Y)) / gVitesse) * gVitesse
End If
End Property

Pour que cela fonctionne, la vitesse doit être un dividende de 1 (exemple : 0.2 ou 0.1).
Une vitesse de 0.3 ou de 0.04 ne conviendrait pas.

Le PacMan a une vitesse constante de 0.1.
Les fantômes ont une vitesse de 0.1 en état normal, et de 0.05 en état effrayé.

La modification de la vitesse des fantômes va se faire également dans le module clSprite.
Lors de la modification du compteur Scared, on va modifier la vitesse des fantômes.
Pour cela il faut tranformer la variable publique Scared en propriété.

Modification de la déclaration de Scared dans clSprite
Sélectionnez
' Etat effrayé
Private gScared As Long
Propriété pour lecture et écriture du compteur d'état effrayé dans clSprite
Sélectionnez
' Propriété pour lecture et écriture de l'état effrayé
Public Property Get Scared() As Long
Scared = gScared
End Property
Public Property Let Scared(pScared As Long)
If gScared = 0 And pScared <> 0 Then
    ' Passage à l'état effrayé => on passe la vitesse de 0.1 à 0.05
    Vitesse = gVitesse / 2
ElseIf gScared <> 0 And pScared = 0 Then
    ' Passage à l'état normal => on repasse la vitesse de  à 0.1
    Vitesse = gVitesse * 2
End If
gScared = pScared
End Property

Le passage des fantômes de l'état effrayé à l'état normal et vice-versa est terminé.

XV-J. Gestion des collisions entre les sprites

Pour simplifier, il n'y a pas de gestion de collisions entre les fantômes : ils se traversent.
Par contre il faut gérer la collision entre le PacMan et les fantômes.

On simplifie également la détection de collision entre les sprites en détectant la collision entre deux rectangles.
Il faudrait vérifier si un des 4 coins du rectangle encadrant un fantome se trouve dans le rectangle encadrant le PacMan.
Plutôt que de tester toutes ces coordonnées (ce ne serait cependant pas très compliqué), on va utiliser les régions de gdi+.

Lors du dessin des sprites (fantôme et PacMan), on va ajouter une région correspondant au rectangle dans lequel le sprite est dessiné.
Cela se fait simplement en rajoutant un paramètre à l'appel de la fonction DrawImg de l'objet oGdi.
Pour identifier le sprite, on va utiliser le pointeur de l'objet qui est déterminé avec ObjPtr.
Ce pointeur est garanti unique.

Modification du dessin du PacMan pour ajouter une région
Sélectionnez
' Dessine le PacMan et ajoute une région rectangulaire
oGdi.DrawImg "pac" & IIf(oPacman.img = 0, "", oPacman.Mvt) & oPacman.img, lX, lY, , , , _
                        GdipSizeModeAutoSize, , , CStr(ObjPtr(oPacman))
Modification du dessin des fantômes pour ajouter une région
Sélectionnez
' Dessine le fantôme et ajoute une région rectangulaire
    oGdi.DrawImg "ghost" & IIf(lFantome.Scared > 0, "scared", lFantome.img), lX, lY, , , , _
                            GdipSizeModeAutoSize, , , CStr(ObjPtr(lFantome))

On va ensuite rechercher une éventuelle collision après avoir dessiner les sprites.
On profite de la boucle déjà en place sur la collection de fantômes.
Les collisions sont testées à l'aide de la fonction RegionsIntersect de l'objet oGdi.
Pour tester rapidement, on écrit un texte en bas à gauche lorsque le PacMan touche un fantôme.

Test de collision
Sélectionnez
' Gestion des fantômes
For Each lFantome In oCollFantomes
[...]
    ' Détection de collision avec le PacMan
    If oGdi.RegionsIntersect(ObjPtr(oPacman), ObjPtr(lFantome)) Then
        oGdi.DrawText "Collision", 20, , 0, 0, oGdi.ImageWidth, oGdi.ImageHeight, 0, 2, vbRed, , RGB(255, 255, 100), , , , , , True
    End If
Next

Affichez le formulaire et allez au contact d'un fantôme pour visualiser la collision.

Que faire en cas de collision ?

Si le fantôme est en état normal => le PacMan est mangé => on affiche l'écran de jeu perdu.
Si le fantôme est en état effrayé => le fantôme est mangé => il doit retourner au centre.

Dans les deux cas, on joue un son.

Pour l'état normal c'est facile :

Le fantôme mange le PacMan
Sélectionnez
' Détection de collision avec le PacMan
    If oGdi.RegionsIntersect(ObjPtr(oPacman), ObjPtr(lFantome)) Then
        If lFantome.Scared = 0 Then
            ' Le fantôme est en état normal => il mange le PacMan
            ' On joue un son
            oSound.PlaySound "aouch"
            ' On demande le changement d'écran vers l'écran de jeu perdu
            gNextScreen = "gameover"
            gChangeScreen = True
        Else
            ' Le fantôme est en état effrayé => il est mangé
  ' Code à suivre
        End If
    End If

Pour l'état effrayé c'est plus compliqué :
Il faut définir une nouvelle propriété des sprites : un indicateur de retour au centre.
Lorsque cet indicateur est à Vrai :

  • les fantômes doivent suivre le chemin pour retourner au centre ;
  • ils sont accélérés pour un retour rapide ;
  • seuls leurs yeux s'affichent ;
  • ils ne doivent pas être remangés ou et ne doivent pas pouvoir manger le PacMan ;
  • ils reviennent ensuite à l'état normal.
Déclaration de l'indicateur de retour au centre dans clSprite
Sélectionnez
' Indicateur pour retour au centre
Private gReturnToCentre As Boolean
Propriétés pour l'indicateur de retour au centre dans clSprite
Sélectionnez
' Propriété pour lecture et écriture du retour au centre
Public Property Get ReturnToCentre() As Boolean
ReturnToCentre = gReturnToCentre
End Property
Public Property Let ReturnToCentre(pReturnToCentre As Boolean)
If gReturnToCentre = False And pReturnToCentre = True Then
    ' Met le flag de retour au centre => on passe la vitesse à 0.25
    Vitesse = 0.25
ElseIf ReturnToCentre = True And pReturnToCentre = False Then
    ' Annule le flag de retour au centre => on repasse la vitesse à 0.1
    Vitesse = 0.1
End If
gReturnToCentre = pReturnToCentre
End Property

Modifions ensuite la détection des collisions.
Notez qu'on ajoute 50 points au score par fantôme mangé.

Modification de la détection des collisions
Sélectionnez
' Détection de collision avec le PacMan
    ' Pas de détection si retour au centre
    If Not lFantome.ReturnToCentre And oGdi.RegionsIntersect(ObjPtr(oPacman), ObjPtr(lFantome)) Then
        If lFantome.Scared = 0 Then
            ' Le fantôme est en état normal => il mange le PacMan
            ' On joue un son
            oSound.PlaySound "aouch"
            ' On demande le changement d'écran vers l'écran de jeu perdu
            gNextScreen = "gameover"
            gChangeScreen = True
        Else
            ' Le fantôme est en état effrayé => il est mangé
            ' On joue un son
            oSound.PlaySound "aie"
            ' Incrémente le score de 50
            oSharedData.Score = oSharedData.Score + 50
            ' Fin de l'état effrayé
            lFantome.Scared = 0
            ' Retour au centre
            lFantome.ReturnToCentre = True
        End If
    End If

Occupons maintenant du retour au centre des fantômes.

XV-K. Retour au centre des fantômes

Les déplacements à suivre pour le retour au centre ont été définis dans la feuille Chemin.
De la même manière que pour les données du tableau de la feuille Jeu, nous allons charger ces données dans un tableau.

Déclaration en en-tête de clScreenGame
Sélectionnez
' Tableau de retour au centre
Private gChemin(0 To 27, 0 To 29) As String
Chargement des déplacements de retour au centre dans Class_Initialize
Sélectionnez
' A insérer après le chargement de gTableau
            
' Charge les données du tableau de retour au centre
With ThisWorkbook.Sheets("Chemin")
    For lCptX = 1 To 28
        For lCptY = 1 To 30
            gChemin(lCptX - 1, lCptY - 1) = .Cells(lCptY, lCptX).Value
        Next
    Next
End With

Ensuite on gère les déplacements dans le cas où ReturnToCentre est Vrai.

Déplacement de retour au centre dans ClScreen_UpdateScreen
Sélectionnez
' Test si le fantôme est sur une case "pleine"
    If Int(lFantome.X) = lFantome.X And Int(lFantome.Y) = lFantome.Y Then
        ' Pas de retour au centre
        If Not lFantome.ReturnToCentre Then
        ' =====> Ici on trouve le code de déplacement précédemment écrit
        Else
            ' Retour au centre
            lFantome.Mvt = gChemin(lFantome.X, lFantome.Y)
            ' Si plus de déplacement => on est arrivé au centre
            If lFantome.Mvt = "" Then lFantome.ReturnToCentre = False
        End If
    End If

Et enfin on affiche la paire d'yeux lors du retour au centre.

Affichage de la paire d'yeux lors du retour au centre dans ClScreen_UpdateScreen
Sélectionnez
' Dessine le fantôme et ajoute une région rectangulaire
    If lFantome.ReturnToCentre Then
    oGdi.DrawImg "yeux", lX, lY, , , , _
                            GdipSizeModeAutoSize, , , CStr(ObjPtr(lFantome))
    Else
        oGdi.DrawImg "ghost" & IIf(lFantome.Scared > 0, "scared", lFantome.img), lX, lY, , , , _
                            GdipSizeModeAutoSize, , , CStr(ObjPtr(lFantome))
    End If

On peut alors tester le retour au centre des fantômes :

Image non disponible

XV-L. Fin du jeu

Il nous reste une dernière chose : tester si les pastilles ont été toutes mangées.
A la fin de la procédure de mise à jour du jeu (ClScreen_UpdateScreen), on teste si le compteur de pastille est à zéro.
Si c'est le cas, on bascule vers l'écran de jeu gagné.

Test de fin du jeu à la fin de ClScreen_UpdateScreen
Sélectionnez
' Test de fin du jeu
If gNbPastilles = 0 Then
    gNextScreen = "gameend"
    gChangeScreen = True
End If

Si on mange toutes les pastilles, le jeu se termine.

Prochaines étapes : la création des écrans de fin de jeu :

  • écran de jeu perdu ;
  • écran de jeu gagné ;
  • écran des scores ;
  • écran des crédits.

précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.