Voilà le premier méchant que je vais mettre dans mon jeu (voir http://www.purebasic.fr/french/viewtopic.php?t=6950)
(sprite nécessaire pour le fonctionnement du code ci-dessous)
[EDIT] légère modification de corps_decalage_x et corps_decalage_y pour éviter des changements de cases trop brusques de la queue(ça évite également les problèmes de repérage dans le tableau 32*24). Maintenant, un maillon de 16*16 est confondu avec la tête.
Code : Tout sélectionner
;auteur Huitbit
;test dragon fer
;pb v4.02
;***************************************************
Enumeration
#spr_dragon_fer
#spr_ecran
#spr_souris
#spr_cible
EndEnumeration
Structure donnees_dragon
x.w
y.w
dx.b
dy.b
numero_decision.b
spr_colonne.w
spr_ligne.w
EndStructure
Structure decision_dragon
x.w
y.w
dx.b
dy.b
EndStructure
Dim carte.b(31, 23)
Dim dragon.donnees_dragon(40)
NewList decision.decision_dragon()
tete.donnees_dragon
;dragon initial
;tête
tete_dl=4
tete\x=320
tete\y=320
tete\dx=2
tete\dy=0
tete_spr_colonne=0
tete_spr_ligne=0
;corps
taille_dragon=10
For i=0 To taille_dragon
dragon(i)\x=tete\x-16*taille_dragon+16*i+corps_decalage_x
dragon(i)\y=320+corps_decalage_y
dragon(i)\dx=tete_dl
dragon(i)\dy=0
dragon(i)\spr_colonne=0
dragon(i)\spr_ligne=32
Next i
corps_decalage_x=-16
corps_decalage_y=8
x_cible=320
y_cible=320
;*********************************************************
InitSprite()
InitKeyboard()
InitMouse()
OpenWindow(0,0,0,1024,768,"Test_dragon, CLIQUER DU DROIT POUR QUITTER...CLIQUER DU GAUCHE POUR PLACER LA CASE D'ARRIVEE DU DRAGON ",#PB_Window_ScreenCentered|#PB_Window_SystemMenu )
OpenWindowedScreen(WindowID(0),0,0,1024,768,1,0,0)
UsePNGImageDecoder()
;planche d'images
LoadSprite(#spr_dragon_fer,"dragon_fer.png")
;-fond d'écran
;{
CreateSprite(#spr_ecran,1024,768)
StartDrawing(SpriteOutput(#spr_ecran));dessin d'un damier 32*32+grille16*16 pour les tests
Box(0,0,1024,768,RGB(255,255,255));fond blanc
For i=0 To 31
For j=0 To 23
If (i+j)%2=0
Box(i*32,j*32,32,32,RGB(128,128,128));cases grises
EndIf
Next j
Next i
For i=1 To 63
Line(i*16,0,0,768,RGB(0,0,255))
Next i
For i=1 To 47
Line(0,i*16,1024,0,RGB(0,0,255))
Next i
StopDrawing()
;}
CreateSprite(#spr_souris,32,32)
StartDrawing(SpriteOutput(#spr_souris))
Circle(16,16,16,RGB(255,255,0))
StopDrawing()
CreateSprite(#spr_cible,32,32)
StartDrawing(SpriteOutput(#spr_cible))
Box(0,0,32,32,RGB(255,0,255))
StopDrawing()
;********************************************************************
;BOUCLE PRINCIPALE*****************************************
;********************************************************************
Repeat
;gestion des évènements de la fenêtre
;{
Repeat
Event = WindowEvent()
If Event = #PB_Event_CloseWindow
End
EndIf
Until Event = 0
;}
ExamineMouse()
x_souris=32*Int(MouseX()/32)
y_souris=32*Int(MouseY()/32)
If MouseButton(#PB_MouseButton_Right )
End
EndIf
If MouseButton(#PB_MouseButton_Left)
carte( x_cible/32, y_cible/32)=0
x_cible=x_souris
y_cible=y_souris
carte( x_cible/32, y_cible/32)=#spr_cible
cible_atteinte=0
EndIf
;déplacement automatique vers la cible
If tete\x%32=0 And tete\y%32=0
;prise de décision pour aller à droite
;{
If tete\x<x_cible And tete\y=y_cible
If tete\dx<>tete_dl
tete\dx=tete_dl
tete\dy=0
tete_spr_colonne=0
tete_spr_ligne=0
corps_decalage_x=0
corps_decalage_y=8
AddElement(decision())
decision()\x=tete\x
decision()\y=tete\y
decision()\dx=tete\dx
decision()\dy=tete\dy
EndIf
EndIf
;}
;prise de décision pour aller à gauche
;{
If tete\x>x_cible And tete\y=y_cible
If tete\dx<>-tete_dl
tete\dx=-tete_dl
tete\dy=0
tete_spr_colonne=64
tete_spr_ligne=0
corps_decalage_x=16
corps_decalage_y=8
AddElement(decision())
decision()\x=tete\x
decision()\y=tete\y
decision()\dx=tete\dx
decision()\dy=tete\dy
EndIf
EndIf
;}
;prise de décision pour aller en bas
;{
If tete\y<y_cible
If tete\dy<>tete_dl
tete\dx=0
tete\dy=tete_dl
tete_spr_colonne=32
tete_spr_ligne=0
corps_decalage_x=8
corps_decalage_y=0
AddElement(decision())
decision()\x=tete\x
decision()\y=tete\y
decision()\dx=tete\dx
decision()\dy=tete\dy
EndIf
EndIf
;}
;prise de décision pour aller en haut
;{
If tete\y>y_cible
If tete\dy<>-tete_dl
tete\dx=0
tete\dy=-tete_dl
tete_spr_colonne=96
tete_spr_ligne=0
corps_decalage_x=8
corps_decalage_y=16
AddElement(decision())
decision()\x=tete\x
decision()\y=tete\y
decision()\dx=tete\dx
decision()\dy=tete\dy
EndIf
EndIf
;}
;si la cible est atteinte
;{
If tete\x=x_cible And tete\y=y_cible
cible_atteinte=1
tete\dx=0
tete\dy=0
EndIf
;}
EndIf
For i=0 To taille_dragon
ForEach decision()
If dragon(i)\x=decision()\x And dragon(i)\y=decision()\y And dragon(i)\numero_decision=ListIndex(decision())
dragon(i)\dx=decision()\dx
dragon(i)\dy=decision()\dy
dragon(i)\numero_decision=dragon(i)\numero_decision+1
EndIf
Next
Next i
;mise à jour du compteur de décision quand la queue est passée
If dragon(0)\numero_decision=1
FirstElement(decision())
DeleteElement(decision())
For i=0 To taille_dragon
dragon(i)\numero_decision=dragon(i)\numero_decision-1
Next i
EndIf
If ElapsedMilliseconds()-chrono>5
chrono=ElapsedMilliseconds()
tete\x=tete\x+tete\dx
tete\y=tete\y+tete\dy
If cible_atteinte=0
For i=0 To taille_dragon
dragon(i)\x=dragon(i)\x+dragon(i)\dx
dragon(i)\y=dragon(i)\y+dragon(i)\dy
Next i
EndIf
EndIf
;affichage des sprites
;{
DisplaySprite(#spr_ecran,0,0)
For i=0 To 31
For j=0 To 23
If carte(i,j)=#spr_cible
DisplaySprite(#spr_cible,i*32,j*32)
EndIf
Next j
Next i
For i=0 To taille_dragon
ClipSprite(#spr_dragon_fer,dragon(i)\spr_colonne,dragon(i)\spr_ligne,16,16)
DisplaySprite(#spr_dragon_fer,dragon(i)\x+corps_decalage_x,dragon(i)\y+corps_decalage_y)
Next i
ClipSprite(#spr_dragon_fer,tete_spr_colonne,tete_spr_ligne,32,32)
DisplaySprite(#spr_dragon_fer,tete\x,tete\y)
DisplaySprite(#spr_souris,x_souris,y_souris)
StartDrawing(ScreenOutput())
ForEach decision()
DrawText(decision()\x,decision()\y,Str(ListIndex(decision()))+";"+Str(decision()\dx)+","+Str(decision()\dy))
Next
StopDrawing()
;}
Delay(15)
;calcul du fps
;{
If Second < ElapsedMilliseconds()
Second = ElapsedMilliseconds()+1000
fps = Frame_Counter
Frame_Counter = 0
Else
Frame_Counter + 1
EndIf
StartDrawing(ScreenOutput())
DrawText(0,0,"FPS : "+Str(fps))
StopDrawing()
;}
FlipBuffers()
ForEver
; IDE Options = PureBasic v4.02 (Windows - x86)
; CursorPosition = 56
; FirstLine = 30
; Folding = -
Dans mon jeu, j'ai copié le style de déplacement du jeu MSX SUPER SNAKE des années 80 (c'est à dire, seuls deux des éléments du serpent sont animés), ici TOUS les éléments sont animés.
La "tête" indique (liste chaînée "décision") les changements de direction au reste du corps.
La GROSSE difficulté a été de gérer une tête de 32*32 avec un corps à éléments 16*16.
J'ai utilisé la liste chaînée "décision()" car au début, le serpent suivait une trace sur un tableau mais avec ce système, dès qu'il y avait un "croisement", les éléments perdaient une information et se superposaient!
Pour le dragon on peut prendre une liste chaînée à la place du tableau "dragon(i)".
J'ai appelé ce post SNAKES car j'aimerais bien regrouper différentes méthodes pour faire des snakes (si ça intéresse quelqu'un, je peux mettre la version basique du dragon vert de mon jeu).
Si vous en avez déjà fait ça serait bien de laissssser une trace sur ce poste

Chaque méthode a ses avantages

Hasta la vista!
PS: j'ai laissé le quadrillage et les infos pour que tout le monde puisse comprendre le fonctionnement :1;4,0 sur une case signifie (changement de direction n°1,dx=4,dy=0)
L'IA est un peu simpliste mais je vais pas sortir tout de suite le pathfinding

