J'ai ce petit code pour simuler très grossièrement le comportement d'une balle qui rebondit sur un décor aléatoire; Le but n'est pas d'être hyper-exact, mais plutôt d'avoir un truc rapide que l'on pourrait insérer dans un jeu.
Seulement, je bute sur un problème: quand la balle a cessé de rebondir, qu'elle est posée sur le sol, elle n'arrête pas de "tressauter" verticalement (essayez le code, vous verrez de quoi je veux parler).
Malgré tous mes efforts, je n'arrive pas à résoudre le problème sans en faire apparaître d'autres (la balle se met à passer à travers les murs, ou à rester collée aux obstacles, etc..)

Quelqu'un aurait une solution ?
Code : Tout sélectionner
; Author: Kelebrindae
; Date: october, 7, 2010
; PB version: v4.51
; OS: Windows
; ---------------------------------------------------------------------------------------------------------------
; Description:
; ---------------------------------------------------------------------------------------------------------------
; Very simple simulation of a bouncing ball physics
;
; Controls:
; - [Space] : reset ball position
; - Esc : quit
; ---------------------------------------------------------------------------------------------------------------
#BLANKCOLOR = $000001
Structure ball_struct
x.f
y.f
radius.i
elasticity.f
vx.f ; horizontal velocity
vy.f ; vertical velocity
EndStructure
Global NewList ball.ball_struct()
Global *ptrSilhouette.b
Global angle.i, nbStep.i
Global collisionX.f,collisionY.f,normalX.f,normalY.f,VdotN.f,normalForceX.f,normalForceY.f, absVx.f, absVy.f,vxStep.f,vyStep.f
;- --- Procedures ---
EnableExplicit
Procedure storeSilhouette(numImage.i)
Protected *ptrSilhouette,*ptr
Protected i.i,j.i,width.i,height.i
width = ImageWidth(numImage)
height = ImageHeight(numImage)
*ptrSilhouette = AllocateMemory(width * height)
If *ptrSilhouette <> 0
*ptr = *ptrSilhouette
StartDrawing(ImageOutput(numImage))
For j = 0 To height-1
For i = 0 To width-1
If Point(i,j) <> #BLANKCOLOR
PokeB(*ptr,255)
Else
PokeB(*ptr,0)
EndIf
*ptr+1
Next i
Next j
StopDrawing()
EndIf
ProcedureReturn *ptrSilhouette
EndProcedure
; Look up in the "silhouette" memory array if a given pixel is blank or not
Procedure.b checkCollision(xtest.i, ytest.i, levelWidth.i, levelHeight.i)
If xtest < 1 Or ytest < 1 Or xtest > levelWidth-1 Or ytest > levelHeight-1
ProcedureReturn 255
EndIf
ProcedureReturn PeekB(*ptrSilhouette + (ytest*levelWidth) + xtest)
EndProcedure
DisableExplicit
;- --- Main program ---
InitSprite()
InitSprite3D()
InitKeyboard()
;- Window
OpenWindow(0, 0, 0, 800, 600, "Bounce", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0, 0, 800,600, 0, 0, 0,#PB_Screen_SmartSynchronization)
;- Create a sprite for the ball
CreateImage(0,25,25)
StartDrawing(ImageOutput(0))
DrawingMode(#PB_2DDrawing_Gradient)
FrontColor($BBBB00)
BackColor($FFFF00)
CircularGradient(7,8,12)
Circle(12,12,12)
StopDrawing()
SaveImage(0,"ball.bmp")
FreeImage(0)
LoadSprite(0, "ball.bmp", #PB_Sprite_Texture)
DeleteFile("ball.bmp")
CreateSprite3D(0, 0)
;- Create a random level
CreateImage(0,800, 600)
StartDrawing(ImageOutput(0))
Box(0,0,800,600,#BLANKCOLOR)
DrawingMode(#PB_2DDrawing_Gradient)
FrontColor($616161)
BackColor($EAEAEA)
For i = 0 To 30
x=Random(800):y=Random(600):w=Random(25)+10:h=Random(25)+10
LinearGradient(x,y,x+h/2,y+h)
Box(x,y,w, h)
Next i
FrontColor($77777A)
BackColor($CCCCCD)
For i = 0 To 30
x=Random(800):y=Random(600):w=Random(25)+10
CircularGradient(x-(w/2),y-(w/2),w)
Circle(x,y,w)
Next i
DrawingMode(#PB_2DDrawing_Default)
Circle(400,10,50,#BLANKCOLOR)
DrawingMode(#PB_2DDrawing_Outlined)
Box(0,0,800,600,$FFFFFF)
StopDrawing()
SaveImage(0,"decor.bmp")
LoadSprite(999, "decor.bmp", #PB_Sprite_Texture)
DeleteFile("decor.bmp")
CreateSprite3D(999,999)
; Store level's "silhouette" in memory for faster collision check
; (it uses a lot of memory, though)
*ptrSilhouette = storeSilhouette(0)
FreeImage(0)
; Store sinus and cosinus (faster)
Global Dim cosTable.f(360)
Global Dim sinTable.f(360)
For i=0 To 359
cosTable(i) = Cos( Radian(i) )
sinTable(i) = Sin( Radian(i) )
Next i
;- Define ball's properties
AddElement(ball())
ball()\x = 400
ball()\y = 20
ball()\radius = 12
ball()\elasticity = 0.75
ball()\vx = (20 - Random(40))/10
ball()\vy = 0
;- --- Main Loop ---
Repeat
While WindowEvent() : Wend
; If X-velocity or Y-velocity exceeds 1, break movement into N steps where "partial" velocities are smaller then 1 (= normalize)
If ball()\Vx > 1 Or ball()\Vx < -1 Or ball()\Vy > 1 Or ball()\Vy < -1
absVx = Abs(ball()\Vx)
absVy = Abs(ball()\Vy)
If absVx > absVy
nbStep = Int(absVx) + 1
Else
nbStep = Int(absVy) + 1
EndIf
vxStep = ball()\Vx / nbStep
vyStep = ball()\Vy / nbStep
Else
nbStep = 1
vxStep = ball()\Vx
vyStep = ball()\Vy
EndIf
; For each step, check for collision and move the ball
For i = 1 To nbStep
;Check 12 collision points around the ball
For angle = 0 To 330 Step 30
; Get coords for check point at angle [angle]
collisionX = cosTable(angle)
collisionY = sinTable(angle)
; Check collision
If checkCollision( ball()\x + collisionX * ball()\radius, ball()\y + collisionY * ball()\radius ,800,600)
; Get normal to collision
normalX = -collisionX
normalY = -collisionY
; Move ball a bit away from collision
ball()\x + normalX
ball()\y + normalY
; Project velocity onto collision normal vector
VdotN = vxStep * normalX + vyStep * normalY
; Calculate normal force
normalForceX = -2.0 * normalX * VdotN
normalForceY = -2.0 * normalY * VdotN
; Add normal force to velocity vector
vxStep + normalForceX * ball()\elasticity
vyStep + normalForceY * ball()\elasticity
EndIf
Next angle
; Move the ball
ball()\x + vxStep
ball()\y + vyStep
Next i
; Total velocities = partial velocities * number of steps
ball()\vx = vxStep * nbStep
ball()\vy = vyStep * nbStep
; Gravity
If checkCollision( ball()\x, ball()\y + ball()\radius,800,600) = 0
ball()\vy + 0.16
EndIf
; Display screen
Start3D()
; Background
DisplaySprite3D(999,0,0,255)
; Ball
DisplaySprite3D(0,ball()\x-ball()\radius,ball()\y-ball()\radius)
Stop3D()
FlipBuffers()
; Space => reset sim
ExamineKeyboard()
If KeyboardReleased(#PB_Key_Space)
ball()\x = 400
ball()\y = 20
ball()\radius = 12
ball()\elasticity = 0.75
ball()\vx = (20 - Random(40))/10
ball()\vy = 0
EndIf
Until KeyboardPushed(#PB_Key_Escape)
FreeMemory(*ptrSilhouette)
End