PureBasic:Realiser un RPG2D/Les sprites et les collisions


PureBasic:Realiser_un_RPG2D

<< Précédent | Sommaire | Suivant >>



Prérequis

Difficultée estimée de cette étape: moyen
Pré-requis:

  • Connaissance moyenne de PureBasic(allocations dynamiques, pointeurs et gestion des entrées/sorties)


Notre jeu ne sera pas complet tant que nous n'aurons pas un héros qui se déplace sur notre carte. Evidemment, ses déplacements devront être contrôlés afin qu'il ne puisse pas traverser les maisons, arbres et autres décors... c'est ce que nous allons gérer dans cette étape.


Sommaire

[modifier] Les sprites

Les sprites sont des éléments graphiques qui se déplacent sur notre fond et qui sont le plus souvent animés.

Ainsi notre héros aura quatre mouvements:

  • marcher vers la gauche
  • marcher vers la droite
  • marcher vers le haut
  • marcher vers le bas

Tous ces mouvements sont créés en affichant une succession d'images fixes représentant chaque étape de l'action, tout comme dans un dessin animé.

Pour stocker le tout, nous utilisons une grande image que l'on appelle charset.


Notre charset: le héros du jeu est le troisième en haut.
Notre charset: le héros du jeu est le troisième en haut.


Pour gérer tous les sprites du jeu, nous allons avoir une librairie de sprites qui va contenir un tableau de tous les sprites. De plus, chaque sprite sera lui-même composé d'un tableau contenant toutes les animations. Chaque animation étant composé d'une liste de frame également stockés dans un tableau. Tous les tableaux sont alloués dynamiquement lors du chargement de la carte.

Pour résumer, notre type une fois alloué peut se schématiser de cette façon:

Schématisation du type gérant les sprites.
Schématisation du type gérant les sprites.


L'une des principales tâches de cette étape va être d'initialiser, charger, sauvegarder et libérer ses données. Il restera encore à gérer l'affichage proprement dit du sprite avec SDL et les collisions.

[modifier] Gestion des collisions

Le principe de détection des collisions est très simple:

  • on détermine sur quel(s) tile(s) est le sprite
  • on cherche dans le chipset si ce tile est traversable ou pas
  • si tous les tiles sont traversables, le mouvement est autorisé sinon il ne l'est pas.

Ce principe est amélioré en donnant de la marge à chaque sprite en indiquant un espacement dans lequel le sprite peut figurer dans un sprite non-traversables sans annuler le mouvement.

Le plus simple est d'illustrer celà avec un dessin:


Chaque marge définie à l'intérieur de notre sprite permet de créer un centre. Ce centre est le point critique en terme de collision: si cette zone est sur un tile non-traversable le mouvement est annulé.

Globalement, les marges sont définies de manière à créer une boîte qui englobe notre personnage au plus près.

Cependant, on peut remarquer que la marge haute est très importante. Via cette astuce, on va simuler un effet de profondeur, notre héros pourra monter très haut dans les tiles non-traversables et les recouvrir. Si le sprite est juste en-desous d'une caisse par exemple, on aura l'impression qu'il arrive réellement au pied du décor.

[modifier] Création des fichiers sprite.pb et sprite.pbi

Besoin de réviser?

Besoin de réviser?


Ajoutez au projet deux nouveaux fichiers sprite.pb et sprite.pbi. Comme d'habitude, insérez le code de départ de nos fichiers:

sprite.pb

;** sprite.pb
;**
;** Gestion des sprites
;**
;** Rôle:
;** - initialisation et libération des sprites.
;** - affichage des sprites
;** - gestion des événements associés au sprites

XIncludeFile "sprite.pbi"


sprite.pbi

;** sprite.pbi
;**
;** Gestion des sprites
;**
;** Rôle:
;** - initialisation et libération des sprites.
;** - affichage des sprites
;** - gestion des événements associés au sprites

XIncludeFile "map.pbi"


[modifier] Implémentation des types

Besoin de réviser?

Besoin de réviser?


Nous devons tout d'abord implémenter les types décris plus haut dans sprite.pbi.

