FN BounceOnCollide below shows exactly how.
The program is optimised for accuracy, not speed. It could be greatly improved by replacing the slow floating point arithmetic by fixed point.
In many bouncing ball simulations you can get away with a quick and dirty bounce algorithm (just swap the velocity vectors), and this is also demonstrated.
COMPILE ,_dimmedvarsOnly
DIM RECORD ballRec
DIM xP!, yP!, xV!, yV! ' position and velocity
DIM END RECORD .ballRec
DIM gRect.8, gCollideDistSq!
_bSize=170
END GLOBALS
LOCAL FN BounceOnCollide(b1Ptr&, b2Ptr&)
'A bounce conserves x- and y-momentum, and also
' the energy (sum of the square of all velocity components)
DIM sine!, cosine!, dV!, dVx!, dVy!, distSq!, dX!, dY!, hypotenuse!
dX!=b1Ptr&.xP! - b2Ptr&.xP!
dY!=b1Ptr&.yP! - b2Ptr&.yP!
distSq!=dX!*dX!+dY!*dY!
LONG IF distSq!< GCOLLIDEDISTSQ! ' collided
LONG IF FN BUTTON
hypotenuse!=SQR(dX!*dX! + dY!*dY!)
' calculate sin and cos of angle of the line between centres
cosine!=dX!/hypotenuse!
sine!=dY!/hypotenuse!
dV!=(b1Ptr&.xV!-b2Ptr&.xV!)*cosine!+(b1Ptr&.yV!-b2Ptr&.yV!)*sine!
dVx!=dV!*cosine! ' x velocity change
dVy!=dV!*sine! ' y velocity change
b1Ptr&.xV!=b1Ptr&.xV!-dVx!
b2Ptr&.xV!=b2Ptr&.xV!+dVx!
b1Ptr&.yV!=b1Ptr&.yV!-dVy!
b2Ptr&.yV!=b2Ptr&.yV!+dVy!
XELSE ' quick and dirty
SWAP b1Ptr&.xV!,b2Ptr&.xV!
SWAP b1Ptr&.yV!,b2Ptr&.yV!
END IF
DIM energy!
energy!=b1Ptr&.xV!^2+b2Ptr&.xV!^2+b1Ptr&.yV!^2+b2Ptr&.yV!^2
CALL TEXTMODE(_srcCopy): COLOR=_zBlack: PRINT @(0,0) energy!
END IF
END FN
LOCAL FN DrawBall(bPtr&,colour)
DIM rect.8
CALL SETRECT(rect,bPtr&.xP!,bPtr&.yP!,bPtr&.xP!+_bSize,bPtr&.yP!+_bSize)
COLOR=colour: CALL FRAMEOVAL(rect)
END FN
LOCAL FN MoveBall(bPtr&,colour,otherBPtr&)
FN DrawBall(bPtr&,_zWhite) 'erase
bPtr&.xP!=bPtr&.xP!+bPtr&.xV! ' new position
bPtr&.yP!=bPtr&.yP!+bPtr&.yV!
LONG IF bPtr&.xP! < gRect.left 'hit left wall
bPtr&.xP!=gRect.left: bPtr&.xV!=-bPtr&.xV!
END IF
LONG IF bPtr&.xP! > gRect.right 'hit right
bPtr&.xP!=gRect.right: bPtr&.xV!=-bPtr&.xV!
END IF
LONG IF bPtr&.yP! < GRECT.TOP 'hit top
bPtr&.yP!=gRect.top: bPtr&.yV!=-bPtr&.yV!
END IF
LONG IF bPtr&.yP!>gRect.bottom 'hit bottom
bPtr&.yP!=gRect.bottom: bPtr&.yV!=-bPtr&.yV!
END IF
FN BounceOnCollide(bPtr&, otherBPtr&)
FN DrawBall(bPtr&,colour) 'redraw
END FN
WINDOW 1,"Bouncers (Cmd-. to end)",(0,0)-(630,420),_docNoGrow
DIM ball1.ballRec, ball2.ballRec, ticks&
gCollideDistSq!=_bSize*_bSize
CALL SETRECT(gRect,10,20,620,410)
CALL FRAMERECT (gRect)
PRINT@(20,0) "Press mouse button for best physics"
CALL INSETRECT(gRect,_bSize/2+1,_bSize/2+1)
CALL OFFSETRECT(gRect,-_bSize/2,-_bSize/2)
ball1.xP!=40: ball1.yP!=50 ' initial positions
ball2.xP!=40+_bSize: ball2.yP!=50+15*_bSize/16
ball1.xV!=0.7 : ball1.yV!=3.7 'initial velocities
ball2.xV!=4.7: ball2.yV!=2.9
FN DrawBall(@ball1,_zRed)
FN DrawBall(@ball2,_zBlue)
DO
CALL PENSIZE(4,4)
ticks&=FN TICKCOUNT
FN MoveBall(@ball1,_zRed,@ball2)
FN MoveBall(@ball2,_zBlue,@ball1)
HANDLEEVENTS
DO
UNTIL FN TICKCOUNT> ticks&
UNTIL 0