PureBasic     Discussion     Modifier     Historique     Forums     Salon IRC

PureBasic:Realiser un RPG2D/L'éditeur de carte

Un article de Games Creators Network.


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 (cf. étape 3)
  • Gestion du clavier et de la souris


Notre éditeur de carte.
Notre éditeur de carte.

Le moment est venu de nous intéresser à une tâche annexe du développement d'un RPG, la création d'un éditeur de cartes.

De nombreux outils très performants existent, le but n'est pas ici de les concurrencer mais de réutiliser ce qui est déjà fait afin de pouvoir concevoir les cartes que nous souhaitons le plus rapidement possible.

Ainsi, nous allons repartir du code de la démo 1 pour développer notre éditeur de cartes.

Le but est d'arriver au résultat que l'on peut voir à droite:

  • à gauche la liste des tuiles que l'on peut utiliser
  • à droite la carte sur laquelle on travaille

Le travail est donc divisé en deux parties:

  1. la gestion de la liste des tuiles à gauche (avec la gestion du défilement). Ceci concerne HandleMap() principalement.
  2. la gestion des clics et des actions spéciales que l'on peut effectuer sur la carte. Ces modifications affecteront MapEvent().


Sommaire

[modifier] Scission du projet

Créez un nouveau répertoire pour votre éditeur de carte et collez le contenu de la démo 1 à l'intérieur.


[modifier] Changements dans le type struct t_map

Structure s_map
    n_tile.l             ; nombre de tiles dont est composé la carte 
    surf_chipset.Surface ; Sprite contenant le chipset
    width.l              ; largeur de la carte
    Height.l             ; hauteur de la carte
    offsetX.l            ; décalage sur l'axe X lors de l'affichage de la carte 
    offsetY.l            ; décalage sur l'axe Y lors de l'affichage de la carte 
    bg_sound.l           ; Identifiant de la musique de fond jouée pendant cette carte 
    chipsetOffsetY.l     ; NOUVEAU    
    tile_sel.l           ; NOUVEAU 
EndStructure
  • chipsetOffsetY va gérer le scrolling des tiles.
  • tile_sel contient le numéro du tile sélectionné.

Profitons-en pour définir une taille pour la bordure entre la carte et la listes des tuiles (à insérer dans map.pbi):

#BORDER_SIZE = 20

Et aussi pour ajouter une nouvelle structure qui sera utilisée par la fonction HandleMap() (à insérer dans map.pbi):

Structure Rectangle
    x.l
    y.l
    w.l
    h.l
EndStructure   


[modifier] Changements dans InitMap()

Comme nous avons rajouté deux nouveaux éléments, il ne faut pas oublier des les initialiser!

Procedure InitMap(*map.s_map)
    *map\n_tile=0
    *map\width=0
    *map\Height=0
    *map\offsetX=0
    *map\offsetY=0
    *map\chipsetOffsetY=0
    *map\tile_sel=0
    *map\surf_chipset\Id=0
    *map\bg_sound=0
EndProcedure


[modifier] Changements dans RandomizeMap()

RandomizeMap va simplement servir à initialiser les cartes lors de leur création. Pour simplifier l'utilisation de l'éditeur, nous changeons simplement le code afin de rendre les tiles non-traversables par défaut.

