Un serpent qui ne se compresse pas

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
THSK
Messages : 3
Inscription : sam. 15/sept./2007 12:07

Un serpent qui ne se compresse pas

Message par THSK »

Bonjour à tous, je débute dans la programmation PureBasic, et après avoir lu les tutos de base, je me suis lancé dans un petit exercice d'application des connaissances :P

J'essaye donc de faire un snake où le déplacement se fait dans toutes les directions (entendez tous les angles, contrairement au snake classique) et où le serpent suit le curseur de la souris. Jusque là ça marche pas trop mal, c'est plutôt fluide tant que la longueur du serpent ne dépasse pas les 15 sprites.

Mais petit problème à résoudre : comme dans ma boucle, la chaîne qui enregistre les positions des sprites qui composent mon serpent, s'actualise en permanence, mon serpent se compresse en un point, constitué de tous les sprites superposés. Je voudrais donc que le serpent garde sa longueur, même "à l'arret".

J'ai essayé :
-actualiser la chaine uniquement si dx²+dy² > valeur : mon serpent se rétrécit moins mais il n'arrive pas jusqu'au curseur
-actualiser la chaine uniquement si distance "premier élément" - "dernier élément" > valeur : mon serpent se comporte comme s'il avait avalé des barres de fer, il tourne avec des angles carrés et c'est très moche)

Code : Tout sélectionner

#Police=1
InitKeyboard () : ; charge environnements
InitMouse()