Enumeration 
    #ANIMATION_HAUT   
    #ANIMATION_DROITE 
    #ANIMATION_BAS    
    #ANIMATION_GAUCHE 
EndEnumeration 



Enumeration 
    #ANIMATION_HAUT   
    #ANIMATION_DROITE 
    #ANIMATION_BAS    
    #ANIMATION_GAUCHE 
EndEnumeration 

Structure s_lib_sprite
    n_sprite.l           ; nombre de sprites chargés 
    *sprites.s_sprite    ; pointeur vers les sprites 
EndStructure
 
Structure s_frame
    x.l                  ; coordonnées de l'animation dans la surface
    y.l          
EndStructure
 
Structure s_anim
    
    n_frame.l            ; nombre de frames dans l'animation 
    cur_frame.l          ; numéro de la frame actuellement affichée 
    lastfrmupdate.l      ; stocke le moment où l'animation est passée à la frame actuelle 
    delaybetweenframes.l ; délai entre deux frames 
    *frame.s_frame       ; pointeur vers les frames 
EndStructure
 
Structure s_sprite
    x.l
    y.l                  ; coordonnées du sprite dans la carte 
    width.l
    height.l             ; taille du sprite 
    
    n_anim.l             ; nombre d'animations pour ce sprite 
    cur_anim.l           ; n° de l'animation courante 
    ispaused_anim.b      ; booléen indiquant si l'animation est en pause 
    *anim.s_anim         ; pointeur vers les animations 
    
    offsetG.l            ; marges du sprite 
    offsetD.l
    offsetH.l
    offsetB.l      
    
    surf.surface;       ; pointeur vers la surface contenant le charset 
EndStructure


[modifier] Initialisation et libération des sprites

Besoin de réviser?

Besoin de réviser?


Attention: nous repartons du code de l'étape n°2, l'éditeur de carte est un projet à part entière.

La fonction d'initialisation de la bibliothèque de sprite met le nombre de sprite à 0 et le pointeur à #NULL.

Procedure InitSprites(*libsprite.s_lib_sprite)
    *libsprite\n_sprite=0
    *libsprite\sprites=#Null
EndProcedure

La fonction de libération parcours la structure en libérant tous les éléments alloués:

Procedure FreeSprites(*libsprite.s_lib_sprite)
    
    Protected i, anim,*Ptsprite.s_sprite,*Ptanim.s_anim,*Ptframe.s_frame
       
    For  i=0 To *libsprite\n_sprite-1
        
        *Ptsprite=*libsprite\sprites + i * SizeOf(s_sprite)
        
        FreeSprite(*Ptsprite\surf\Id)
        
        For anim=0 To *Ptsprite\n_anim-1
            
            *Ptanim=*Ptsprite\anim + anim * SizeOf(s_anim)
            FreeMemory(*Ptanim\frame)
            
        Next anim
        
        FreeMemory(*Ptsprite\anim)
        
    Next i
    
    FreeMemory(*libsprite\sprites)
    
    *libsprite\n_sprite=0   
    
EndProcedure


[modifier] Chargement statique de données

Nous allons créer une fonction qui va remplir la structure à la main. Quel est l'intérêt? En modifiant cette fonction, vous générez absolument n'importe quel fichier sprite sans avoir besoin de développer un éditeur graphique ce qui est coûteux en temps et relativement inutile, à part dans le cadre d'équipes de développements importantes.

J'en profite pour signaler une notation qui sera gardée dans tout le projet à partir de maintenant: le sprite n°0 représente le joueur (le sprite que l'on déplace avec le clavier).

Remarque: Puisque PureBasic ne permet toujours pas d'allouer dynamiquement un tableau dans une structure , cette fois ci je vais utiliser la solution 1 évoquée durant l'étape 3. C'est à dire , utiliser un pointeur intermédiaire pour chaque tableau dynamique *Ptsprite, *Ptanim et *Ptframe.