Procedure RandomizeMap(*map.s_map, width.l, Height.l, NomChipset.s)
    Protected i,x,y
    
    InitMap(*map)
    
    ;Charge le chipset et mémorise son identifiant
    *map\surf_chipset\Id=LoadSprite(#PB_Any,NomChipset)
    
    If *map\surf_chipset\Id=0
        MessageRequester("Erreur","Impossible de charger " + NomChipset,0)
        End
    EndIf    
    
    ;Change la couleur transparente du sprite
    TransparentSpriteColor(*map\surf_chipset\Id, 8, 33, 82)
    
    ;Stock les dimensions du sprite
    *map\surf_chipset\w=SpriteWidth(*map\surf_chipset\Id)
    *map\surf_chipset\h=SpriteHeight(*map\surf_chipset\Id)
    
    ;Calcule le nombre de tiles disponibles sur le chipset
    *map\n_tile=(*map\surf_chipset\w/#TILE_WIDTH)*(*map\surf_chipset\h/#TILE_HEIGHT)
    
    ;Tableau contenant les caractéristiques de chaque tile du chipset
    Dim chipset.s_chipset(*map\n_tile)
    
    x=0
    y=0
    
    For i=0 To *map\n_tile-1
        chipset(i)\x=x
        chipset(i)\y=y
        chipset(i)\Collision=1 ;Le seul changement 
        chipset(i)\nextTile=0
        x + #TILE_WIDTH
        If x>*map\surf_chipset\w-#TILE_WIDTH
            x=0
            y + #TILE_HEIGHT
        EndIf
    Next i
    
    *map\width = width
    *map\Height = Height
    
    ;Tableau contenant les données de la carte
    Dim DataMap.l(*map\width * *map\Height)
    
    
    For x=0 To *map\width-1
        For y=0 To *map\Height-1
            DataMap(GET_TILE(x,y,*map))=Random(*map\n_tile-1)
        Next y
    Next x
    
EndProcedure


[modifier] Changements dans HandleMap()

Besoin de réviser?

Besoin de réviser?


Les gros changements sont réalisés dans cette partie: tout d'abord nous avons un paramètre supplémentaire left qui indique à partir d'où la carte va être affichée. En effet, nous avons besoin d'espace à gauche pour afficher la liste des tuiles.

Procedure HandleMap(*map.s_map, left.l)
    
    Protected x,y,tx,ty,src.Rectangle,dest.Rectangle 
    
    x=-(*map\offsetX%#TILE_WIDTH)+left ; Changement
    y=-(*map\offsetY%#TILE_HEIGHT)
    tx=*map\offsetX/#TILE_WIDTH
    ty=*map\offsetY/#TILE_HEIGHT
    
    While(y<600)
        
        x=-(*map\offsetX%#TILE_WIDTH)+left ; Changement
        tx=*map\offsetX/#TILE_WIDTH
        
        While(x<800)
            
            src\x=chipset(DataMap(GET_TILE(tx,ty,*map)))\x
            src\y=chipset(DataMap(GET_TILE(tx,ty,*map)))\y
            dest\x=x
            dest\y=y
            src\w=#TILE_WIDTH
            src\h=#TILE_HEIGHT
            dest\w=#TILE_WIDTH
            dest\h=#TILE_HEIGHT
            
            If dest\x<left 
                ; CHANGEMENT 
                src\x -(dest\x-left) 
                src\w + (dest\x-left)
                dest\w + (dest\x-left)
                dest\x=left
            EndIf
            If dest\y<0
                src\y - dest\y
                src\h + dest\y
                dest\h + dest\h
                dest\y=0
            EndIf 
            ClipSprite(*map\surf_chipset\Id, src\x, src\y,src\w, src\h)
            
            DisplayTransparentSprite(*map\surf_chipset\Id,dest\x,dest\y)
            
            
            x+ #TILE_WIDTH;
            tx + 1;
        Wend
        y + #TILE_HEIGHT;
        ty + 1
    Wend
    
    ; AFFICHAGE DU CHIPSET - NOUVEAU 
    
    x=1
    y=1
    
    For i=*map\chipsetOffsetY To *map\n_tile-1
        
        src\x=chipset(i)\x
        src\y=chipset(i)\y
        dest\x=x
        dest\y=y
        src\w=#TILE_WIDTH
        src\h=#TILE_HEIGHT
        dest\w=#TILE_WIDTH
        dest\h=#TILE_HEIGHT
        
        If i=*map\tile_sel
            
            dest\x - 1
            dest\y - 1
            dest\w + 2
            dest\h + 2
            
            StartDrawing(ScreenOutput())
            DrawingMode(4)
            If chipset(i)\Collision=0
                Box(dest\x,dest\y,dest\w,dest\h,RGB(0,255,0))
            Else
                Box(dest\x,dest\y,dest\w,dest\h,RGB(255,0,0)) 
            EndIf
            StopDrawing()
            
            dest\x + 1
            dest\y + 1
            dest\w - 2
            dest\h - 2
        EndIf
        
        ClipSprite(*map\surf_chipset\Id, src\x, src\y,src\w, src\h)
        
        DisplayTransparentSprite(*map\surf_chipset\Id,dest\x,dest\y)
        
        dest\x + (#TILE_WIDTH-2)
        dest\y + (#TILE_HEIGHT-2)
        dest\w=2
        dest\h=2
        
        StartDrawing(ScreenOutput())
        If chipset(i)\Collision=0
            Box(dest\x,dest\y,dest\w,dest\h,RGB(0,255,0))
        Else
            Box(dest\x,dest\y,dest\w,dest\h,RGB(255,0,0)) 
        EndIf
        StopDrawing()
        
        x + #TILE_WIDTH + 1
        
        If x>(left-#TILE_WIDTH-#BORDER_SIZE)
            x=1
            y + #TILE_HEIGHT + 1
        EndIf
        
    Next i
    
EndProcedure

Les changements signalés au début du code sont mineurs et ne sont là que pour gérer le paramètre left mais la seconde partie de la fonction qui est totalement nouvelle sert à afficher les tuiles dans la partie gauche de la fenêtre.

De plus, on affiche 4 pixels de couleur, en bas à droite de chaque tuile, afin de spécifier si la tuile est traversable ou pas:

  • vert: traversable
  • rouge: non-traversable


[modifier] Changements dans HandleEvent()


La fonction qui gère les événements a également été très largement modifiée. Nous gérons en plus:

  • les clics gauche:
    • souris dans la zone des tuiles: on sélectionne une autre tuile
    • souris sur la carte: on change la tuile dans la carte
  • les clics droits:
    • souris dans la zone des tuiles: changement tuile traversable/non-traversable
  • le clavier:
    • f: remplit la carte avec la tuile sélectionnée
    • l: charge la carte
    • s: sauvegarde la carte
    • flèches directionnelles: fait défiler la carte (déjà implémenté dans l'étape 3)
    • PageUp et PageDown : défilement dans la zone tuile.

De plus, si on laisse CTRL enfoncé pendant que l'on change une tuile dans la carte, les 4 tuiles en haut, à droite de la souris seront modifiés. Si l'on laisse ALT enfoncé, ce sont les 9 tuiles tout autour de la tuile sélectionnée qui sont modifiés.

Procedure MapEvent(Event.l, *map.s_map, left.l)
    
    Protected i.l,j.l,ntileparcol.l
    
    Static BoutonGauche ; Mémoire touche gauche de la souris appuyée
    
    ; nombre de tile dans chaque colonne... dans la démo: 5 
    ntileparcol=(left-#BORDER_SIZE)/(#TILE_WIDTH+1)
    
    If ExamineKeyboard()
        
        ;Déplacement de la carte
        If KeyboardPushed(#PB_Key_Right)
            
            If *map\offsetX<*map\width*#TILE_WIDTH-800
                *map\offsetX + 1
            EndIf    
            
        ElseIf KeyboardPushed(#PB_Key_Left)
            
            If (*map\offsetX<>0)
                *map\offsetX - 1
            EndIf
            
        EndIf
        
        If KeyboardPushed(#PB_Key_Down)
            
            If *map\offsetY<*map\Height*#TILE_HEIGHT-600 
                *map\offsetY + 1
            EndIf
            
        ElseIf KeyboardPushed(#PB_Key_Up)
            
            If *map\offsetY<>0
                *map\offsetY - 1
            EndIf 
            
        EndIf
        
        ; Déplacement CHIPSET 
        If KeyboardReleased(#PB_Key_PageUp)
            
            If *map\chipsetOffsetY>=ntileparcol 
                *map\chipsetOffsetY - ntileparcol;
            EndIf
            
        ElseIf KeyboardReleased(#PB_Key_PageDown)
            
            If *map\chipsetOffsetY<*map\n_tile-ntileparcol
                *map\chipsetOffsetY + ntileparcol;
            EndIf 
            
            ; FONCTION SPECIALES 
        ElseIf KeyboardReleased(#PB_Key_F) ;  FILL 
            
            For i=0 To *map\width-1
                For j=0 To *map\Height-1
                    DataMap(GET_TILE(i,j,*map))=*map\tile_sel
                Next j
            Next i    
            
        ElseIf KeyboardReleased(#PB_Key_S)  ; SAVE 
            
            SaveMap(*map, "map\save.map", "musique\Opening1.mid", "tiles\mchip0.bmp");
            
        ElseIf KeyboardReleased(#PB_Key_L) ; LOAD 
            Taille=FileSize("map\save.map")
            
            If Taille>0
                
                FreeMap(*map);
                InitMap(*map);
                LoadMap(*map, "map\save.map")
                
            EndIf   
            
        EndIf
        
        If Event = #WM_LBUTTONUP
            BoutonGauche = 0
            
        ElseIf Event = #WM_LBUTTONDOWN Or BoutonGauche = 1 
            
            BoutonGauche = 1
            
            ;La souris se trouve dans la fenêtre
            If WindowMouseX()>-1 And WindowMouseX()>-1
                
                If WindowMouseX()<=left-#BORDER_SIZE 
                    
                    *map\tile_sel=*map\chipsetOffsetY+(WindowMouseX()/(#TILE_WIDTH+1))+ntileparcol*(WindowMouseY()/(#TILE_HEIGHT+1))
                    
                    If *map\tile_sel>=*map\n_tile
                        *map\tile_sel=*map\n_tile-1
                    EndIf
                    
                    
                ElseIf WindowMouseX()>=left
                    
                    i=*map\offsetX+WindowMouseX()-left
                    j=*map\offsetY+WindowMouseY()
                    i / #TILE_WIDTH
                    j / #TILE_HEIGHT
                    
                    If i>=*map\width
                        i=*map\width-1
                    EndIf    
                    
                    If j>=*map\Height
                        j=*map\Height-1
                    EndIf    
                    
                    DataMap(GET_TILE(i,j,*map))=*map\tile_sel
                    
                    ;  on appuie sur CTRL gauche ou ALT gauche
                    
                    ;Regroupe les deux touches CTRL 
                    ToucheControl=KeyboardPushed(#PB_Key_LeftControl) Or KeyboardPushed(#PB_Key_RightControl)
                    
                    ;Regroupe les deux touches ALT 
                    ToucheAlt=KeyboardPushed(#PB_Key_LeftAlt) Or KeyboardPushed(#PB_Key_RightAlt)
                    
                    If ToucheControl Or ToucheAlt
                        
                        If i<*map\width-1
                            DataMap(GET_TILE(i+1,j,*map))=*map\tile_sel
                        EndIf
                        
                        If j>0 
                            DataMap(GET_TILE(i,j-1,*map))=*map\tile_sel
                        EndIf    
                        If j>0 And i<*map\width-1
                            DataMap(GET_TILE(i+1,j-1,*map))=*map\tile_sel
                        EndIf    
                        
                        If ToucheAlt
                            
                            If i>0
                                DataMap(GET_TILE(i-1,j,*map))=*map\tile_sel
                            EndIf
                            
                            If j<*map\Height-1
                                DataMap(GET_TILE(i,j+1,*map))=*map\tile_sel
                            EndIf
                            
                            If i>0 And j<*map\Height-1
                                DataMap(GET_TILE(i-1,j+1,*map))=*map\tile_sel
                            EndIf
                            
                            If i>0 And j>0
                                DataMap(GET_TILE(i-1,j-1,*map))=*map\tile_sel
                            EndIf
                            
                            If j<*map\Height-1 And i<*map\width-1
                                DataMap(GET_TILE(i+1,j+1,*map))=*map\tile_sel
                            EndIf
                        EndIf
                    EndIf
                    
                EndIf     
            EndIf 
            
        ElseIf Event = #WM_RBUTTONDOWN
            
            If WindowMouseX()>-1 And WindowMouseX()>-1
                
                If WindowMouseX()<=left-#BORDER_SIZE  
                    
                    i=*map\chipsetOffsetY+(WindowMouseX()/(#TILE_WIDTH+1))+ntileparcol*(WindowMouseY()/(#TILE_HEIGHT+1))
                    
                    If i>=*map\n_tile
                        i=*map\n_tile-1
                    EndIf
                    
                    chipset(i)\Collision=1-chipset(i)\Collision
                    
                EndIf 
            EndIf     
            
        EndIf    
        
    EndIf
        
EndProcedure 
 

[modifier] Changements dans Main()

Très peu de changement dans cette partie, il faut simplement préciser le paramètre supplémentaire left. Ici nous le fixons à 185 (pour obtenir 5 colonnes).

Remarque : Le jeu est en mode plein écran , mais l'éditeur est en mode fenêtré , il sera plus simple par la suite d'ajouter un menu ou une StatusBar. Par contre ce changement implique la gestion des évènements reçus avec la fonction WindowEvent().

Procedure Main()
    
    map.s_map
    left.l=185 ;Décalage en X de la carte pour laisser la place au chipset sur la gauche

    RandomizeMap(@map,100,100,"tiles\mchip0.bmp");
    ;SaveMap(@map,"map\save.map", "musique\Opening1.mid", "tiles\mchip0.bmp");
    ;LoadMap(@map, "map\01.map")
    
    Repeat
        
        Event=WindowEvent()
        
        Select Event
            
            Case 0                     ;Libère la CPU si aucun évènement à traiter
                Delay(5)
                
            Case #PB_Event_CloseWindow ;Quitte le programme 
                
                Quit=1
                
        EndSelect
        
        FlipBuffers()
        
        ClearScreen(0,0,0)
        
        MapEvent(Event, @map, left)
        
        HandleMap(@map, left)
        
    Until Quit
    
    FreeMap(@map)
    
EndProcedure


[modifier] Ce que sait faire et ne sait pas faire notre éditeur de carte

Notre but est de faire un éditeur de carte utilisable à moindre frais. C'est ce qui a été réalisé mais au prix de quelques sacrifices, ainsi vous devrez recompiler l'éditeur pour changer:

  • la taille des cartes
  • le chipset
  • la musique de fond

Et l'éditeur de carte ne gère pas les tuiles animées.


[modifier] Démo 2: l'éditeur de cartes

Démo 2: l'éditeur de cartes
Démo 2: l'éditeur de cartes

 

Rechercher
Installer l'extension de recherche Plus d'informations

 

Comprendre
Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. - Benjamin Franklin

 

Partager
La connaissance est la seule chose qui s'accroit lorsqu'on la partage. - Sacha Boudjema

 

Créer
L'imagination est plus importante que la connaissance. - Albert Einstein

 

 

Le wiki en images Le wiki en images Image du mois: «Snowball: un prototype de jeu développé avec NeL.