Trigo_squash (démo+ exemple de minijeu)

Programmation avancée de jeux en PureBasic
Avatar de l’utilisateur
Huitbit
Messages : 940
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Trigo_squash (démo+ exemple de minijeu)

Message par Huitbit »

En regardant le jeu squash, j’ai eu l’idée (éclair d'intelligence à la RantanplanImage) de remplacer les lignes droites par des courbes.
J'ai jamais vu ça, c'est peut-être une exclusivité mondiale :lol: :lol:

Le principe est simple :
Au point de collision, la courbe peut-être remplacée par sa tangente.
La tangente est reliée à la courbe par sa dérivée.
Tout se passe comme si la balle cognait sur une surface plane mais inclinée.
Le rebond correspond donc à une rotation d’angle égal au double de celui d’arrivée.
Les notions mathématiques abordées sont le produit scalaire, la rotation en 2D, les relations entre les fonctions trigonométriques, la dérivée, la tangente.

Dans l’exemple, je prends une courbe simple (un sinus) :
f(x)=amplitude*sin(pulsation*x)+valeur_moyenne
sa dérivée f’(x)=amplitude*pulsation*cos(pulsation*x)
Image


La raquette bouge toute seule (magie! :lol:) car je voulais juste présenter l'idée!

Pour la collision, j'utilise spritepixelcollision() quand la balle se rapproche de la courbe.
Au départ, je comparais les coordonnées de la balle à celles de la courbe mais en fonction de la vitesse de la balle, il y avait parfois des "ratés!".
Si on change les paramètres, il faut se débrouiller pour garder une courbe assez épaisse car la balle se déplace de plusieurs pixels à chaque boucle!

Rien n'est optimisé mais le nombre d'images par seconde(FPS) reste au maximum chez moi.

On peut prendre des courbes plus compliquées(voir à la fin du post), il suffit de calculer leurs dérivées à la main (sauf si on paramètre le tout bien sûr!)

Code : Tout sélectionner

;trigo_squash
;auteur Huitbit
;PureBasic 4.10
Enumeration
  #spr_courbe
  #spr_calque
  #spr_balle
  #spr_raquette
EndEnumeration
Structure donnees
  x.w
  y.w
  dx.f
  dy.f
  dl.f
EndStructure
balle.donnees
balle\x=300
balle\y=564
balle\dx=-4
balle\dy=-4
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
ancien_dx.f
ancien_dy.f
abscisse.w

cos_a.f
cos_2a.f
sin_2a.f
#amplitude=80
#pulsation=2*#PI/240
#valeur_moyenne=240
Dim fonction.f(800)
Dim derive.f(800)
Dim norme_vecteur_tangent.f(800)
For abscisse= 0 To 800
  fonction(abscisse)=#amplitude*Sin(#pulsation*abscisse)+#valeur_moyenne
  derive(abscisse)=#amplitude*#pulsation*Cos(#pulsation*abscisse)
  norme_vecteur_tangent(abscisse)=Sqr(1+derive(abscisse)*derive(abscisse))
Next abscisse
Macro calcul_cos_a
cos_a=(balle\dx+balle\dy*derive(balle\x+4))/(balle\dl*norme_vecteur_tangent(balle\x+4))
EndMacro
Macro calculs_pour_rotation
cos_2a=2*cos_a*cos_a-1
sin_2a=Sqr(1-cos_2a*cos_2a)
EndMacro
Macro rotation
ancien_dx=balle\dx
ancien_dy=balle\dy
If cos_a>0
  balle\dx=cos_2a*ancien_dx-sin_2a*ancien_dy
  balle\dy=sin_2a*ancien_dx+cos_2a*ancien_dy
Else
  balle\dx=cos_2a*ancien_dx+sin_2a*ancien_dy
  balle\dy=-sin_2a*ancien_dx+cos_2a*ancien_dy
EndIf
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
EndMacro

;***************************************************
;-PROGRAMME PRINCIPAL
InitSprite()
InitKeyboard()
OpenWindow(0,0,0,800,600,"trigo_squash")
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0)
;-courbe
CreateSprite(#spr_courbe,800,600)
StartDrawing(SpriteOutput(#spr_courbe))
old_x=0
old_y=fonction(0)
For abscisse=1 To 799
  For j=0 To 20
    LineXY(old_x,old_y-j,abscisse,fonction(abscisse)-j,RGB(255-10*j,0,0))
  Next j
  LineXY(old_x,old_y,abscisse,fonction(abscisse),RGB(255,0,0))
  
  old_x=abscisse
  old_y=fonction(abscisse)
Next abscisse 
StopDrawing()
;-calque
CreateSprite(#spr_calque,800,600)
StartDrawing(SpriteOutput(#spr_calque))
;fond étoilé
For  numero_etoile = 0 To 500
  Plot(1+Random(798),1+Random(567),RGB(255,255,255))
Next numero_etoile
StopDrawing()
;-balle
CreateSprite(#spr_balle,8,8)
StartDrawing(SpriteOutput(#spr_balle))
Circle(4,4,4,RGB(255,255,255))
StopDrawing()
;-raquette
CreateSprite(#spr_raquette,64,16)
StartDrawing(SpriteOutput(#spr_raquette))
Box(0,0,64,16,RGB(255,255,255))
StopDrawing()

;-BOUCLE PRINCIPALE
Repeat
  Repeat
    Event = WindowEvent()      
    If  Event = #PB_Event_CloseWindow
      End 
    EndIf
  Until Event = 0
  
  ;-test collision si la balle se rapproche de la courbe
  If balle\y+balle\dy<#amplitude+#valeur_moyenne+balle\dl
    If SpritePixelCollision(#spr_balle,balle\x+4,balle\y,#spr_courbe,0,0)
      calcul_cos_a
      calculs_pour_rotation
      rotation
    EndIf
  EndIf
  ;-tests bordure écran
  If balle\x+balle\dx>792 Or balle\x+balle\dx<1
    balle\dx=-balle\dx
  EndIf
  If balle\y+balle\dy>560 Or balle\y+balle\dy<1
    balle\dy=-balle\dy
  EndIf
  If balle\x+balle\dx<799 And balle\x+balle\dx>1 And balle\y+balle\dy<599 And balle\x+balle\dx>1 
    balle\x=balle\x+balle\dx
    balle\y=balle\y+balle\dy
  EndIf
  ;-affichage
  DisplaySprite(#spr_calque,0,0)
  DisplayTransparentSprite(#spr_courbe,0,0)
  DisplaySprite(#spr_balle,balle\x,balle\y)
  DisplaySprite(#spr_raquette,balle\x-32,568)
  ;-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() 
  Delay(10)
  FlipBuffers() 
ForEver
Il y a peut-être des bugs qui traînent mais l'idée est là :P !

Exemple de courbe(à remplacer directement dans le code) :

Code : Tout sélectionner

  fonction(abscisse)=#amplitude*Sin(#pulsation*abscisse)+#amplitude*0.5*Sin(3*#pulsation*abscisse)+#valeur_moyenne
  
derive(abscisse)=#amplitude*#pulsation*Cos(#pulsation*abscisse)+3*#pulsation*#amplitude*0.5*Cos(3*#pulsation*abscisse)
Ne pas oublier de modifier le test de collision(l'amplitude du signal a changé!):

Code : Tout sélectionner

  ;-test collision si la balle se rapproche de la courbe
  If balle\y+balle\dy<1.5*#amplitude+#valeur_moyenne+balle\dl
Image

Hasta la vista!
Dernière modification par Huitbit le ven. 28/déc./2007 4:31, modifié 4 fois.
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

c'est super sympa !! :o)
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

super !! :)

cependant je dénote une erreur dans les rebonds !! :)


en effet ta balle suit la trajectoire bleu dans ce cas de figure, alors qu'elle devrai suivre la trajectoire verte !!

Image

c'est meme encore plus flagrant sur le bord droit !!

je pense que c'est due au fait que tu inverse la coordonée y des qu'il y a contact avec la ligne !!

alors qu'il faudrai que les y s'inversent seulement lorsque c'est le haut de la balle qui est touchée ...





une balle qui a un angle d'arrivé sur un bord, rebondit toujours avec le meme angle pour la sortie , si ,bien sur elle n'as pas d'effets !! :)

comme ça :
Image

et peut importe l'angle du bord jaune, c'est toujours pareille
(vu dans "C'est pas sorcier" theme sur le billard :) )
Anonyme

Message par Anonyme »

Image

C'est l'écart d'angle entre la ligne jaune & la trajectoire qu'il faut prendre en compte , utilise les vecteurs , c'est plus simple pour se genre de manip.
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

j'ai modifier ton code, pour montrer ce que je veux dire

pour le coup je n'utilise aucun calcul !!!
juste les rebonds "Naturel" :D

bien sur il reste des petites erreurs dues a la fonction spritecollision qui n'est pas parfaite ... (a une epoque nous avions fait des recherche la dessus :) )


mon exemple te montre que c'est plus realiste parceque la balle rebondit plusieurs fois dans le creux ... alors qu'avec ta fonction, cela n'arrive jamais
et c'est pas .... naturel :lol:

test ça :

Code : Tout sélectionner


;trigo_squash
;auteur Huitbit
;PureBasic 4.10
Enumeration
  #spr_courbe
  #spr_calque
  #spr_balle
  #spr_raquette
EndEnumeration
Structure donnees
  x.w
  Y.w
  dx.f
  dy.f
  dl.f
EndStructure
balle.donnees
balle\x=300
balle\Y=564
balle\dx=-6
balle\dy=-4
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
ancien_dx.f
ancien_dy.f
abscisse.w

cos_a.f
cos_2a.f
sin_2a.f
#amplitude=100
#pulsation=2*#PI/240
#valeur_moyenne=240
Dim fonction.f(800)
Dim derive.f(800)
Dim norme_vecteur_tangent.f(800)
For abscisse= 0 To 800
  fonction(abscisse)=#amplitude*Sin(#pulsation*abscisse)+#valeur_moyenne
  derive(abscisse)=#amplitude*#pulsation*Cos(#pulsation*abscisse)
  norme_vecteur_tangent(abscisse)=Sqr(1+derive(abscisse)*derive(abscisse))
Next abscisse
Macro calcul_cos_a
  cos_a=(balle\dx+balle\dy*derive(balle\x+4))/(balle\dl*norme_vecteur_tangent(balle\x+4))
EndMacro


;***************************************************
;-PROGRAMME PRINCIPAL
InitSprite()
InitKeyboard()
OpenWindow(0,0,0,800,600,"trigo_squash")
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0)
;-courbe
CreateSprite(#spr_courbe,800,600)
StartDrawing(SpriteOutput(#spr_courbe))
old_x=0
old_y=fonction(0)
For abscisse=1 To 799
  For j=0 To 20
    LineXY(old_x,old_y-j,abscisse,fonction(abscisse)-j,RGB(255-10*j,0,0))
  Next j
  LineXY(old_x,old_y,abscisse,fonction(abscisse),RGB(255,0,0))
  
  old_x=abscisse
  old_y=fonction(abscisse)
Next abscisse
StopDrawing()
;-calque
CreateSprite(#spr_calque,800,600)
StartDrawing(SpriteOutput(#spr_calque))
;fond étoilé
For  numero_etoile = 0 To 500
  Plot(1+Random(798),1+Random(567),RGB(255,255,255))
Next numero_etoile
StopDrawing()
;-balle
CreateSprite(#spr_balle,8,8)
StartDrawing(SpriteOutput(#spr_balle))
Circle(4,4,4,RGB(255,255,255))
StopDrawing()
;-raquette
CreateSprite(#spr_raquette,64,16)
StartDrawing(SpriteOutput(#spr_raquette))
Box(0,0,64,16,RGB(255,255,255))
StopDrawing()

;-BOUCLE PRINCIPALE
Repeat
  Repeat
    Event = WindowEvent()     
    If  Event = #PB_Event_CloseWindow
      End
    EndIf
  Until Event = 0 
  calcul_cos_a
  
  
  If SpritePixelCollision(#spr_balle,balle\x+2,balle\Y,#spr_courbe,0,0) 
    balle\dy=-balle\dy   
  EndIf
  
  
  If SpritePixelCollision(#spr_balle,balle\x+10,balle\Y,#spr_courbe,0,0)  
    balle\dx=-balle\dx  
  EndIf 
  
  
  If SpritePixelCollision(#spr_balle,balle\x-10,balle\Y,#spr_courbe,0,0) 
    balle\dx=-balle\dx  
  EndIf
  
  ;-tests bordure écran
  If balle\x+balle\dx>792 Or balle\x+balle\dx<1
    balle\dx=-balle\dx
  EndIf
  If balle\Y+balle\dy>560 Or balle\Y+balle\dy<1
    balle\dy=-balle\dy
    
    de=Random(6)+2
    If balle\dx<0
      balle\dx=-de
    Else
      balle\dx=de
    EndIf
    
    
    
  EndIf
  If balle\x+balle\dx<799 And balle\x+balle\dx>1 And balle\Y+balle\dy<599 And balle\x+balle\dx>1
    balle\x=balle\x+balle\dx
    balle\Y=balle\Y+balle\dy
  EndIf
  ;-affichage
  DisplaySprite(#spr_calque,0,0)
  DisplayTransparentSprite(#spr_courbe,0,0)
  DisplaySprite(#spr_balle,balle\x,balle\Y)
  DisplaySprite(#spr_raquette,balle\x-32,568)
  ;-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()
  Delay(10)
  FlipBuffers()
ForEver

relancez le prg plusieurs fois
parceque quelque fois la séquence de random déconne :lol:
Avatar de l’utilisateur
Huitbit
Messages : 940
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Message par Huitbit »

@Thyphoon
Merci :wink:

@Dobro
je pense que c'est due au fait que tu inverses la coordonée y des qu'il y a contact avec la ligne !!
Bien vu! En fait j'ai fait le test sans me soucier de la forme de la balle!

Par contre pour les calculs, tu peux avoir confiance, ils sont nécessaires pour être correct.
bien sur il reste des petites erreurs dues a la fonction spritecollision qui n'est pas parfaite ... (a une epoque nous avions fait des recherche la dessus )
Justement, avec les calculs, on peut se passer de spritepixelcollision() (ici spritecollision() n'a pas à être super efficace, il doit juste me permettre de récupérer balle\x!)
Au début, je faisais comme ça :

Code : Tout sélectionner

If Abs(balle\y+4-fonction(balle\x+4))<balle\dl*2
    calcul_cos_a
    calculs_pour_rotation
    rotation
  EndIf
Tant que la balle ne se déplaçait pas trop vite, ça donnait des résultats corrects!
Image

et peut importe l'angle du bord jaune, c'est toujours pareille
(vu dans "C'est pas sorcier" theme sur le billard )
C'est une super émission, même si elle prend quelques libertés pour mieux expliquer les phénomènes.
Pour les collisions en physique, on travaille toujours avec la normale(voir dessin plus bas), car c'est la seule chose dont on soit sûr!



@Cpl Bator
C'est l'écart d'angle entre la ligne jaune & la trajectoire qu'il faut prendre en compte , utilise les vecteurs , c'est plus simple pour se genre de manip.
En fait, je n'utilise que les vecteurs!
Quand je parle de rotation d'angle double de celui d'arrivée, c'est une autre manière de dire ça:
Image


En physique, on définit les angles par rapport à la normale(qui est perpendiculaire à la tangente au point d'impact!).
Grâce à dx et dy et au vecteur directeur de la tangente je peux calculer le cosinus de l'angle (voir produit scalaire de deux vecteurs).
Avec le cosinus, je peux faire ma rotation!

Merci de vos remarques, je vais voir si je peux arranger ces tests de collision! :wink:

Hasta la vista !

[EDIT]
J'ai rajouté des tests supplémentaires pour la balle(prise en compte de la direction de déplacement).
Mais, j'ai du mal avec spritepixelcollision()!
:? Je sais pas trop ce que je fais :?
Y a-t-il déjà eu un tuto sur ce type de collisions ?

Code : Tout sélectionner

;trigo_squash
;auteur Huitbit
;PureBasic 4.10
Enumeration
  #spr_courbe
  #spr_calque
  #spr_balle
  #spr_raquette
EndEnumeration
Structure donnees
  x.w
  y.w
  dx.f
  dy.f
  dl.f
EndStructure
balle.donnees
balle\x=300
balle\y=564
balle\dx=-4
balle\dy=-4
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
ancien_dx.f
ancien_dy.f
abscisse.w
x_test.b
y_test.b

cos_a.f
cos_2a.f
sin_2a.f
#amplitude=80
#pulsation=2*#PI/240
#valeur_moyenne=240
Dim fonction.f(800)
Dim derive.f(800)
Dim norme_vecteur_tangent.f(800)
For abscisse= 0 To 800
  fonction(abscisse)=#amplitude*Sin(#pulsation*abscisse)+#amplitude*0.3*Sin(5*#pulsation*abscisse)+#valeur_moyenne
  derive(abscisse)=#amplitude*#pulsation*Cos(#pulsation*abscisse)+5*#pulsation*#amplitude*0.3*Cos(5*#pulsation*abscisse)
  norme_vecteur_tangent(abscisse)=Sqr(1+derive(abscisse)*derive(abscisse))
Next abscisse
Macro calcul_cos_a
cos_a=(balle\dx+balle\dy*derive(balle\x+4))/(balle\dl*norme_vecteur_tangent(balle\x+4))
EndMacro
Macro calculs_pour_rotation
cos_2a=2*cos_a*cos_a-1
sin_2a=Sqr(1-cos_2a*cos_2a)
EndMacro
Macro rotation
ancien_dx=balle\dx
ancien_dy=balle\dy
If cos_a>0
  balle\dx=cos_2a*ancien_dx-sin_2a*ancien_dy
  balle\dy=sin_2a*ancien_dx+cos_2a*ancien_dy
Else
  balle\dx=cos_2a*ancien_dx+sin_2a*ancien_dy
  balle\dy=-sin_2a*ancien_dx+cos_2a*ancien_dy
EndIf
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
EndMacro

;***************************************************
;-PROGRAMME PRINCIPAL
InitSprite()
InitKeyboard()
OpenWindow(0,0,0,800,600,"trigo_squash")
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0)
;-courbe
CreateSprite(#spr_courbe,800,600)
StartDrawing(SpriteOutput(#spr_courbe))
old_x=0
old_y=fonction(0)
For abscisse=1 To 799
  For j=0 To 40
    LineXY(old_x,old_y-j,abscisse,fonction(abscisse)-j,RGB(255-6*j,0,0))
  Next j
  LineXY(old_x,old_y,abscisse,fonction(abscisse),RGB(255,0,0))
  
  old_x=abscisse
  old_y=fonction(abscisse)
Next abscisse 
StopDrawing()
;-calque
CreateSprite(#spr_calque,800,600)
StartDrawing(SpriteOutput(#spr_calque))
;fond étoilé
For  numero_etoile = 0 To 500
  Plot(1+Random(798),1+Random(567),RGB(255,255,255))
Next numero_etoile
StopDrawing()
;-balle
CreateSprite(#spr_balle,8,8)
StartDrawing(SpriteOutput(#spr_balle))
Circle(4,4,4,RGB(255,255,255))
StopDrawing()
;-raquette
CreateSprite(#spr_raquette,64,16)
StartDrawing(SpriteOutput(#spr_raquette))
Box(0,0,64,16,RGB(255,255,255))
StopDrawing()

;-BOUCLE PRINCIPALE
Repeat
  Repeat
    Event = WindowEvent()      
    If  Event = #PB_Event_CloseWindow
      End 
    EndIf
  Until Event = 0
  ;-test collision si la balle se rapproche de la courbe
  If balle\y+balle\dy<1.3*#amplitude+#valeur_moyenne+balle\dl
    If balle\dx<0 And balle\dy<0
      x_test=0 : y_test=0
    ElseIf balle\dx>0 And balle\dy<0
      x_test=8 : y_test=0
    ElseIf balle\dx>0 And balle\dy>0
      x_test=8 : y_test=8
    ElseIf balle\dx<0 And balle\dy>0
      x_test=0 : y_test=8
    EndIf   
    If SpritePixelCollision(#spr_balle,balle\x+x_test,balle\y+y_test,#spr_courbe,0,0)
      calcul_cos_a
      calculs_pour_rotation
      rotation
    EndIf
  EndIf
  ;-tests bordure écran
  If balle\x+balle\dx>792 Or balle\x+balle\dx<1
    balle\dx=-balle\dx
  EndIf
  If balle\y+balle\dy>560 Or balle\y+balle\dy<1
    balle\dy=-balle\dy
  EndIf
  If balle\x+balle\dx<799 And balle\x+balle\dx>1 And balle\y+balle\dy<599 And balle\x+balle\dx>1 
    balle\x=balle\x+balle\dx
    balle\y=balle\y+balle\dy
    ;tracé de la trajectoire
;     StartDrawing(SpriteOutput(#spr_calque))
;     Plot(balle\x+4,balle\y+4,RGB(255,255,0))
;     StopDrawing()
    
  EndIf
  ;-affichage
  DisplaySprite(#spr_calque,0,0)
  DisplayTransparentSprite(#spr_courbe,0,0)
  DisplaySprite(#spr_balle,balle\x,balle\y)
  DisplaySprite(#spr_raquette,balle\x-32,568)
  ;-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() 
  Delay(10)
  FlipBuffers() 
ForEver
Dernière modification par Huitbit le ven. 28/déc./2007 15:46, modifié 2 fois.
Anonyme

Message par Anonyme »

Tu n'est pas obligé d'utilisé la fct° de collision au pixel près.
Tu connais le rayon de ta balle , et le point de rebond ( dans ton cas la courbe ) tu teste la distance entre le point de ta courbe et le centre de ta balle , si la distance est inférieur au rayon , alors il y a une collision.

@++
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

c'est quand meme vachement mieux !! :)

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

Message par Huitbit »

@Dobro
c'est quand meme vachement mieux !!

bravo a toi ..
Merci, mais c'est toi qui a eu l'idée pour le test :wink: (il y a encore du boulot ; exemple, un balle\dy=0 ça peut entraîner dans certains cas un déplacement horizontal "infini")
Si ça peut te donner des idées de suite pour Purenoïde :P
Exemple : en précalculant plusieurs fonctions (avec leurs dérivées), on peut animer la courbe et faire un mur de fond mouvant !
ou...plus simple :
faire descendre ou monter la courbe (changer la valeur moyenne) au cours du temps, cela ne change pas la dérivée!


@Cpl.Bator
Le problème, c'est qu'avec balle\dx ou balle\dy, on peut passer au travers du test (j'en parle au post précédent).
Pour faire plus simple, j'ai utilisé spritepixelcollision() (que je ne maîtrise pas du tout :x )


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

Un exemple vaut mieux que des mots (minijeu) !

Message par Huitbit »

[EDIT] J'ai légèrement modifié les valeurs de x_test et y_test (8 au lieu de 4) dans la collision balle_courbe, car avec la fonction f(x)=200 (droite horizontale, dérivée f'(x)=0, la balle faisait plusieurs contacts et repartait dans le mauvais sens 8O )
J'ai aussi "épaissi" la courbe

Autre exemple de courbe (plan incliné):

Code : Tout sélectionner

fonction(abscisse)=200+10/80*abscisse
  derive(abscisse)=10/80
Décalage de la courbe à chaque impact !
Comptage du score.
Collision avec la raquette à améliorer :?
Déplacement horizontal anormal(assez rare)
Passage de la balle au travers (de l'autre côté) de la courbe(rare)
Pas d'effets avec la raquette
...etc ?

Code : Tout sélectionner

;trigo_squash_stress
;auteur Huitbit
;PureBasic 4.10
Enumeration
  #spr_courbe
  #spr_calque
  #spr_balle
  #spr_raquette
EndEnumeration
Structure donnees
  x.w
  y.w
  dx.f
  dy.f
  dl.f
EndStructure
balle.donnees
balle\x=300
balle\y=564
balle\dx=-4
balle\dy=-4
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
ancien_dx.f
ancien_dy.f
abscisse.w
x_test.b
y_test.b
x_raquette.w=balle\x-32
chrono.l=ElapsedMilliseconds() 
cos_a.f
cos_2a.f
sin_2a.f
#amplitude=80
#pulsation=2*#PI/240
#valeur_moyenne=200
decalage_valeur_moyenne.w=0
decalage.b=8
Dim fonction.f(800)
Dim derive.f(800)
Dim norme_vecteur_tangent.f(800)
For abscisse= 0 To 800
  fonction(abscisse)=#amplitude*Sin(#pulsation*abscisse)+#amplitude*0.3*Sin(4*#pulsation*abscisse)+#valeur_moyenne
  derive(abscisse)=#amplitude*#pulsation*Cos(#pulsation*abscisse)+4*#pulsation*#amplitude*0.3*Cos(4*#pulsation*abscisse)
  norme_vecteur_tangent(abscisse)=Sqr(1+derive(abscisse)*derive(abscisse))
Next abscisse
Macro calcul_cos_a
cos_a=(balle\dx+balle\dy*derive(balle\x+4))/(balle\dl*norme_vecteur_tangent(balle\x+4))
EndMacro
Macro calculs_pour_rotation
cos_2a=2*cos_a*cos_a-1
sin_2a=Sqr(1-cos_2a*cos_2a)
EndMacro
Macro rotation
ancien_dx=balle\dx
ancien_dy=balle\dy
If cos_a>0
  balle\dx=cos_2a*ancien_dx-sin_2a*ancien_dy
  balle\dy=sin_2a*ancien_dx+cos_2a*ancien_dy
Else
  balle\dx=cos_2a*ancien_dx+sin_2a*ancien_dy
  balle\dy=-sin_2a*ancien_dx+cos_2a*ancien_dy
EndIf
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
EndMacro

;***************************************************
;-PROGRAMME PRINCIPAL
InitSprite()
InitKeyboard()
OpenWindow(0,0,0,800,600,"Trigo_squash_stress <<<<>>>>flêches gauche / droite pour diriger la raquette")
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0)
;-courbe
CreateSprite(#spr_courbe,800,600)
StartDrawing(SpriteOutput(#spr_courbe))
old_x=0
old_y=fonction(0)
For abscisse=1 To 799
  For j=0 To 40
    LineXY(old_x,old_y-j,abscisse,fonction(abscisse)-j,RGB(255-6*j,0,0))
  Next j
  LineXY(old_x,old_y,abscisse,fonction(abscisse),RGB(255,0,0))
  
  old_x=abscisse
  old_y=fonction(abscisse)
Next abscisse 
StopDrawing()
;-calque
CreateSprite(#spr_calque,800,600)
StartDrawing(SpriteOutput(#spr_calque))
;fond étoilé
For  numero_etoile = 0 To 500
  Plot(1+Random(798),1+Random(567),RGB(255,255,255))
Next numero_etoile
StopDrawing()
;-balle
CreateSprite(#spr_balle,8,8)
StartDrawing(SpriteOutput(#spr_balle))
Circle(4,4,4,RGB(255,255,255))
StopDrawing()
;-raquette
CreateSprite(#spr_raquette,64,16)
StartDrawing(SpriteOutput(#spr_raquette))
For i=0 To 7
  Box(i,i,64-2*i,16-2*i,RGB(255-15*i,255-15*i,255-15*i))
Next i
StopDrawing()

;-BOUCLE PRINCIPALE
Repeat
  Repeat
    Event = WindowEvent()      
    If  Event = #PB_Event_CloseWindow
      End 
    EndIf
  Until Event = 0
  ;-test collision si la balle se rapproche de la courbe
  If balle\y+balle\dy<1.3*#amplitude+#valeur_moyenne+decalage_valeur_moyenne+balle\dl
    If balle\dx<0 And balle\dy<0
      x_test=0 : y_test=0
    ElseIf balle\dx>0 And balle\dy<0
      x_test=8 : y_test=0
    ElseIf balle\dx>0 And balle\dy>0
      x_test=8 : y_test=8
    ElseIf balle\dx<0 And balle\dy>0
      x_test=0 : y_test=8
    EndIf   
    If SpritePixelCollision(#spr_balle,balle\x+x_test,balle\y+y_test,#spr_courbe,0,decalage_valeur_moyenne)
      score=score+1
      calcul_cos_a
      calculs_pour_rotation
      rotation
      If #valeur_moyenne+decalage_valeur_moyenne+1.3*#amplitude>=500 Or #valeur_moyenne+decalage_valeur_moyenne-1.3*#amplitude=<50
        decalage=-decalage
      EndIf
      If ElapsedMilliseconds()-chrono>100
        chrono=ElapsedMilliseconds() 
        decalage_valeur_moyenne=decalage_valeur_moyenne+decalage
      EndIf
    EndIf
  EndIf
  ;-tests bordure écran
  If balle\x+balle\dx>792 Or balle\x+balle\dx<1
    balle\dx=-balle\dx
  EndIf
  If   balle\y+balle\dy<1
    balle\dy=-balle\dy
  EndIf
  If  balle\y+balle\dy>592 
    score=score-10
    balle\dy=-balle\dy
  EndIf
  
  If balle\x+balle\dx<799 And balle\x+balle\dx>1 And balle\y+balle\dy<599 And balle\x+balle\dx>1 
    balle\x=balle\x+balle\dx
    balle\y=balle\y+balle\dy
    ;tracé de la trajectoire
    ;     StartDrawing(SpriteOutput(#spr_calque))
    ;     Plot(balle\x+4,balle\y+4,RGB(255,255,0))
    ;     StopDrawing()
  EndIf
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_Left)
    x_raquette=x_raquette-8
  ElseIf KeyboardPushed(#PB_Key_Right)
    x_raquette=x_raquette+8
  EndIf
  ;test collision raquette
  If  SpriteCollision(#spr_raquette,x_raquette,568,#spr_balle,balle\x,balle\y)
    balle\dy=-balle\dy
  EndIf
  
  ;-affichage
  DisplaySprite(#spr_calque,0,0)
  DisplayTransparentSprite(#spr_courbe,0,decalage_valeur_moyenne)
  DisplaySprite(#spr_balle,balle\x,balle\y)
  DisplaySprite(#spr_raquette,x_raquette,568)
  ;-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)+">>>>>>>>>>>>>>>>SCORE : "+Str(score) )
  StopDrawing() 
  Delay(10)
  FlipBuffers() 
ForEver
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Message par comtois »

très sympa ton code :)

Tu devrais peut-être ajouter une dose de gravité pour que la balle tombe de temps en temps ; je me suis retrouvé dans le cas où elle rebondissait du bord droit au bord gauche indéfiniment :)

Je suis de l'avis de Cpl.Bator , tu as deux équations celle de la balle et celle de la courbe, il devrait être possible de calculer à quel moment aura lieu l'impact

un truc du genre pour la trajectoire de la balle

b = b0 + t*v avec
t compris dans l'intervalle [0..1]
b0 position d'origine de la balle
b nouvelle position de la balle.
v c'est la vitesse de déplacement de la balle.

avec t=0 on a b=b0 --> normal on est à l'origine
avec t=1 on a b=b0 + v --> nouvelle position de la balle.

Reste à calculer à quel moment la balle se trouvera sur la courbe (calcul de l'intersection entre une droite et une sinusoïde. La balle est petite l'intersection avec une droite suffit pour donner l'illusion d'une collision parfaite, ,sinon faudrait calculer l'intersection avec une sphère).

En déduire la valeur de t pour que la balle soit sur la courbe, si t n'est pas dans l'intervalle [0..1] c'est qu'il n'y aura pas collision , sinon on a calculé la position de l'impact, c'est peut-être ce que tu fais déjà dans ton code ? je ne l'ai pas étudié, et je n'ai pas la tête à faire des maths en ce moment :)

C'est la méthode utilisée pour ne pas être dépendant de la vitesse de l'objet.
En procédant ainsi quelque soit la vitesse de ta balle tu ne peux pas louper une collision, puisque tu la calcules avant de déplacer ta balle :)

[EDIT]

Si tu as calculé une collision pour t=0.7 par exemple , normalement , il faut placer ta balle au point de collision (c'est fictif c'est à dire que tu initialises des variables à ces valeurs pour poursuivre tes calculs), et ensuite tu calcules le rebond , et tu recalcules une collision avec le nouveau vecteur vitesse.

il faut traiter ce truc dans une procédure récursive.

Sinon si tu te contentes de replacer ta balle au point de collision, le mouvement ne sera peut-être pas fluide.

Exemple concret, tu te déplaces de 20 pixels , si tu es en collision au bout de 5 pixels, et que tu affiches ta balle au point de collision, et qu'ensuite à l'image suivante tu te déplaces de 20 pixels, ça sera pas clean.

Alors que si tu calcules le reste du déplacement à partir du point d'impact, tu vas encore déplacer ta balle de 15 pixels en tenant compte du rebond, et à l'image suivante tu pourras te déplacer de 20 pixels, tu te déplaces bien de 20 pixels à chaque image. le mouvement sera plus fluide.

C'est beaucoup de baratin pour pas grand chose, j'espère que je ne t'embrouille pas trop :)
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
Avatar de l’utilisateur
Huitbit
Messages : 940
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Message par Huitbit »

@Comtois
Intéressant tout ça...
La plupart des bugs viennent de ce test de collision.
C'est flagrant quand on prend le cas le plus simple : un mur horizontal
Pour que ça marche j'ai dû modifier la zone de test pour éviter les "collisions en chaîne".
Idem, les bugs sont moins fréquents avec une balle plus grosse!
Je vais essayer ta méthode(après les fêtes :P ), ça pourra servir dans d'autres situations.
Dommage pour spritepixelcollision()....

Hasta la vista!
beauregard
Messages : 1307
Inscription : dim. 08/juil./2007 18:32
Localisation : Toulouse

système D

Message par beauregard »

Huitbit a écrit :Dommage pour spritepixelcollision()....
bonsoir, heu, ce que tu fais là est de haute volée, et une simple commande ne répondra pas à toutes tes attentes.
Voici 2 pensées, que je retranscris difficilement avec des mots:
-> après une collision, il faudait attendre un petit peu avant de refaire un autre test de collision.
-> s'il y a un 2ème rebond sur les courbes, alors il faut orienter la balle vers le bas de l'écran.
A+
Avatar de l’utilisateur
Huitbit
Messages : 940
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Message par Huitbit »

que je retranscris difficilement avec des mots
Tout est clair ! Tu as bien compris d'où viennent les problèmes!
L'idéal pour spritepixelcollision(), ça serait une balle de 1 pixel :lol:

Pour l'instant, je vais essayer la méthode conseillée par Comtois et Cpl. Bator, ça demande un peu plus d'efforts mais ça donnera un meilleur contrôle car avec spritepixelcollision() j'ai l'impression d'avoir un bandeau sur les yeux 8) !

Hasta la vista.

[EDIT]
Je n'ai appliqué qu'une partie de la méthode:
;-test collision si la balle se rapproche de la courbe
If balle\y+balle\dy<#amplitude+#valeur_moyenne
t=0
For k=0 To 99
t=t+0.01
balle_x_test=balle\x+4+t*balle\dx
balle_y_test=balle\y+4+t*balle\dy
If Abs(balle_y_test-(#amplitude*Sin(#pulsation*balle_x_test)+#valeur_moyenne))<4
calcul_cos_a
calculs_pour_rotation
rotation
Break
EndIf
Next k
EndIf
...mais pour l'instant j'ai de bons résultats même avec des signaux de grandes amplitudes (en plus les rebonds sont de meilleure qualité :P ) :

Code : Tout sélectionner

;trigo_squash_0
;auteur Huitbit
;PureBasic 4.10
Enumeration
  #spr_courbe
  #spr_calque
  #spr_balle
  #spr_raquette
EndEnumeration
Structure donnees
  x.w
  y.w
  dx.f
  dy.f
  dl.f
EndStructure
balle.donnees
balle\x=300
balle\y=564
balle\dx=-4
balle\dy=-4
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
ancien_dx.f
ancien_dy.f
abscisse.w
balle_x_test.f
balle_y_test.f
x_raquette.w=balle\x-32
cos_a.f
cos_2a.f
sin_2a.f
t.f;paramètre pour test collision
i.b;variable d'itération
k.b; variable d'itération
#amplitude=120
#pulsation=2*#PI/160
#valeur_moyenne=240
Dim fonction.f(800)
Dim derive.f(800)
Dim norme_vecteur_tangent.f(800)
For abscisse= 0 To 800
  fonction(abscisse)=#amplitude*Sin(#pulsation*abscisse)+#valeur_moyenne
  derive(abscisse)=#amplitude*#pulsation*Cos(#pulsation*abscisse)
  norme_vecteur_tangent(abscisse)=Sqr(1+derive(abscisse)*derive(abscisse))
Next abscisse
Macro calcul_cos_a
cos_a=(balle\dx+balle\dy*derive(balle\x+4))/(balle\dl*norme_vecteur_tangent(balle\x+4))
EndMacro
Macro calculs_pour_rotation
cos_2a=2*cos_a*cos_a-1
sin_2a=Sqr(1-cos_2a*cos_2a)
EndMacro
Macro rotation
ancien_dx=balle\dx
ancien_dy=balle\dy
If cos_a>0
  balle\dx=cos_2a*ancien_dx-sin_2a*ancien_dy
  balle\dy=sin_2a*ancien_dx+cos_2a*ancien_dy
Else
  balle\dx=cos_2a*ancien_dx+sin_2a*ancien_dy
  balle\dy=-sin_2a*ancien_dx+cos_2a*ancien_dy
EndIf
balle\dl=Sqr(balle\dx*balle\dx+balle\dy*balle\dy)
EndMacro

;***************************************************
;-PROGRAMME PRINCIPAL
InitSprite()
InitKeyboard()
OpenWindow(0,0,0,800,600,"Trigo_squash_test <<<<>>>>flêches gauche / droite pour diriger la raquette")
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0)
;-courbe
CreateSprite(#spr_courbe,800,600)
StartDrawing(SpriteOutput(#spr_courbe))
old_x=0
old_y=fonction(0)
For abscisse=1 To 799
  LineXY(old_x,old_y,abscisse,fonction(abscisse),RGB(255,0,0))
  old_x=abscisse
  old_y=fonction(abscisse)
Next abscisse 
StopDrawing()
;-calque
CreateSprite(#spr_calque,800,600)
;-balle
CreateSprite(#spr_balle,8,8)
StartDrawing(SpriteOutput(#spr_balle))
Circle(4,4,4,RGB(255,255,255))
StopDrawing()
;-raquette
CreateSprite(#spr_raquette,64,16)
StartDrawing(SpriteOutput(#spr_raquette))
Box(0,0,64,16,RGB(255,255,255))
StopDrawing()

;-BOUCLE PRINCIPALE
Repeat
  Repeat
    Event = WindowEvent()      
    If  Event = #PB_Event_CloseWindow
      End 
    EndIf
  Until Event = 0
  ;-test collision si la balle se rapproche de la courbe
  If balle\y+balle\dy<#amplitude+#valeur_moyenne
    t=0
    For k=0 To 99
      t=t+0.01
      balle_x_test=balle\x+4+t*balle\dx
      balle_y_test=balle\y+4+t*balle\dy
      If Abs(balle_y_test-(#amplitude*Sin(#pulsation*balle_x_test)+#valeur_moyenne))<4
        Debug "balle\dy="+StrF(balle\dy)
        Debug "t="+StrF(t)+"   k="+Str(k)
        Debug "balley_test="+StrF(balle_y_test)
        Debug "f(x_test)="+StrF(#amplitude*Sin(#pulsation*balle_x_test)+#valeur_moyenne)
        Debug "différence="+StrF(Abs(balle_y_test-(#amplitude*Sin(#pulsation*balle_x_test)+#valeur_moyenne)))
        calcul_cos_a
        calculs_pour_rotation
        rotation
        Break
      EndIf
    Next k
  EndIf
  ;-tests bordure écran
  If balle\x+balle\dx>792 Or balle\x+balle\dx<1
    balle\dx=-balle\dx
  EndIf
  If   balle\y+balle\dy<1
    balle\dy=-balle\dy
  EndIf
  If  balle\y+balle\dy>592 
    balle\dy=-balle\dy
  EndIf
  If balle\x+balle\dx<799 And balle\x+balle\dx>1 And balle\y+balle\dy<599 And balle\x+balle\dx>1 
    balle\x=balle\x+balle\dx
    balle\y=balle\y+balle\dy
    ;-tracé de la trajectoire
    StartDrawing(SpriteOutput(#spr_calque))
    Plot(balle\x+4,balle\y+4,RGB(255,255,0))
    StopDrawing()
  EndIf
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_Left)
    x_raquette=x_raquette-8
  ElseIf KeyboardPushed(#PB_Key_Right)
    x_raquette=x_raquette+8
  EndIf
  ;test collision raquette
  If  SpriteCollision(#spr_raquette,x_raquette,568,#spr_balle,balle\x,balle\y)
    balle\dy=-balle\dy
  EndIf
  ;-affichage
  DisplaySprite(#spr_calque,0,0)
  DisplayTransparentSprite(#spr_courbe,0,decalage_valeur_moyenne)
  DisplaySprite(#spr_balle,balle\x,balle\y)
  DisplaySprite(#spr_raquette,x_raquette,568)
  ;-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() 
  Delay(10)
  FlipBuffers() 
ForEver
Répondre