Quelques explications sur la physique qui se cache derrière le petit jeu de bascule "retour à Winterfell"
http://purebasic.fr/french/viewtopic.ph ... &start=240
J'ai utilisé ce qu'on appelle la conservation du moment cinétique.
Pour le vol, la notion de chute libre (voir la macro dans le code)
Par contre, pour modéliser l'impulsion donnée par les jambes du sauteur, il n'y a pas de modèle
alors, j'ai juste multiplié la vitesse de départ du sauteur par un facteur arbitraire.
Dans le punch, j'ai dû définir une vitesse minimale et une vitesse maximale pour éviter les situations anormales
(vMax=racine(2*g*hMax) formule obtenue avec la conservation de l'énergie mécanique).
Il y a environ 70 tests

Bref voici le code du moteur :
(tout est proportionnel à la taille de la bascule a=256, il suffit de changer a pour changer les dimensions,
dans le punch a=128.
A correspond au sprite de gauche
B au sprite de droite
On peut modifier mA et mB (dans le punch mA = mB) )
Code : Tout sélectionner
; Simulation de bascule
; Conservation du moment cinétique
; Huitbit juillet 2014
; PureBasic 5.22
; *********************************
;- déclarations et initialisations des variables
EnableExplicit
;{
Enumeration
#sprBasculeA
#sprBasculeB
#sprA
#sprB
#volA
#volB
#volNull
EndEnumeration
;- dimensions écran
;{
ExamineDesktops()
Define.i L = DesktopWidth(0) * 3 / 4
Define.i H = DesktopHeight(0) * 3 / 4
;}
#g = 10 ; intensité du champ de pesanteur
#dt = 0.1 ; pas de calcul pour la méthode d'Euler
Define.f dvy = #g * #dt
Define.i dxBascule = 4
Define.f xA, yA, vxA, vyA, vA, yAprecedent
Define.f xB, yB, vxB, vyB, vB, yBprecedent
Define.w vol = #volNull
Define.w sprBascule = #sprBasculeA
Define.i a = 256 ; largeur du sprite bascule
Define.i b = a / 4 ; hauteur bascule
Define.i lBascule = Sqr(a * a + b * b) ; longueur en pixels de la planche de la bascule(diagonale du rectangle)
Define.i c = a / 8 ; côté personnage
Define.f pente = b / a
Define.f alpha = ATan2(a, b)
Define.f omega = 0 ; vitesse angulaire
Define.i mBascule = 10 ;masses
Define.i mA = 100
Define.i mB = 100
Define.i jbascule = mBascule * lBascule * lBascule / 12 ; J=mL²/12 moment d'inertie de la planche
Define.i jtotal
Define.f rA
Define.f rB
Define.f xBascule = (L - a) / 2
Define.f yBascule = H - b - 5
Define.f yCentreBascule = yBascule + b * 0.5
Define.f xCentreBascule = xBascule + a * 0.5
Define.i i ; variable d'itération
;}
;- macros
;{
Macro chuteLibre(x, y, vx, vy)
; vx = vx+0*#dt pas de composante de g selon l'horizontale
vy = vy + dvy
x = x + vx * #dt
y = y + vy * #dt
EndMacro
Macro rayon(r, x1, y1, x2, y2)
r = Sqr((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
EndMacro
;}
;- déclarations des procédures
;{
;}
;-
;-PROGRAMME PRINCIPAL
InitSprite()
InitKeyboard()
OpenWindow(0, 0, 0, L, H, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0, 0, L, H, 0, 0, 0, #PB_Screen_SmartSynchronization)
;- sprites bascule
;{
CreateSprite(#sprBasculeA, a, b) ; A en haut
StartDrawing(SpriteOutput(#sprBasculeA))
DrawingMode(#PB_2DDrawing_Outlined)
Box(0, 0, a, b, RGB(255, 255, 0))
LineXY(0, 0, a, b, RGB(255, 192, 0))
LineXY(a / 2, 0, a / 2, b, RGB(255, 0, 0))
For i = 0 To 3 * a / 8
Box(i,(pente * i), 1, 2, RGB(255, 32, 0))
Next i
For i = 5 * a / 8 To a
Box(i,(pente * i), 1, 2, RGB(255, 32, 0))
Next i
StopDrawing()
CreateSprite(#sprBasculeB, a, b) ; B en haut
StartDrawing(SpriteOutput(#sprBasculeB))
DrawingMode(#PB_2DDrawing_Outlined)
Box(0, 0, a, b, RGB(255, 255, 0))
LineXY(0, b, a, 0, RGB(255, 192, 0))
LineXY(a / 2, 0, a / 2, b, RGB(255, 0, 0))
For i = 0 To 3 * a / 8
Box(i,(b - pente * i), 1, 1, RGB(255, 0, 0))
Next i
For i = 5 * a / 8 To a
Box(i,(b - pente * i), 1, 1, RGB(255, 0, 0))
Next i
StopDrawing()
;}
;- sprites personnages A et B
;{
CreateSprite(#sprA, c, c)
StartDrawing(SpriteOutput(#sprA))
DrawingMode(#PB_2DDrawing_Outlined)
Box(0, 0, c, c, RGB(0, 255, 0))
StopDrawing()
CreateSprite(#sprB, c, c)
StartDrawing(SpriteOutput(#sprB))
DrawingMode(#PB_2DDrawing_Outlined)
Box(0, 0, c, c, RGB(0, 0, 255))
StopDrawing()
;}
;- état initial
xA = xBascule
yA = yBascule - (1 - b / a) * c ; prise en compte de l'inclinaison de la planche
xB = xBascule + a - c
yB = yBascule + b - c
;-
;-BOUCLE PRINCIPALE
Repeat
;{
;- gestion clavier
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Left)
If xBascule > 0
xBascule = xBascule - dxBascule
If vol <>#volA
xA = xA - dxBascule
EndIf
If vol <>#volB
xB = xB - dxBascule
EndIf
EndIf
EndIf
If KeyboardPushed(#PB_Key_Right)
If xBascule + a < L
xBascule = xBascule + dxBascule
If vol <>#volA
xA = xA + dxBascule
EndIf
If vol <>#volB
xB = xB + dxBascule
EndIf
EndIf
EndIf
If KeyboardPushed(#PB_Key_Space)
;- Démarrage
If vol = #volNull
vol =#volB
xB = xA + a - c
yB = yA
vxB = 0 * Sin(alpha); vxB=0 !
vyB = -80 * Cos(alpha)
sprBascule = #sprBasculeB
; xA=xA
yA = yBascule + b - c
EndIf ; vol=#volNull
EndIf ; KeyboardPushed(#PB_Key_Space)
;}
; mise à jour de la position du centre de la bascule
xCentreBascule = xBascule + a * 0.5
;- Animation
;{
If vol =#volA
yAprecedent = yA
chuteLibre(xA, yA, vxA, vyA)
If vyA > 0 ; le sprite A redescend
If (xA >= xBascule) And (xA <= xBascule + 3 * a / 8 - c)
If (yA + c) >= yBascule And (yAprecedent + c) <= yBascule
; replacement de A au moment du choc en fonction de sa distance par rapport à l'axe
yA = yCentreBascule - pente * (xCentreBascule - (xA + c)) - c
; initialisation pour le départ de B
vol =#volB
; Calcul de |vB> en appliquant les lois de la mécanique
; calcul des rayons
rayon(rA,(xA + c / 2),(yA + c), xCentreBascule, yCentreBascule)
rayon(rB,(xB + c / 2),(yB + c), xCentreBascule, yCentreBascule)
; Calcul du moment d'inertie total : Jtotal=mA*rA²+mB*rB²+Jbascule
jtotal = mA * rA * rA + mB * rB * rB + jbascule
omega = mA * ((xA + c / 2 - xCentreBascule) * vyA - (yA + c - yCentreBascule) * vxA) / jtotal
; on place B en position haute
yB = yCentreBascule - pente * (xB - xCentreBascule) - c
vB = Abs(rB * omega * 2.5) ; 2,5 facteur arbitraire qui modélise l'impulsion donnée par le sauteur
vxB = -vB * Sin(alpha)
vyB = -vB * Cos(alpha)
; replacement de A au moment du départ de B en fonction de sa distance par rapport à l'axe
yA = yBascule + b - pente * (xA - xBascule) - c
sprBascule = #sprBasculeB
EndIf ; test sur yA+c=yApiedDroit et la position précedente du pied droit
EndIf ; test xA
EndIf ; vyA>0
If yA > H
Delay(1000)
End
EndIf
EndIf ; vol=#volPop
If vol =#volB
yBprecedent = yB
chuteLibre(xB, yB, vxB, vyB)
If vyB > 0 ; le sprite B redescend
If (xB >= xBascule + 5 * a / 8) And (xB <= xBascule + a)
If (yB + c) >= yBascule And (yBprecedent + c) <= yBascule
; replacement de B au moment du choc en fonction de sa distance par rapport à l'axe
yB = yCentreBascule - pente * (xB - xCentreBascule) - c
; initialisation pour le départ de A
vol =#volA
; Calcul de |vA> en appliquant les lois de la mécanique
; Calcul du moment d'inertie total : Jtotal=mA*rA²+mB*rB²+Jbascule
; calcul des rayons
rayon(rA,(xA + c / 2),(yA + c), xCentreBascule, yCentreBascule)
rayon(rB,(xB + c / 2),(yB + c), xCentreBascule, yCentreBascule)
jtotal = mA * rA * rA + mB * rB * rB + jbascule
omega = mB * ((xB + c / 2 - xCentreBascule) * vyB - (yB + c - yCentreBascule) * vxB) / jtotal
; on place A en position haute
yA = yBascule + pente * (xA + c - xBascule) - c
vA = Abs(rA * omega * 2.5); 2,5 facteur arbitraire qui modélise l'impulsion donnée par le sauteur
vxA = vA * Sin(alpha)
vyA = -vA * Cos(alpha)
; replacement de B au moment du départ de A en fonction de sa distance par rapport à l'axe
yB = yCentreBascule + pente * (xB + c - xCentreBascule) - c
sprBascule = #sprBasculeA
EndIf ; test sur yB+c=yBpiedGauche et la position précedente du pied gauche
EndIf ; test xB
EndIf ; vyB>0
If yB > H
Delay(1000)
End
EndIf
EndIf ; vol=#volAphr
;}
;- affichage
ClearScreen(RGB(155, 155, 155))
DisplaySprite(sprBascule, xBascule, yBascule)
DisplayTransparentSprite(#sprA, xA, yA)
DisplayTransparentSprite(#sprB, xB, yB)
StartDrawing(ScreenOutput())
DrawText(0, 0, "xA=" + xA + " yA=" + yA + " xB=" + xB + " yB=" + yB)
DrawText(0, 20, "vxA=" + vxA + " vyA=" + vyA + " vA=" + vA + " vxB=" + vxB + " vyB=" + vyB + " vB=" + vB)
DrawText(0, 40, "w=" + omega + " rA=" + rA + " rB=" + rB)
DrawText(0, 60, "xBascule=" + xBascule + " yBascule=" + yBascule + " xC=" + xCentreBascule + " yC=" + yCentreBascule)
StopDrawing()
FlipBuffers()
;}
Until WindowEvent() = #PB_Event_CloseWindow
End
; @@@@@@@@@@@@@@@@
;- @@@@ INFOS @@@@
; @@@@@@@@@@@@@@@@
; Conservation du moment cinétique I*|omega>= |L >
; I moment d'inertie
; omega vitesse angulaire, |v> = |omega> ^|r>
; L moment cinétique avec |L> = |r> ^|p>
; |r> vecteur correspondant à la distance par rapport à l'axe de rotation (ici coordonnées de A ou de B selon le cas)
; |p> quantité de mouvement |p> = m*|v> avec |v> vecteur vitesse
; (xBascule,yBascule) >>> haut gauche bascule
; (xA,yA) >>>A en haut
; (xA,yBascule+b-c) >>>A en bas
; (xA+a-c,yA) >>> B en haut
; (xA+a-c,yBascule+b-c) >>>B en bas
PS : pour le punch, j'ai fait des tests avec le code original, j'espère que je n'ai pas laissé traîner des erreurs
