PureBasic:Realiser un RPG2D/L'éditeur de carte
Un article de Games Creators Network.
PureBasic:Realiser_un_RPG2D
<< Précédent | Sommaire | Suivant >>
Difficultée estimée de cette étape: moyen
Pré-requis:
- Connaissance moyenne de PureBasic (cf. étape 3)
- Gestion du clavier et de la souris
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:
- la gestion de la liste des tuiles à gauche (avec la gestion du défilement). Ceci concerne HandleMap() principalement.
- la gestion des clics et des actions spéciales que l'on peut effectuer sur la carte. Ces modifications affecteront MapEvent().
[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()
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


