For real objects elastic collision emulation (billiards, pinball...) this formule is the one which must be used:
(I've had there in Amiga BlitzBasic2, and i've translated to PB)
Code: Select all
; This example shows the way to make a "perfect elastic collision" between 2 spheric (with masses
;centre same as geometrical centre) objects in the emptyness (no rub).
;Perfect elastic collision in a closed system (closed system means no external forces but only the
;two ones of the both colliding objects forces) mean no lossing kinetic energy from the closed system.
;It is ideal, because in the reality there are always a kinetic energy lossing in rub, deformation, rotation,
;heat, etc.
; NOTE: you can add external forces (like gravities between objects, or absolute to all system)
;playing with dirX and dirY parameters.
; 2003-12-25 (Psychophanta) (translated and updated from Amiga BlitzBasic2)
;-INITS:
#bitplanes=32:#RX=1024:#RY=768:#PI=3.14159265
If InitMouse()=0 Or InitSprite()=0 Or InitKeyboard()=0
MessageRequester("Error","Can't open DirectX",0)
End
EndIf
If OpenScreen(#RX,#RY,#bitplanes,"Balls")=0:End:EndIf
CreateSprite(0,100,100)
StartDrawing(SpriteOutput(0))
BackColor(0,0,0)
For t.l=50 To 1 Step -1:Circle(50,50,t,RGB(220-t*2,220-t*2,220-t*2)):Next
StopDrawing()
CreateSprite(1,128,128)
StartDrawing(SpriteOutput(1))
BackColor(0,0,0)
For t.l=64 To 1 Step -1:Circle(64,64,t,RGB(200-t*2,240-t*2,240-t*2)):Next
StopDrawing()
TransparentSpriteColor(0,0,0,0)
TransparentSpriteColor(1,0,0,0)
CursorCentreX.w=SpriteWidth(0)/2:CursorCentreY.w=SpriteHeight(0)/2;<-centro de Cursor
BallCentreX.w=SpriteWidth(1)/2:BallCentreY.w=SpriteHeight(1)/2;<-centro de Ball
CursorDimension.w=(SpriteWidth(0)+SpriteHeight(0))/2
BallDimension.w=(SpriteWidth(1)+SpriteHeight(1))/2
CursorMass.f=4*#PI*Pow(CursorDimension.w/2,3)/3;<-masa de Cursor
BallMass.f=4*#PI*Pow(BallDimension.w/2,3)/3;<-masa de Ball
mouseX=Random(#RX):mouseY=Random(#RY);<-Posición inicial de Cursor
BallX.f=Random(#RX):BallY.f=Random(#RY);<-Posición inicial de Ball
CursorX.f=mouseX:CursorY.f=mouseY
MouseLocate(mouseX,mouseY);<-Posicionamiento del Cursor
dirCursorX.f=Random(600000)/100000+1:dirCursorY.f=Random(600000)/100000+1
dirBallX.f=Random(600000)/100000+1:dirBallY.f=Random(600000)/100000+1
;-MAIN:
Repeat
ExamineKeyboard()
ExamineMouse()
ClearScreen(0,0,0)
; Cursor position and vector:
prevCursorX.f=CursorX.f:prevCursorY.f=CursorY.f;<-Coordenadas anteriores de Cursor
CursorX.f=MouseX():CursorY.f=MouseY();<-Coordenadas actuales de Cursor
dirCursorX.f=CursorX.f-prevCursorX.f:dirCursorY.f=CursorY.f-prevCursorY.f;<-vector director del movimiento de Cursor
; Ball-moving
BallX.f+dirBallX.f:BallY.f+dirBallY.f;<-Coordenadas actuales de Ball (se suma a las anteriores el vector director)
; Ball-Screen limits:
If BallX.f<=-BallCentreX:dirBallX.f=Abs(dirBallX.f):EndIf
If BallX.f+BallCentreX>=#RX:dirBallX.f=-Abs(dirBallX.f):EndIf
If BallY.f<=-BallCentreY:dirBallY.f=Abs(dirBallY.f):EndIf
dirBallY.f+0.1;<-----------------gravity force to down
If BallY.f+BallCentreY>=#RY:dirBallY.f=-Abs(dirBallY.f)+0.1:EndIf;<---------bounce
; Ball-Cursor collision
If SpritePixelCollision(0,CursorX.f,CursorY.f,1,BallX.f,BallY.f);Si hay colisión:
Gosub Shock
;Reposicionamos el cursor:
CursorX.f+dirCursorX.f:CursorY.f+dirCursorY.f
Gosub PreserveDistance
MouseLocate(CursorX.f,CursorY.f)
EndIf
DisplayTransparentSprite(1,BallX.f,BallY.f)
DisplayTransparentSprite(0,CursorX.f,CursorY.f)
FlipBuffers()
Until KeyboardPushed(#PB_Key_All)
CloseScreen()
End
;-SUBROUTINES:
Shock:
;*;al momento de chocar tenemos:
;ENTRADAS: Vectores directores de los movimientos ((dirCursorX,dirCursorY) y (dirBallX,dirBallY))
; Masas de Ball y de Cursor (CursorMass y BallMass)
;*;Debemos calcular:
;SALIDAS: Nuevo vector director de los movimientos de Ball y de Cursor ((dirCursorX,dirCursorY) y (dirBallX,dirBallY)).
;*;COMPONENTES POR CONTACTO:
;Es un vector que está en la lÃnea de choque (recta normal a la recta tangente, en el punto de contacto, sobre la superficie contra la que se choca).
;Obtenemos su sentido y dirección, que viene marcado por la diferencia de coordenadas de cada objeto al momento de chocar:
DiffX.f=BallX.f+BallCentreX-CursorX.f-CursorCentreX:DiffY.f=BallY.f+BallCentreY-CursorY.f-CursorCentreY;<-Vector de contacto Cursor->Ball
;su módulo depende de la componente de la cantidad de movimiento con respecto a la linea de choque (DiffX,DiffY) (proyección del vector (dirX,dirY) sobre el (DiffX,DiffY)):
K.f=(DiffX.f*dirCursorX.f+DiffY.f*dirCursorY.f)/(DiffX.f*DiffX.f+DiffY.f*DiffY.f);<-constante de proyección del actual vector del movimiento de Cursor sobre la lÃnea de choque
dirCursorXK.f=K.f*DiffX.f:dirCursorYK.f=K.f*DiffY.f;<-vector componente en lÃnea de choque de la velocidad de Cursor.
K.f=(-DiffX.f*dirBallX.f-DiffY.f*dirBallY.f)/(-DiffX.f*-DiffX.f+-DiffY.f*-DiffY.f);<-constante de proyección del actual vector del movimiento de Ball sobre la lÃnea de choque.
dirBallXK.f=K.f*-DiffX.f:dirBallYK.f=K.f*-DiffY.f;<-vector componente en lÃnea de choque de la velocidad de Ball.
CX.f=(2*BallMass.f*dirBallXK.f+CursorMass.f*dirCursorXK.f-BallMass.f*dirCursorXK.f)/(CursorMass.f+BallMass.f)
CY.f=(2*BallMass.f*dirBallYK.f+CursorMass.f*dirCursorYK.f-BallMass.f*dirCursorYK.f)/(CursorMass.f+BallMass.f)
BX.f=(2*CursorMass.f*dirCursorXK.f+BallMass.f*dirBallXK.f-CursorMass.f*dirBallXK.f)/(CursorMass.f+BallMass.f)
BY.f=(2*CursorMass.f*dirCursorYK.f+BallMass.f*dirBallYK.f-CursorMass.f*dirBallYK.f)/(CursorMass.f+BallMass.f)
;*;Ahora obtener el vector resultante para el movimiento de Ball:
;Se mantiene la componente perpendicular a la linea de choque del movimiento de Ball (componente que no afecta al choque):
dirBallX.f-dirBallXK.f+BX.f:dirBallY.f-dirBallYK.f+BY.f;<-se suman, obteniendo el vector buscado.
;*;Finalmente obtener el vector director resultante para el movimiento de Cursor:
;Se mantiene la componente perpendicular a la linea de choque del movimiento de Cursor (componente que no afecta al choque):
dirCursorX.f-dirCursorXK.f+CX.f:dirCursorY.f-dirCursorYK.f+CY.f;<-se suman, obteniendo el vector buscado.
Return
PreserveDistance:
Distance.f=Sqr(DiffX.f*DiffX.f+DiffY.f*DiffY.f)
Clip.f=(CursorDimension/2+BallDimension/2)-Distance.f
If Clip.f>0:Clip.f/2
BallX.f+DiffX.f*Clip.f/Distance.f
BallY.f+DiffY.f*Clip.f/Distance.f
CursorX.f-DiffX.f*Clip.f/Distance.f
CursorY.f-DiffY.f*Clip.f/Distance.f
EndIf
Return
Code: Select all
; This example shows the way to make a "perfect elastic collision" between 2 spheric (with masses
;centre same as geometrical centre) objects in the emptyness (no rub).
;Perfect elastic collision in a closed system (closed system means no external forces but only the
;two ones of the both colliding objects forces) mean no lossing kinetic energy from the closed system.
;It is ideal, because in the reality there are always a kinetic energy lossing in rub, deformation, rotation,
;heat, etc.
; NOTE: decrease #lines constant and/or number of objects if your computer is too slow.
; NOTE2: you can add external forces (like gravities between objects, or absolute to all)
;playing with dirX and dirY parameters.
; 2003-12-25 (Psychophanta) (translated and updated from Amiga BlitzBasic2)
;-INITS:
#bitplanes=32:#RX=1024:#RY=768
#BallsDiameter=76
#lines=4
#PI=3.14159265
If InitMouse()=0 Or InitSprite()=0 Or InitKeyboard()=0
MessageRequester("Error","Can't open DirectX",0)
End
EndIf
Structure balls
sprite.w;<-#Sprite
x.f:y.f;<-coordenadas instantáneas
dirX.f:dirY.f;<-Vector del movimiento (velocidad)
CentreX.w:CentreY.w;<-centro geométrico
Dimension.w
Mass.f;<-masa
Kinetic.f;<-energÃa cinética
EndStructure
NewList Capsule.balls():Cursor.balls
;-FUNCTIONS:
Procedure Shock(*a.balls)
;*;al momento de chocar tenemos:
;ENTRADAS: Vectores directores de los movimientos ((dirX,dirY) para Objeto0 y (dirX,dirY) para Objeto1)
; Masas de Objeto1 y de Objeto0 (Objeto0 Mass y Objeto1 Mass)
;*;Debemos calcular:
;SALIDAS: Nuevo vector director de los movimientos de Objeto1 y de Objeto0 ((dirX,dirY) de Objeto0 y (dirX,dirY) de Objeto1).
;*;COMPONENTES POR CONTACTO:
;Es un vector que está en la lÃnea de choque (recta normal a la recta tangente, en el punto de contacto, sobre la superficie contra la que se choca).
;Obtenemos su sentido y dirección, que viene marcado por la diferencia de coordenadas de cada objeto al momento de chocar:
DiffX.f=Capsule()\x+Capsule()\CentreX-*a\x-*a\CentreX:DiffY.f=Capsule()\y+Capsule()\CentreY-*a\y-*a\CentreY;<-Vector de distancia
;su módulo depende de la componente de la velocidad con respecto a la linea de choque (DiffX,DiffY) (proyección del vector (dirX,dirY) sobre el (DiffX,DiffY)):
K.f=(DiffX.f**a\dirX+DiffY.f**a\dirY)/(DiffX.f*DiffX.f+DiffY.f*DiffY.f);<-constante de proyección del actual vector del movimiento de Cursor sobre la lÃnea de choque.
dirCursorXK.f=K.f*DiffX.f:dirCursorYK.f=K.f*DiffY.f;<-vector componente en lÃnea de choque de la velocidad de Cursor.
K.f=(-DiffX.f*Capsule()\dirX-DiffY.f*Capsule()\dirY)/(-DiffX.f*-DiffX.f+-DiffY.f*-DiffY.f);<-constante de proyección del actual vector del movimiento de Ball sobre la lÃnea de choque.
dirBallXK.f=K.f*-DiffX.f:dirBallYK.f=K.f*-DiffY.f;<-vector componente en lÃnea de choque de la velocidad de Ball.
CX.f=(2*Capsule()\Mass*dirBallXK.f+*a\Mass*dirCursorXK.f-Capsule()\Mass*dirCursorXK.f)/(*a\Mass+Capsule()\Mass)
CY.f=(2*Capsule()\Mass*dirBallYK.f+*a\Mass*dirCursorYK.f-Capsule()\Mass*dirCursorYK.f)/(*a\Mass+Capsule()\Mass)
BX.f=(2**a\Mass*dirCursorXK.f+Capsule()\Mass*dirBallXK.f-*a\Mass*dirBallXK.f)/(*a\Mass+Capsule()\Mass)
BY.f=(2**a\Mass*dirCursorYK.f+Capsule()\Mass*dirBallYK.f-*a\Mass*dirBallYK.f)/(*a\Mass+Capsule()\Mass)
;*;Ahora obtener el vector resultante para el movimiento de Ball:
;Se mantiene la componente perpendicular a la linea de choque del movimiento de Ball (componente que no afecta al choque):
Capsule()\dirX-dirBallXK.f+BX.f:Capsule()\dirY-dirBallYK.f+BY.f;<-se suman, obteniendo el vector buscado.
;*;Finalmente obtener el vector director resultante para el movimiento de Cursor:
;Se mantiene la componente perpendicular a la linea de choque del movimiento de Cursor (componente que no afecta al choque):
*a\dirX-dirCursorXK.f+CX.f:*a\dirY-dirCursorYK.f+CY.f;<-se suman, obteniendo el vector buscado.
;*;Preservar distancia:
Distance.f=Sqr(DiffX*DiffX+DiffY*DiffY)
Clip.f=(*a\Dimension/2+Capsule()\Dimension/2)-Distance.f
If Clip.f>0:Clip.f/2
Capsule()\x+DiffX.f*Clip.f/Distance.f
Capsule()\y+DiffY.f*Clip.f/Distance.f
*a\x-DiffX.f*Clip.f/Distance.f
*a\y-DiffY.f*Clip.f/Distance.f
EndIf
EndProcedure
Procedure CreateBallSprite(c.l,size.l,color.l)
CreateSprite(c,size,size)
StartDrawing(SpriteOutput(c))
BackColor(0,0,0):R.w=color&$FF:G.w=color>>8&$FF:B.w=color>>16&$FF
For t.l=size/2 To 1 Step -1
R+160/size:G+160/size:B+160/size:If R>255:R=255:EndIf:If G>255:G=255:EndIf:If B>255:B=255:EndIf
Circle(size/2,size/2,t,RGB(R,G,B))
Next
StopDrawing()
EndProcedure
;-MOREINITS:
If OpenScreen(#RX,#RY,#bitplanes,"Balls")=0:End:EndIf
Cursor\sprite=0
CreateBallSprite(Cursor\sprite,64,$009eae)
Cursor\CentreX=SpriteWidth(Cursor\sprite)/2:Cursor\CentreY=SpriteHeight(Cursor\sprite)/2;<-centro de Objeto0
Cursor\Dimension=(SpriteWidth(Cursor\sprite)+SpriteHeight(Cursor\sprite))/2
Cursor\Mass=4*#PI*Pow(Cursor\Dimension/2,3)/3;<-masa de Objeto0
t.w=1:g.b=1
BallsPerLine.w=#RX/#BallsDiameter-1;<-Número de Objeto1 por lineas
While g<=#lines
While t<=g*BallsPerLine.w
*prev.balls=@Capsule()
AddElement(Capsule())
Capsule()\sprite=t
CreateBallSprite(Capsule()\sprite,#BallsDiameter,$6dae00)
Capsule()\CentreX=SpriteWidth(Capsule()\sprite)/2:Capsule()\CentreY=SpriteHeight(Capsule()\sprite)/2;<-centro de Objeto1
Capsule()\Dimension=(SpriteWidth(Capsule()\sprite)+SpriteHeight(Capsule()\sprite))/2
Capsule()\Mass=4*#PI*Pow(Capsule()\Dimension/2,3)/3;<-masa de Objeto1
If t%BallsPerLine.w=1:Capsule()\x=Capsule()\Dimension/2:Capsule()\y=g*50;<-Posición inicial de este Objeto1
Else:Capsule()\x=*prev\x+*prev\Dimension/2+Capsule()\Dimension/2:Capsule()\y=g*50;<-Posición inicial de este Objeto1
EndIf
t.w+1
Wend
g.b+1
Wend
mouseX=#RX/2:mouseY=#RY*4/5
Cursor\x=mouseX:Cursor\y=mouseY
MouseLocate(mouseX,mouseY);<-Posición inicial de Objeto0
Cursor\dirX=Random(1000000)/100000-5:Cursor\dirY=-Random(4000000)/1000000+1
;-MAIN:
Repeat
ExamineKeyboard()
ExamineMouse()
ClearScreen(0,0,0)
; Cursor position and vector:
prevmouseX=mouseX:prevmouseY=mouseY;<-previous mouse coordinates
mouseX=MouseX():mouseY=MouseY();<-current mouse coordinates
If mouseX<>prevmouseX Or mouseY<>prevmouseY;If mouse is moved:
Cursor\dirX=mouseX-prevmouseX:Cursor\dirY=mouseY-prevmouseY;<-vector director del movimiento de Cursor
mouseX=Cursor\x+Cursor\dirX:mouseY=Cursor\y+Cursor\dirY;<-actualizamos las coordenadas del mouse
MouseLocate(mouseX,mouseY);<-y reposicionamos el mouse en las actuales coordenadas de Cursor
EndIf
Cursor\x+Cursor\dirX:Cursor\y+Cursor\dirY;<-que son estas (se suma el vector director a las anteriores)
; Cursor-Screen limits:
If Cursor\x<=-Cursor\CentreX:Cursor\dirX=Abs(Cursor\dirX):EndIf
If Cursor\x+Cursor\CentreX>=#RX:Cursor\dirX=-Abs(Cursor\dirX):EndIf
If Cursor\y<=-Cursor\CentreY:Cursor\dirY=Abs(Cursor\dirY):EndIf
;Cursor\dirY+0.1;<-------------Gravity to down
If Cursor\y+Cursor\CentreY>=#RY:Cursor\dirY=-Abs(Cursor\dirY);+0.1;<----bound
EndIf
ForEach Capsule()
; Ball-Screen limits:
If Capsule()\x<=-Capsule()\CentreX:Capsule()\dirX=Abs(Capsule()\dirX):EndIf
If Capsule()\x+Capsule()\CentreX>=#RX:Capsule()\dirX=-Abs(Capsule()\dirX):EndIf
If Capsule()\y<=-Capsule()\CentreY:Capsule()\dirY=Abs(Capsule()\dirY):EndIf
;Capsule()\dirY+0.1;<-------------Gravity to down
If Capsule()\y+Capsule()\CentreY>=#RY:Capsule()\dirY=-Abs(Capsule()\dirY);+0.1;<----bound
EndIf
; Ball-moving:
Capsule()\x+Capsule()\dirX:Capsule()\y+Capsule()\dirY;<-Coordenadas actuales de Ball (se suma a las anteriores el vector director)
; collision:
*i.balls=@Capsule()
While NextElement(Capsule())
If SpritePixelCollision(*i\sprite,*i\x,*i\y,Capsule()\sprite,Capsule()\x,Capsule()\y);Si hay colisión:
Shock(*i)
EndIf
Wend
ChangeCurrentElement(Capsule(),*i)
If SpritePixelCollision(Cursor\sprite,Cursor\x,Cursor\y,Capsule()\sprite,Capsule()\x,Capsule()\y);Si hay colisión:
Shock(@Cursor)
EndIf
DisplayTransparentSprite(Capsule()\sprite,Capsule()\x,Capsule()\y)
Next
DisplayTransparentSprite(Cursor\sprite,Cursor\x,Cursor\y)
FlipBuffers()
Until KeyboardPushed(#PB_Key_All)
CloseScreen()
End