Resultat = InitSprite ()
FontID = LoadFont ( #Police , "arial" , 18, #PB_Font_Bold ) ;police chargée
EcranX = GetSystemMetrics_ ( #SM_CXSCREEN ): ;=largeur de l'ecran
EcranY = GetSystemMetrics_ ( #SM_CYSCREEN ): ;=hauteur de l'ecran

;****************************OUVERTURE ECRAN ********************************
WindowID = OpenWindow (1, 0, 0,EcranX, EcranY, "Abysse" , #PB_Window_SystemMenu|#PB_Window_BorderLess |#PB_Window_ScreenCentered )
Result = OpenWindowedScreen ( WindowID (1),0,0, 800,600, 1, 0,0)

x_sprite=EcranX/2 ; on centre les coordonées du sprite
y_sprite=EcranY/2 ; on centre les coordonées du sprite

dx.f = 0
dy.f = 0

;***************ici entrer longueur serpent-***********
longserpent.b=10
NewList bodyx.l()
NewList bodyy.l()
For partbody = 0 To longserpent
AddElement(bodyx())
bodyx()=x_sprite
AddElement(bodyy())
bodyy()=y_sprite
Next

longueur.b=CountList(bodyx())

Resultat = ReleaseMouse(0) ;bloque sours




;DEBUT BOUCLE*******
Repeat
Event= WindowEvent ()


;**********************SUIT LA SOURIS***********************
ExamineMouse()
dx=x_sprite-MouseX()
dy=y_sprite-MouseY()

; ******* creation sprite ************
For partbody=1 To longueur
If CreateSprite (partbody,32,32) ; on creer un sprite vide (une cellule) de 32 par 32 et on lui donne le numero 1
TransparentSpriteColor(partbody,black)
StartDrawing ( SpriteOutput (partbody) ) ; on va dessiner dedans !
Circle (16,16,8, RGB (150,150,255)) ; on dessine un cercle
StopDrawing ()
EndIf
Next

If CreateSprite (cursor,32,32) ; on creer un sprite vide (une cellule) de 32 par 32 et on le nomme cursor
TransparentSpriteColor(cursor,black)
StartDrawing ( SpriteOutput (cursor) ) ; on va dessiner dedans !
Circle (16,16,4, RGB (255,0,0)) ; on dessine un cercle rouge pour reperer la souris
StopDrawing ()
EndIf

  For partbody=1 To longueur
   SelectElement(bodyx(), partbody) ; Sélectionne un élément
  *elementx1 = @bodyx()
  SelectElement(bodyx(), partbody-1) ; Sélectionne l'élément précédant ce dernier
  *elementx2 = @bodyx()  
  ; Echange les éléments
  LastElement(bodyx())
 If Abs(x_sprite-bodyx())>10 ; évite compression serpent
  SwapElements(bodyx(), *elementx1, *elementx2)
  EndIf

     SelectElement(bodyy(), partbody) ; Sélectionne un élément
  *elementy1 = @bodyy()
  SelectElement(bodyy(), partbody-1) ; Sélectionne l'élément précédant ce dernier
  *elementy2 = @bodyy()  
  ; Echange les éléments
   LastElement(bodyy())
   If Abs(y_sprite-bodyy())>10 ; évite compression serpent
  SwapElements(bodyy(), *elementy1, *elementy2)
    EndIf
  Next
  
  FirstElement(bodyx())
  bodyx() = x_sprite ; actualise la tete
  FirstElement(bodyy())
  bodyy() = y_sprite ; pour le premier element de la liste et efface le dernier element

  If dy<0
  y_sprite= y_sprite-0.03*(dy)
  Else
  y_sprite= y_sprite-0.03*(dy)
  EndIf

  If dx<0
  x_sprite = x_sprite-0.03*(dx)
  Else
  x_sprite = x_sprite-0.03*(dx)
  EndIf

  For partbody=1 To longueur
  SelectElement(bodyx(), partbody)
  SelectElement(bodyy(), partbody)
DisplayTransparentSprite (partbody, bodyx(), bodyy()) ; affiche le sprite au coordonées 
  Next

DisplayTransparentSprite (cursor, MouseX(), MouseY()) ; affiche le curseur

FlipBuffers (): ; affiche l'ecran
ClearScreen ( RGB (0, 0, 0)) : ;efface l'ecran
ExamineKeyboard()

Until Event= #PB_Event_CloseWindow Or KeyboardPushed ( #PB_Key_Escape ) ; press echap
Là je n'ai plus d'idées, mais je suppose que tout débutant a fait un snake et donc que quelqu'un doit bien avoir la solution.

Au passage, si vous avez des astuces pour optimiser mon code, en particulier pour l'enregitrement du chemin parcouru par le serpent, je suis preneur !

Merci d'avance, THSK.
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Mh?
8O
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Tiens, je t'ai fait ça vite fait pour que tu pige le procédé à établir.

Code : Tout sélectionner

; ------------------------------------------------------------
;
;   PureBasic - Sprite example file
;
;    (c) 2001 - Fantaisie Software
;
; ------------------------------------------------------------
;

    If InitSprite() = 0 Or InitKeyboard() = 0
        MessageRequester("Error", "Can't open DirectX 7 or later", 0)
        End
    EndIf

    If OpenScreen(1024, 768, 32, "Sprite")

    CreateSprite(0, 16, 16)
  
    If StartDrawing(SpriteOutput(0))
        ;Box(0, 0, 16, 16, #White)
        Circle(8, 8, 8, #White)
    StopDrawing()
  EndIf

Structure SERP
    X.L
    Y.L
    A.F
    R.L
EndStructure
  
  NewList Serp.SERP()
  For i = 0 To 31
      AddElement(Serp() )
      Serp()\X = i << 4
      Serp()\Y = 3 << 7
      Serp()\A = 0.0
      Serp()\R = 1 << 4
  Next

  Queue.L = 0
  Tete.L = 31
  A.F = 0
  Repeat
    
    
    FlipBuffers()
    
    ClearScreen(RGB(0,0,0))
    
    ForEach(Serp() )
        DisplaySprite(0, Serp()\X, Serp()\Y)
    Next
    SelectElement(Serp(), Tete)
    X = Serp()\X
    Y = Serp()\Y    
    R = Serp()\R
    SelectElement(Serp(), Queue)
    Serp()\X = X + Cos(A) * R
    Serp()\Y = Y - Sin(A) * R
    If ElapsedMilliseconds() > Delai
        Delai = ElapsedMilliseconds() + 100
        Sature = 0
        Tete = Queue
        Queue + 1
        If Queue > 31: Queue = 0: EndIf    
    EndIf
    ExamineKeyboard()
    If KeyboardPushed(#PB_Key_Left)
        If Sature = 0
            A + (#PI / 8)
            Sature = 1
        EndIf
    EndIf
    If KeyboardPushed(#PB_Key_Right)          
        If Sature = 0
            A - (#PI / 8)
            Sature = 1
        EndIf
    EndIf
  Until KeyboardPushed(#PB_Key_Escape)
  
EndIf

End   
Avatar de l’utilisateur
Huitbit
Messages : 940
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Message par Huitbit »

@THSK
Bon courage!
:wink:

@Ollivier
whaou! Bien vu!
J'avais pas pensé à ça quand j'ai fait mon snake avec liste chaînée!
SelectElement()
Moi, je rajoutais d'un coté et je supprimais de l'autre!

Hasta la vista!
Mytic
Messages : 466
Inscription : mer. 25/juil./2007 0:13

Message par Mytic »

Et si tu veux donner à ton serpent l’effet vibratoire :lol:

Code : Tout sélectionner

InitSprite()
InitKeyboard()
OpenScreen(1024,768,16,"teste")
CreateSprite(0,20,20)

StartDrawing(SpriteOutput(0))
DrawingMode(#PB_2DDrawing_Outlined)
For i = 1 To 10
Circle(10,10,i,4000/i)
Next i
StopDrawing()
max = 30
Structure info
x.d
y.d
ax.d
ay.d
mx.d
my.d
EndStructure

Global Dim mor.info(999)
angle.d
ax.d = 0
ay.d = 0
sx.D = 50
sy.D = 300
mov.D = 2
For i = 0 To max
sx = sx + 20
mor(i)\x = sx
mor(i)\y = sy
mor(i)\ax = Sin(30)*mov
mor(i)\ay = Cos(30)*mov
DisplaySprite(0,sx,sy)
Next i

Repeat
ClearScreen(0)
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape):End:EndIf
If KeyboardPushed(#PB_Key_Left)
angle = angle - 0.2
ax = Sin(angle)*mov
ay = Cos(angle)*mov
EndIf
If KeyboardPushed(#PB_Key_Right)
angle = angle + 0.2
ax = Sin(angle)*mov
ay = Cos(angle)*mov
EndIf

For i = 0 To max
angle = angle - 0.2
If i = max
mor(i)\ax = ax
mor(i)\ay = ay
StartDrawing(ScreenOutput())
LineXY(mor(i)\x,mor(i)\y,mor(i)\x+ax*10,mor(i)\y+ay*10,255)
StopDrawing()
mor(i)\x = mor(i)\x + ax
mor(i)\y = mor(i)\y + ay
Else
mor(i)\ax = mor(i+1)\ax
mor(i)\ay = mor(i+1)\ay
mor(i)\x = mor(i)\x + mor(i+1)\ax
mor(i)\y = mor(i)\y + mor(i+1)\ay
EndIf
DisplaySprite(0,mor(i)\x,mor(i)\y)
Next i

Delay(10)
FlipBuffers()
ForEver


THSK
Messages : 3
Inscription : sam. 15/sept./2007 12:07

Message par THSK »

Merci pour tout, je vais regarder ça de plus près. Si j'ai bien compris, les opérations se font uniquement sur l'élément tête et la queue, et on définit une valeur angle.
THSK
Messages : 3
Inscription : sam. 15/sept./2007 12:07

Message par THSK »

Bon en fait même en repartant du code d'Olivier, une fois qu'on a remis le suivi du curseur, le problème du serpent qui se compresse nest pas résolu :P Mais cette fois j'ai trouvé moi-même comment résoudre le problème.

Je mets la boucle avec le problème résolu, ça peut toujours servir :

Code : Tout sélectionner

 Repeat
   
   
    FlipBuffers()
   
    ClearScreen(RGB(0,0,0))
   
    ForEach(Serp() )
        DisplayTransparentSprite(1, Serp()\X, Serp()\Y)
    Next
    DisplayTransparentSprite (cursor, MouseX(), MouseY()) ; affiche le curseur 
    SelectElement(Serp(), Tete)
    X = Serp()\X
    Y = Serp()\Y   
    R = Serp()\R
    dx=MouseX()-X
    dy=Y-MouseY()
    SelectElement(Serp(), Queue)
    Serp()\X = X + Cos(A) * R
    Serp()\Y = Y - Sin(A) * R
    If ElapsedMilliseconds() > Delai And (Pow(dx,2)+Pow(dy,2))>16*R
        Delai = ElapsedMilliseconds() + 50
        Sature = 0
        Tete = Queue
        Queue + 1
        If Queue > 31: Queue = 0: EndIf   
    EndIf
    ExamineKeyboard()
    ExamineMouse()
    A = ATan2f(dy,dx)
    
  Until KeyboardPushed(#PB_Key_Escape)
Je reprends donc l'idée du calcul (dx²+dy²) pour autoriser le déplacement du serpent seulement quand cette distance est supérieure à telle valeur. Et donc le serpent stoppe quand il est à une certaine distance du curseur.

Et je mets en avant cette astuce :

Code : Tout sélectionner

Procedure.f atan2f(y.f, x.f)
  !fld dword[p.v_y]
  !fld dword[p.v_x]
  !fpatan
  ProcedureReturn
EndProcedure 
J'y comprends rien, c'est du copier-coller du forum, mais ça marche d'enfer ;)
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

!1 et !2 : ça charge les 2 valeur dans ton coprocesseur
!3 fais le calcul
Répondre