Trigo_squash (démo+ exemple de minijeu)
Publié : jeu. 27/déc./2007 5:24
En regardant le jeu squash, j’ai eu l’idée (éclair d'intelligence à la Rantanplan
) de remplacer les lignes droites par des courbes.
J'ai jamais vu ça, c'est peut-être une exclusivité mondiale
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)

La raquette bouge toute seule (magie!
) 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!)
Il y a peut-être des bugs qui traînent mais l'idée est là
!
Exemple de courbe(à remplacer directement dans le code) :
Ne pas oublier de modifier le test de collision(l'amplitude du signal a changé!):

Hasta la vista!
J'ai jamais vu ça, c'est peut-être une exclusivité mondiale


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)
La raquette bouge toute seule (magie!

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

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)
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
Hasta la vista!