Procedure SpriteStaticData(*libsprite.s_lib_sprite)
    
    Protected *Ptsprite.s_sprite,*Ptanim.s_anim,*Ptframe.s_frame
    
    *libsprite\n_sprite = 1
    *libsprite\sprites=AllocateMemory(*libsprite\n_sprite*SizeOf(s_sprite))
    
    ; Sprite 0: player 
    *Ptsprite=*libsprite\sprites
    *Ptsprite\x=256
    *Ptsprite\y=86
    
    *Ptsprite\offsetG=5
    *Ptsprite\offsetD=15 
    *Ptsprite\offsetH=20
    *Ptsprite\offsetB=3 
    
    *Ptsprite\surf\Id=LoadSprite(#PB_Any,"sprites\player.bmp")
    TransparentSpriteColor(*Ptsprite\surf\Id,0,0,0)
    *Ptsprite\width=24
    *Ptsprite\height=32
    *Ptsprite\n_anim=4
    *Ptsprite\cur_anim=#ANIMATION_BAS
    *Ptsprite\ispaused_anim=1
    *Ptsprite\anim=AllocateMemory(*Ptsprite\n_anim*SizeOf(s_anim))
    
    ; anim 0: haut 
    *Ptanim=*Ptsprite\anim
    *Ptanim\cur_frame=0
    *Ptanim\n_frame=3
    *Ptanim\lastfrmupdate=ElapsedMilliseconds()
    *Ptanim\delaybetweenframes = 100
    *Ptanim\frame=AllocateMemory(*Ptanim\n_frame*SizeOf(s_frame))
    
    *Ptframe=*Ptanim\frame
    *Ptframe\x=144
    *Ptframe\y=0
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=168
    *Ptframe\y=0
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=192
    *Ptframe\y=0
    
    ; anim 1: droite 
    *Ptanim + SizeOf(s_anim)
    *Ptanim\cur_frame=0
    *Ptanim\n_frame=3
    *Ptanim\lastfrmupdate=ElapsedMilliseconds()
    *Ptanim\delaybetweenframes = 100
    *Ptanim\frame=AllocateMemory(*Ptanim\n_frame*SizeOf(s_frame))
    
    *Ptframe=*Ptanim\frame
    *Ptframe\x=144
    *Ptframe\y=32
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=168
    *Ptframe\y=32
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=192
    *Ptframe\y=32
    
    ; anim 2: bas
    *Ptanim + SizeOf(s_anim)
    *Ptanim\cur_frame=0
    *Ptanim\n_frame=3
    *Ptanim\lastfrmupdate=ElapsedMilliseconds()
    *Ptanim\delaybetweenframes = 100
    *Ptanim\frame=AllocateMemory(*Ptanim\n_frame*SizeOf(s_frame))
    
    *Ptframe=*Ptanim\frame
    *Ptframe\x=144
    *Ptframe\y=64
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=168
    *Ptframe\y=64
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=192
    *Ptframe\y=64
    
    ; anim 3: gauche
    *Ptanim + SizeOf(s_anim)
    *Ptanim\cur_frame=0
    *Ptanim\n_frame=3
    *Ptanim\lastfrmupdate=ElapsedMilliseconds()
    *Ptanim\delaybetweenframes = 100
    *Ptanim\frame=AllocateMemory(*Ptanim\n_frame*SizeOf(s_frame))
    
    *Ptframe=*Ptanim\frame
    *Ptframe\x=144
    *Ptframe\y=96
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=168
    *Ptframe\y=96
    
    *Ptframe + SizeOf(s_frame)
    *Ptframe\x=192
    *Ptframe\y=96
EndProcedure


[modifier] Sauvegarde des sprites

La sauvegarde des sprites reprend le même schéma que la sauvegarde des cartes. On parcourt toute la structure en sauvegardant chaque membre.

Procedure SaveSprites(*libsprite.s_lib_sprite, charset.s, filename.s)
    
    Protected i,anim,frm,*Ptsprite.s_sprite,*Ptanim.s_anim,*Ptframe.s_frame
    *buffer=AllocateMemory(256)
    
    If *libsprite=0
        ProcedureReturn
    EndIf    

    If OpenFile(0,filename)=0
        ProcedureReturn
    EndIf
    
    WriteLong(*libsprite\n_sprite)
     
    For i=0 To *libsprite\n_sprite-1
        
        *Ptsprite=*libsprite\sprites + i * SizeOf(s_sprite)
        
        WriteLong(*Ptsprite\x)
        WriteLong(*Ptsprite\y)
        WriteLong(*Ptsprite\width)
        WriteLong(*Ptsprite\height)
        WriteLong(*Ptsprite\n_anim)
        WriteLong(*Ptsprite\cur_anim)
        WriteByte(*Ptsprite\ispaused_anim)
        
        WriteLong(*Ptsprite\offsetG)
        WriteLong(*Ptsprite\offsetD)
        WriteLong(*Ptsprite\offsetH)
        WriteLong(*Ptsprite\offsetB)
        
        WriteData(@charset,Len(charset))
        WriteData(*buffer,255-Len(charset))
        
        For anim=0 To *Ptsprite\n_anim-1
            
            *Ptanim=*Ptsprite\anim + anim * SizeOf(s_anim)
            
            WriteLong(*Ptanim\n_frame)
            WriteLong(*Ptanim\cur_frame)
            WriteLong(*Ptanim\lastfrmupdate)
            WriteLong(*Ptanim\delaybetweenframes)
            
            For frm=0 To *Ptanim\n_frame-1
                
                *Ptframe=*Ptanim\frame + frm * SizeOf(s_frame)
                
                WriteLong(*Ptframe\x)
                WriteLong(*Ptframe\y)
            Next frm
        Next anim     
    Next i
    
    CloseFile(0)
EndProcedure      


[modifier] Chargement des sprites

Le chargement des sprites est très similaire à ce que nous avons déjà vu. Il s'agit de réaliser l'opération inverse que précédemment en allouant les tableaux.

Procedure LoadSprites(*libsprite.s_lib_sprite, filename.s)
    
    Protected i, anim, frm, *Ptsprite.s_sprite,*Ptanim.s_anim,*Ptframe.s_frame
    
    If *libsprite=0
        ProcedureReturn
    EndIf   
    If OpenFile(0,filename)=0
        ProcedureReturn
    EndIf
    
    *libsprite\n_sprite=ReadLong()
    
    *libsprite\sprites=AllocateMemory(*libsprite\n_sprite*SizeOf(s_sprite))
    
    For i=0 To *libsprite\n_sprite-1
        
        *Ptsprite=*libsprite\sprites + i * SizeOf(s_sprite)
        
        *Ptsprite\x=ReadLong()
        *Ptsprite\y=ReadLong()
        *Ptsprite\width=ReadLong()
        *Ptsprite\height=ReadLong()
        *Ptsprite\n_anim=ReadLong()
        *Ptsprite\cur_anim=ReadLong()
        *Ptsprite\ispaused_anim=ReadByte()
        
        *Ptsprite\offsetG=ReadLong()
        *Ptsprite\offsetD=ReadLong()
        *Ptsprite\offsetH=ReadLong()
        *Ptsprite\offsetB=ReadLong()
        
        FichierCharset.s=Space(256)
        ReadData(@FichierCharset,255)
        
        *Ptsprite\surf\Id=LoadSprite(#PB_Any,FichierCharset)
        
        If *Ptsprite\surf\Id=0
            MessageRequester("Erreur","Impossible de charger " + FichierCharset,0)
            End
        EndIf    
        
        ;Change la couleur transparente du sprite
        TransparentSpriteColor(*Ptsprite\surf\Id, 0, 0, 0)
        
        *Ptsprite\anim=AllocateMemory(*Ptsprite\n_anim*SizeOf(s_anim))
        
        For anim=0 To *Ptsprite\n_anim-1
            
            *Ptanim=*Ptsprite\anim + anim * SizeOf(s_anim)
            
            *Ptanim\n_frame=ReadLong()
            *Ptanim\cur_frame=ReadLong()
            *Ptanim\lastfrmupdate=ReadLong()
            *Ptanim\delaybetweenframes=ReadLong()
            
            *Ptanim\frame=AllocateMemory(*Ptanim\n_frame*SizeOf(s_frame))
         
            For frm=0 To *Ptanim\n_frame-1
                
                *Ptframe=*Ptanim\frame + frm * SizeOf(s_frame)

                *Ptframe\x=ReadLong()
                *Ptframe\y=ReadLong()
                
            Next frm
        Next anim
    Next i
    
    CloseFile(0)

EndProcedure


[modifier] Affichage des sprites

Besoin de réviser?

Besoin de réviser?


L'affichage des sprites se décompose en deux fonctions:

  • HandleSprites qui gère tous les sprites.
  • HandleSprite qui gère un sprite en particulier.


La première étape dans la gestion de tous les sprites est de recentrer la carte sur le héros afin de toujours le voir quelque soit ses déplacements. Le problème est que si le héro se déplace près des bords de la carte, on ne peut pas le centrer totalement sans sortir de la carte, il faut donc vérifier que le décalage de la carte reste acceptable.

Il ne reste plus qu'à appeller HandleSprite pour chaque sprite.

Procedure HandleSprites(*libsprite.s_lib_sprite,*map.s_map)
    
    Protected i,*Ptsprite.s_sprite
    
    ; centre la vue sur le joueur 
    *Ptsprite=*libsprite\sprites
    If(*Ptsprite\x<(800/2))
        *map\offsetX=0     
    Else
        
        *map\offsetX=*Ptsprite\x-(800/2)
        If *map\offsetX>*map\width*#TILE_WIDTH-800 
            *map\offsetX=*map\width*#TILE_WIDTH-800
        EndIf 
        
    EndIf
    
    If *Ptsprite\y<(600/2)
        *map\offsetY=0     
    Else
        
        *map\offsetY=*Ptsprite\y-(600/2)
        If *map\offsetY>*map\height*#TILE_HEIGHT-600 
            *map\offsetY=*map\height*#TILE_HEIGHT-600
        EndIf
        
    EndIf    
    
    For i=0 To *libsprite\n_sprite-1
        
        *Ptsprite=*libsprite\sprites + i * SizeOf(s_sprite)
        
        HandleSprite(*Ptsprite, *map)
        
    Next i
    
EndProcedure


La fonction HandleSprite commence par éventuellement mettre à jour l'animation en avançant d'une frame si:

  • l'animation est en train d'être jouée (ispaused_anim<tt> à 0)
  • le temps durant lequel la frame doit être jouée est dépassée <tt>ElapsedMilliseconds()-lastfrmupdate > delaybetweenframes)

Si une mise à jour est nécessaire, on avance d'une frame (en utilisant un modulo pour faire revenir à zéro le compteur après la dernière frame) et on mets à jour le timer du sprite.

Ensuite, on passe au code qui permet d'afficher à l'écran le sprite et qui est très ressemblant à ce que l'on a pu voir dans la gestion de la carte.

On effectue enfin les vérifications pour redimensionner le rectangle si la surface à copier est partiellement en dehors de l'écran.

Procedure HandleSprite(*sprite.s_sprite, *map.s_map)
    
    Protected *Ptsprite.s_sprite,*Ptanim.s_anim,*Ptframe.s_frame,src.rectangle, dest.rectangle
    
    *Ptanim=*sprite\anim + *sprite\cur_anim * SizeOf(s_anim) 
    
    If *sprite\ispaused_anim=0
        
        If ElapsedMilliseconds()-*Ptanim\lastfrmupdate > *Ptanim\delaybetweenframes
            
            *Ptanim\cur_frame = (*Ptanim\cur_frame+1)%*Ptanim\n_frame
            *Ptanim\lastfrmupdate=ElapsedMilliseconds()
            
        EndIf
     
    EndIf   
    
    *Ptframe=*Ptanim\frame + *Ptanim\cur_frame * SizeOf(s_frame)
    
    src\x = *Ptframe\x
    src\y = *Ptframe\y
    src\w = *sprite\width
    src\h = *sprite\height
    
    dest\x = *sprite\x-*map\offsetX
    dest\y = *sprite\y-*map\offsetY
    dest\w = *sprite\width
    dest\h = *sprite\height
    
    ; teste si le sprite est à l'écran... si ce n'est pas le cas, inutile de continuer! 
    If dest\x<-dest\w : ProcedureReturn : EndIf
    If dest\y<-dest\h : ProcedureReturn : EndIf
    
    If dest\x<0
        src\x -dest\x
        src\w + dest\x
        dest\w + dest\x
        dest\x=0;
    EndIf  
    
    If dest\y<0
        src\y - dest\y
        src\h + dest\y
        dest\h + dest\h
        dest\y=0
    EndIf
        
    ClipSprite(*sprite\surf\Id, src\x, src\y,src\w, src\h)
    
    DisplayTransparentSprite(*sprite\surf\Id,dest\x,dest\y)
    
EndProcedure


[modifier] Changer l'animation d'un sprite

Il est intéressant de créer une petite fonction qui remet à jour le sprite à chaque changement d'animation. Cette fonction met à jour:

  • l'animation en cours
  • la valeur du booléen testant si l'animation est en train d'être jouée
  • met à 0 le numéro de la frame actuelle
  • met à jour le timer
Procedure SpriteChangeAnim(*sprite.s_sprite,anim.l,ispaused.b)
    Protected *Ptanim.s_anim
    *sprite\cur_anim=anim
    *sprite\ispaused_anim=ispaused
    *Ptanim=*sprite\anim + *sprite\cur_anim * SizeOf(s_anim)   
    *Ptanim\cur_frame=0
    *Ptanim\lastfrmupdate=ElapsedMilliseconds()
EndProcedure


[modifier] Déplacement d'un sprite et gestion des collisions

Besoin de réviser?

Besoin de réviser?

La fonction qui permet de déplacer le sprite intègre la détection de collision détaillée plus haut. On va calculer sur quel tile est situé chaque coin du sprite (coin haut gauche, haut droit, bas gauche, bas droit). Ces coins ne sont pas les coins réels dans la mesure où ils prennent en compte les marges définies pour chaque côté.

On teste si chacun des tiles sur lequel est positionné le sprite est traversable ou pas, s'ils sont tous non-traversables alors on effectue le déplacement en vérifiant tout de même qu'il ne sort pas de la carte.

Procedure SpriteMove(*sprite.s_sprite, x.l, y.l, *map.s_map)
    
    Protected hgx,hgy, hdx,hdy, bgx,bgy, bdx,bdy
    
    ; calcul du tile sur lequel est situé chaque bord du sprite 
    hgx = (x+*sprite\offsetG)/#TILE_WIDTH
    hgy = (y+*sprite\offsetH)/#TILE_HEIGHT
    
    hdx = (x+#TILE_WIDTH-*sprite\offsetD)/#TILE_WIDTH
    hdy = (y+*sprite\offsetH)/#TILE_HEIGHT
    
    bgx = (x+*sprite\offsetG)/#TILE_WIDTH
    bgy = (y+#TILE_HEIGHT-*sprite\offsetB)/#TILE_HEIGHT
    
    bdx = (x+#TILE_WIDTH-*sprite\offsetD)/#TILE_WIDTH
    bdy = (y+#TILE_HEIGHT-*sprite\offsetB)/#TILE_HEIGHT
    
    T1=DataMap(GET_TILE(hgx,hgy,*map))   
    T2=DataMap(GET_TILE(hdx,hdy,*map)) 
    T3=DataMap(GET_TILE(bgx,bgy,*map)) 
    T4=DataMap(GET_TILE(bdx,bdy,*map)) 
    
    If chipset(T1)\Collision=0 And chipset(T2)\Collision=0 And chipset(T3)\Collision=0 And chipset(T4)\Collision=0
        
        If x>*map\width*#TILE_WIDTH-*sprite\width 
            x=*map\width*#TILE_WIDTH-*sprite\width
        EndIf
        
        If y>*map\height*#TILE_HEIGHT-*sprite\height
            y=*map\height*#TILE_HEIGHT-*sprite\height
        EndIf
        
        *sprite\x=x
        *sprite\y=y
        
    EndIf
EndProcedure


[modifier] Gestion des événements


La gestion des événement consiste à déplacer le sprite et à changer son animation selon la touche appuyée.

Procedure.l SpriteEvent(*map.s_map,*libsprite.s_lib_sprite)
    
    Protected *Ptsprite.s_sprite
    
    *Ptsprite=*libsprite\sprites
    
    *Ptsprite\ispaused_anim=1
    
    If ExamineKeyboard()  
        
        If KeyboardPushed(#PB_Key_Escape)
            
            ProcedureReturn 1
            
        EndIf    
        
        If KeyboardPushed(#PB_Key_Right)
            
            SpriteMove(*Ptsprite, *Ptsprite\x+1, *Ptsprite\y, *map)
            If *Ptsprite\cur_anim<>#ANIMATION_DROITE
                SpriteChangeAnim(*Ptsprite, #ANIMATION_DROITE, 0)
            Else
                *Ptsprite\ispaused_anim=0
            EndIf
            
        ElseIf KeyboardPushed(#PB_Key_Left)
            
            If *Ptsprite\x>0
                SpriteMove(*Ptsprite, *Ptsprite\x-1, *Ptsprite\y, *map)
            EndIf    
            If *Ptsprite\cur_anim<>#ANIMATION_GAUCHE
                SpriteChangeAnim(*Ptsprite, #ANIMATION_GAUCHE, 0)
            Else
                *Ptsprite\ispaused_anim=0
            EndIf
            
        ElseIf KeyboardPushed(#PB_Key_Down)
            
            SpriteMove(*Ptsprite, *Ptsprite\x, *Ptsprite\y+1, *map)
            If *Ptsprite\cur_anim<>#ANIMATION_BAS
                SpriteChangeAnim(*Ptsprite, #ANIMATION_BAS, 0)
            Else
                *Ptsprite\ispaused_anim=0
            EndIf
            
        ElseIf KeyboardPushed(#PB_Key_Up)
            
            If*Ptsprite\y>0
                SpriteMove(*Ptsprite, *Ptsprite\x, *Ptsprite\y-1, *map)
            EndIf    
            If *Ptsprite\cur_anim<>#ANIMATION_HAUT
                SpriteChangeAnim(*Ptsprite, #ANIMATION_HAUT, 0)
            Else
                *Ptsprite\ispaused_anim=0
            EndIf
            
        EndIf
        
    EndIf
    
EndProcedure

Bien évidemment, n'oubliez pas de supprimer le comportement de la démo précédente en supprimant le contenu de MapEvent() (mais gardons la fonction, elle nous reservira peut-être plus tard).

Procedure MapEvent(*map.s_map)
    
EndProcedure   


[modifier] Modification de la fonction main()

La fonction main() n'est pas réellement changée, il faut simplement penser à:

  • initialiser les sprites (InitSprite())
  • charger les sprites (LoadSprite())
  • à gérer les événements associés au sprite en appellant régulièrement SpriteEvent()
  • à mettre à jour les sprites régulièrement (HandleSprites())


Procedure Main()
    
    map.s_map
    libsprite.s_lib_sprite
    
    LoadMap(@map, "map\01.map")
    
    InitSprites(@libsprite)
    
    SpriteStaticData(@libsprite)
    
    SaveSprites(@libsprite, "sprites\player.bmp", "map\save.sprite")
    
    ;LoadSprites(@libsprite,"map\save.sprite")
    
    Repeat
     
        FlipBuffers()
        ClearScreen(0,0,0)
        MapEvent(@map)
        quit=SpriteEvent(@map, @libsprite)
        HandleMap(@map)
        HandleSprites(@libsprite, @map)
        
    Until quit
    
    FreeMap(@map)
    FreeSprites(@libsprite)
    
EndProcedure

[modifier] Démo 3: Sprites et collision

Démo 3: sprites et collision

Cette nouvelle démo est dotée d'une carte plus sympathique réalisée avec l'éditeur de carte construit durant l'étape précédente.