Ball collisions

General FreeBASIC programming questions.
exige
Posts: 3
Joined: Jul 15, 2007 20:52

Ball collisions

Postby exige » Jul 15, 2007 21:12

Hello, I am trying to do a program with bouncing balls that can collide with each other.

The problem is that I don't know how to calculate the collisions in a correct way, so that it would be realistic. If someone could explain it, I would be very happy. I have searched the forum, and found solutions, but I can't understand them, and I don't like having code that I don't understand.

I am also having problem with grabbing one of the set number of balls. The mouse will not get stuck where I click, instead it sticks to the corners when you drag that ball. I can't figure out what's causing it.

If you find any else error or slow/bad code in my program I would be happy to know.

Sorry for my bad English, I come from Sweden. Feel free to correct spelling mistakes as well.

Code: Select all

' Anders Reinholdsson 2007-07-05

option explicit

' Declare variables
dim fps, frames,n, n2, balls = 10, radius(balls), colour(balls), mx, mx2, my, my2, mb, grab
dim as double x(balls), y(balls), vy(balls), vx(balls), time1, time2, g = .01, temp

randomize timer
for n = 0 to balls - 1 ' Randomize things
    x(n) = 640 * rnd
    y(n) = 200 * rnd
    vx(n) = 20 * rnd - 10
    radius(n) = 10 ' rnd * 10 + 10
    colour(n) = rnd * 14 + 1
next

screenres 640,400, 8, 2

' Main loop
do while inkey$ = ""
    getmouse mx, my,, mb
    screenset 1:cls
   
    print "Version 0.017"
    print "FPS:"; fps
    print "Frames:"; frames
    print "Balls:"; balls
   
    for n = 0 to balls - 1
        vy(n) += g ' Changes in y-way
        vy(n) = .999 * vy(n) ' To simulate air-friction
        y(n) += vy(n)
        if y(n) >= 400 - radius(n) then
            vy(n) = -.9 * vy(n)
            y(n) = 400 - radius(n)
        end if
        if y(n) <= 0 + radius(n) then
            vy(n) = -.9 * vy(n)
            y(n) = 0 + radius(n)
        end if
   
        if x(n) >= 640 - radius(n) then ' Changes in x-way
            vx(n) = .9 * -vx(n)
            x(n) = 640 - radius(n)
        end if
        if x(n) <= 0 + radius(n) then
            vx(n) = .9 * -vx(n)
            x(n) = 0 + radius(n)
        end if
        vx(n) = .999 * vx(n) ' To simulate air-friction
        x(n) += vx(n)
       
        ' To grab balls
        if mb = 1 then
            if sqr((x(n)-mx)^2+(y(n)-my)^2) < radius(n) then
                grab = n
                mx2 = mx - x(n)
                my2 = my - y(n)
            end if
        else
            grab = -1
        end if
        if grab <> -1 then
            x(grab) = mx - mx2
            y(grab) = my - my2
            vx(grab) = 0
            vy(grab) = 0
        end if
       
        ' Detect collisions
        for n2 = 0 to balls - 1
            if n <> n2 and sqr((x(n)-x(n2))^2+(y(n)-y(n2))^2) < radius(n)+radius(n2) then
                temp = vx(n)
                vx(n) = vx(n2)
                vx(n2) = temp
                temp = vy(n)
                vy(n) = vy(n2)
                vy(n2) = temp
            end if
        next
       
        circle (x(n), y(n)),radius(n),colour(n),,,,F
    next

    if timer - time2 > 1 then ' FPS counter
        fps = 1/(timer-time1)
        time2 = timer
    end if
    time1 = timer
   
    flip
    frames += 1
    sleep 9 ' To get about 100 fps
loop

Thanks for any help! :)
Merick
Posts: 1038
Joined: May 28, 2007 1:52

Postby Merick » Jul 15, 2007 22:32

I haven't really tested it with freebasic, but this function was edited from one I used with another language and it should work for your collisions:

Code: Select all

function Circle_Collision(byval obj1x as integer,_
                          byval obj1y as integer,_
                          byval obj1radius as integer,_
                          byval obj2x as integer,_
                          byval obj2y as integer,_
                          byval obj2radius as integer) as integer

    var distanceSquared = (obj2x - obj1x) * (obj2x - obj1x) + (obj2y - obj1y) * (obj2y - obj1y)
    var radiusSquared = (obj2radius + obj1radius) * (obj2radius + obj1radius)

    if (distanceSquared < radiusSquared) then
        return true
    else
        return false
    endif
end function


obj1x, obj1y, and obj1radius are the x,y position and the radius of the first ball, obj2x, obj2y, and obj2radius are the x,y position and radius of the ball you want to check it against.
h4tt3n
Posts: 638
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Jul 16, 2007 8:11

@ Merick,

I think what exige means is that he's having trouble figuring out how the balls should interact on collision, not the actual collision detection itself (which is fairly straightforward).

@ exige,

I know for sure that there is a plane and simple tutorial about this subject on the net somewhere. I'll take a look for it and post a link if I find anything.
Richard
Posts: 2753
Joined: Jan 15, 2007 20:44
Location: Australia

Postby Richard » Jul 16, 2007 12:36

You have two laws of physics, conservation of energy and conservation of momentum. The total energy after collision is equal to the total energy before collision. The total momentum after collision is equal to the total momentum before collision.
Momentum = mass * velocity
Kinetic energy = 0.5 * mass * velocity * velocity
Solve the simultaneous equations which will probably have a quadratic equation. Since velocity is a vector you need to consider these conservation laws in both the x and y directions.
This is a particularly difficult problem if the balls do not hit head-on. Also if the balls roll up to each other or spin as a result of an off-centre collision it is harder still. Rolling or spinning are conserved as angular energy and angular momentum.
Look for the sticky link at the top of Libraries on this forum because you might be better using the ODE library http://ode.org/ Or Newton http://www.physicsengine.com/

Here is some code to handle the general case, in C or Fortran.
http://www.plasmaphysics.org.uk/programs/coll2d_cpp.htm
http://www.plasmaphysics.org.uk/collision2d.htm
Here is an UNtested translation to FB from Fortran. No warranty.

Code: Select all

' ******************************************************************************
' This program is a 'remote' 2D-collision detector for two balls on linear
' trajectories and returns, if applicable, the location of the collision for
' both balls as well as the new velocity vectors (assuming a fully elastic
' collision).
' All variables apart from 'ErrorCode' are of Double Precision Floating Point type.
' In 'free' mode no positions but only the initial velocities and an impact
' angle are required.
' All variables apart from 'mode' and 'ErrorCode' are of Double Precision
' Floating Point type.
'
' The Parameters are:
'
' mode  (character*4) (if='free' alpha must be supplied; otherwise arbitrary)
' alpha (impact angle) only required in mode='free';
' should be between -PI/2 and PI/2 (0 = head-on collision))
' m1    (mass of ball 1)
' m2    (mass of ball 2)
' r1    (radius of ball 1)       not needed for 'free' mode
' r2    (radius of ball 2)                "
' * x1    (x-coordinate of ball 1)          "
' * y1    (y-coordinate of ball 1)          "
' * x2    (x-coordinate of ball 2)          "
' * y2    (y-coordinate of ball 2)          "
' * vx1   (velocity x-component of ball 1)
' * vy1   (velocity y-component of ball 1)
' * vx2   (velocity x-component of ball 2)
' * vy2   (velocity y-component of ball 2)
' * ErrorCode (Integer*2) (0: no error
' 1: balls do not collide
' 2: initial positions impossible (balls overlap)
'
' In the calling program, the arguments corresponding to the parameters
' with an asterisk (*) will be updated (the positions and velocities however
' only if 'ErrorCode'=0).
' All variables should have the same data types in the calling program
' and all should be initialized before calling the subroutine.
'
' This program is free to use for everybody. However, you use it at your own
' risk and I do not accept any liability resulting from incorrect behaviour.
' I have tested the program for numerous cases and I could not see anything
' wrong with it but I can not guarantee that it is bug-free under any
' circumstances.
' I do in general not recommend the use of single precision because for
' iterative computations the rounding errors can accumulate and render the
' result useless if there are not enough decimal places; however for uncritical
' applications or if speed is of paramount importance, you can change the
' program to single precision by changing the variable declaration to REAL*4
' and remove the 'D' in front of the mathematical function names.
'
'
' I would appreciate if you could report any problems to me
' (for contact details see  http://www.plasmaphysics.org.uk/feedback.htm ).
'
' Thomas Smid, January  2004
' December 2005 (corrected faulty collision detection;
' a few minor changes to improve speed;
' added simplified code without collision detection)
' **********************************************************************************

Sub collision2D(_
    Byref mode As String,_
    Byref alpha As Double,_
    Byref m1 As Double,_
    Byref m2 As Double,_
    Byref r1 As Double,_
    Byref r2 As Double,_
    Byref x1 As Double,_
    Byref y1 As Double,_
    Byref x2 As Double,_
    Byref y2 As Double,_
    Byref vx1 As Double,_
    Byref vy1 As Double,_
    Byref vx2 As Double,_
    Byref vy2 As Double,_
    Byref ErrorCode As Integer)
   
    ' **** initialize some additional variables ****
    Dim As Double PI2, R12, M21, X21, Y21, VX21, VY21, GAMMAV, D
    Dim As Double GAMMAXY, DGAMMA, DDGAMMA, DR, DC, SQS, T, A, DVX2
    PI2 = 2.0D0*Atn(-1.0D0)
    ErrorCode = 0
    R12 = r1+r2
    M21 = m2/m1
    X21 = x2-x1
    Y21 = y2-y1
    VX21 = vx2-vx1
    VY21 = vy2-vy1
   
    ' **** return if relative velocity =0  ****
    If (vx21  =  0  And  vy21  =  0) Then
        ErrorCode = 1
        Exit Sub
    End If
   
    ' *** calculate relative velocity angle
    GAMMAV = Atan2(-vy21,-vx21)
   
    ' ******** this block only if initial positions are given ***
   
    If (mode  <>  "free") Then
       
        D = Sqr(x21*x21+y21*y21)
       
        ' **** return if distance between balls smaller than sum of radii ***
        If (d  <  r12) Then
            ErrorCode = 2
            Exit Sub
        End If
       
        ' **** calculate relative position angle and normalized impact parameter ***
        GAMMAXY = Atan2(y21,x21)
        DGAMMA = gammaxy-gammav
        If (dgamma  >  pi2) Then
            DGAMMA = dgamma-pi2
        Else
            If (dgamma  <  -pi2) Then
                DGAMMA = dgamma+pi2
            End If
        End If
        DR = d*Sin(dgamma)/r12
       
        ' **** return old positions and velocities if balls do not collide ***
        If ((dgamma > pi2/4.0D0 And ddgamma < 0.75D0*pi2 ) Or Abs(dr) > 1.0D0 ) Then
            ErrorCode = 1
            Exit Sub
        End If
        ' **** calculate impact angle if balls do collide ***
        ALPHA = Asin(dr)
       
        ' **** calculate time to collision ***
        DC = d*Cos(dgamma)
        If (dc  >  0) Then
            SQS = 1.0D0
        Else
            SQS = -1.0D0
        End If
        T = (dc-sqs*r12*Sqr(1-dr*dr))/Sqr(vx21*vx21+vy21*vy21)
       
        ' **** update positions ***
        X1 = x1+vx1*t
        Y1 = y1+vy1*t
        X2 = x2+vx2*t
        Y2 = y2+vy2*t
       
    End If
   
    ' ******** END 'this block only if initial positions are given' ***
   
    ' ***  update velocities ***
   
    A = Tan(gammav+alpha)
   
    DVX2 = -2*(vx21+a*vy21)/((1+a*a)*(1+m21))
   
    VX2 = vx2+dvx2
    VY2 = vy2+a*dvx2
    VX1 = vx1-m21*dvx2
    VY1 = vy1-a*m21*dvx2
   
End Sub


' ******************************************************************************
' Simplified Version
' The advantage of the 'remote' collision detection in the program above is
' that one does not have to continuously track the balls to detect a collision.
' The program needs only to be called once for any two balls unless their
' velocity changes. However, if somebody wants to use a separate collision
' detection routine for whatever reason, below is a simplified version of the
' code which just calculates the new velocities, assuming that the balls are
' already touching and approaching each other (these two conditions are
' important as otherwise the results will be incorrect)
' ****************************************************************************

Sub collision2Ds(_
    Byref m1 As Double,_
    Byref m2 As Double,_
    Byref x1 As Double,_
    Byref y1 As Double,_
    Byref x2 As Double,_
    Byref y2 As Double,_
    Byref vx1 As Double,_
    Byref vy1 As Double,_
    Byref vx2 As Double,_
    Byref vy2 As Double)
   
    Dim As Double M21, X21, Y21, VX21, VY21, FY21, Sign, A, DVX2
   
    M21 = m2/m1
    X21 = x2-x1
    Y21 = y2-y1
    VX21 = vx2-vx1
    VY21 = vy2-vy1
   
    ' *** I have inserted the following statements to avoid a zero divide;
    ' *** (for single precision calculations,
    ' ***         1.0D-12 should be replaced by a larger value). **********
   
    FY21 = 1.0D-12*Abs(y21)
    If ( Abs(x21)  <  fy21 ) Then
        If (x21  <  0) Then
            SIGN = -1.0D0
        Else
            SIGN = 1.0D0
        End If
        X21 = fy21*sign
    End If
   
    ' ***  update velocities ***
   
    A = y21/x21
   
    DVX2 = -2*(vx21+a*vy21)/((1+a*a)*(1+m21))
   
    VX2 = vx2+dvx2
    VY2 = vy2+a*dvx2
    VX1 = vx1-m21*dvx2
    VY1 = vy1-a*m21*dvx2
   
End Sub
exige
Posts: 3
Joined: Jul 15, 2007 20:52

Postby exige » Jul 16, 2007 16:32

Thanks for all your answers!

h4tt3n: You are right. I am very interested if you find that tutorial.

Richard: Yes, I know about the laws. But my real problem is to make the code, and perhaps to make the math general (being able to solve ball collisions for different angels, speeds and masses). Tomorrow I will take a look in my physics books. I am interested in doing the physics calculations on my own, so I don't want to use any external library. The goal with this program was not to make it very advanced, instead simple but still realistic. So therefor I will ignore friction on the balls, as well as rotations.

Anyone figured out my problem with grabbing one ball?
Lachie Dazdarian
Posts: 2230
Joined: May 31, 2005 9:59
Location: Croatia
Contact:

Postby Lachie Dazdarian » Jul 16, 2007 19:14

I'm not sure if you are aware of this tutorial:

http://www.petesqbsite.com/sections/express/issue21/index.html#bounce

It contains this demo of ball to ball collison:

Code: Select all

'Snooker balls bouncing like they should be. ;*)
'Relsoft 2003
'http://rel.betterwebber.com
'thanks to: Hugo Elias for writting that lil tute. Very *vague* I mght add. ;*)
'
'Notes: No friction is implemented as my daughter is... well, I have to cook
'       her breakfast. ;*)
'Ported to FB June 20, 2006

'Keys:
'LEFT and RIGHT arrows = change direction
'SPACE = shoot
'ESC = QUIT

DEFINT A-Z

TYPE ballType
    x       AS SINGLE               'the x coord
    y       AS SINGLE
    xv      AS SINGLE
    yv      AS SINGLE
    speed   AS SINGLE               '???
    mass    AS SINGLE               'the mass
    angle   AS INTEGER              'angle of direction
END TYPE

DECLARE SUB Normalize (nx!, ny!, X1!, Y1!, X2!, Y2!)
DECLARE FUNCTION Collide% (Ball() AS balltype)
DECLARE FUNCTION Dot! (ax!, ay!, bx!, by!)

CONST FALSE = 0, TRUE = NOT FALSE
CONST PI = 3.141593                 'wanna eat pie?
CONST FRICTION = .001



DIM balls(1) AS ballType

'Initialise balls

balls(0).x = 200
balls(0).y = 170
balls(0).xv = 0
balls(0).yv = 0
balls(0).speed = 2.6
balls(0).mass = 1.5
balls(0).angle = 218

balls(1).x = 100
balls(1).y = 80
balls(1).xv = 0
balls(1).yv = 0
balls(1).speed = 0
balls(1).mass = 2.5
balls(1).angle = 30

CLS
SCREEN 13,8,2

screenset 1,0

'Target the ball
DO
       
        LINE (0, 0)-(319, 199), 0, BF       'cls
        FOR i% = 0 TO 1                     'draw em ballz
            x = balls(i%).x
            y = balls(i%).y
            CIRCLE (x, y), 10, i% + 5
            PAINT (x, y), i% + 5
        NEXT i%
        tx% = COS(balls(0).angle * PI / 180) * 50       'Line of target
        ty% = SIN(balls(0).angle * PI / 180) * 50
        LINE (balls(0).x, balls(0).y)-(balls(0).x + tx%, balls(0).y + ty%), 15, , &HFAFA
       
        screensync
        screencopy
       
        IF MULTIKEY(&h4B) then balls(0).angle = (balls(0).angle - 1) MOD 360
        IF MULTIKEY(&h4D) then balls(0).angle = balls(0).angle + 1 MOD 360

       
LOOP WHILE NOT MULTIKEY(&h39)


    'Set initial ball vectors
    balls(0).xv = COS(balls(0).angle * PI / 180) * balls(0).speed
    balls(0).yv = SIN(balls(0).angle * PI / 180) * balls(0).speed

'Bouncy!!!!
dim xva!,xvb!,yva!,yvb!,impulsex!,impulsey!
DO

    LINE (0, 0)-(319, 199), 0, BF           'cls
    FOR i% = 0 TO 1                         'move the balls
        balls(i%).x = balls(i%).x + balls(i%).xv        'x move
        balls(i%).y = balls(i%).y + balls(i%).yv        'y move

        'the next if then else clause checks to see if the balls are
        'inside the screen of out of bounds, they bounce
        IF balls(i%).x < 10 THEN
            balls(i%).xv = -balls(i%).xv
        ELSEIF balls(i%).x > 310 THEN
            balls(i%).xv = -balls(i%).xv
        ELSE
        END IF
        IF balls(i%).y < 10 THEN
            balls(i%).yv = -balls(i%).yv
        ELSEIF balls(i%).y > 190 THEN
            balls(i%).yv = -balls(i%).yv
        ELSE
        END IF
        balls(i%).xv = balls(i%).xv - balls(i%).xv * FRICTION
        balls(i%).yv = balls(i%).yv - balls(i%).yv * FRICTION
        x = balls(i%).x
        y = balls(i%).y
        CIRCLE (x, y), 10, i% + 5       'Draw the balls
        PAINT (x, y), i% + 5
    NEXT i%

    IF Collide(balls()) = -1 THEN
            GOSUB calcVectors           'The ball to ball algo
            balls(0).xv = xva!          'set new velocities/vectors
            balls(0).yv = yva!
            balls(1).xv = xvb!
            balls(1).yv = yvb!
    END IF
    screensync
    screencopy
   
LOOP WHILE NOT MULTIKEY(&h1)


END

calcVectors:
    'Notes:
    'vectors: Impact,Impulse,
    '           balla,ballb,newa,newb
    'Points:    p1,p2

    'Real Floats: Mass1,Mass2,Impactspeed

    'Formula:
    '1. Impact= balla-ballb         'vector subtaction
    '2. impulse = Normalize(p1,p2)  'see normalize sub
    '3. impactspeed = Dot!(impact,impulse)
    '4. newimpulse=impulse*impactspeed*mass1*mass2
    '5. newa=balla+newimpulse/mass1
    '6. newb=ballb-newimpulse/mass2
    'PS the divide by 2 is for the frictionless model. ;*)


    impactx! = balls(1).xv - balls(0).xv
    impacty! = balls(1).yv - balls(0).yv
    Normalize impulsex!, impulsey!, balls(0).x, balls(0).y, balls(1).x, balls(1).y
    impactSpeed! = Dot!(impactx!, impacty!, impulsex!, impulsey!)

    impulsex! = impulsex! * impactSpeed! * balls(0).mass * balls(1).mass / 2
    impulsey! = impulsey! * impactSpeed! * balls(0).mass * balls(1).mass / 2

    xva! = balls(0).xv + impulsex! / balls(0).mass
    yva! = balls(0).yv + impulsey! / balls(0).mass

    xvb! = balls(1).xv - impulsex! / balls(1).mass
    yvb! = balls(1).yv - impulsey! / balls(1).mass
RETURN

FUNCTION Collide (Ball() AS ballType)
'returns id the ball collided using the distance formula.
'can be optimized a lot!!!
    Collide = 0
    dx! = Ball(1).x - Ball(0).x
    dy! = Ball(1).y - Ball(0).y
    dist! = SQR(dx! ^ 2 + dy! ^ 2)
    IF dist! <= 20 THEN
        Collide = -1
    END IF
END FUNCTION

FUNCTION Dot! (ax!, ay!, bx!, by!)
'returns the dot product between 2 vectors a and b.
    Dot! = (ax! * bx!) + (ay! * by!)
END FUNCTION

SUB Normalize (nx!, ny!, X1!, Y1!, X2!, Y2!)
'normalizes the components of a vector derived from 2 points (x1,y1) and
'(x2,y2)

    dx! = X2! - X1!
    dy! = Y2! - Y1!
    dist! = SQR(dx! ^ 2 + dy! ^ 2)
    nx! = dx! / dist!
    ny! = dy! / dist!
END SUB


Compiles with -lang qb. You'll have to do the usual changes to compile it with -lang fb (declare all variables, no variable suffixes, etc.).

Personally, this code is all I need in ball to ball collision. Relsoft is God! :P
integer
Posts: 349
Joined: Feb 01, 2007 16:54
Location: usa

Postby integer » Jul 16, 2007 20:06

commented out: OPTION EXPLICIT
added "AS INTEGER" after the first DIM

results:

Code: Select all

Command executed:
"C:\FreeBASIC\fbc.exe" "C:\FB\bounce1.bas"

Compiler output:
C:\FB\bounce1.o:fake:(.text+0x43a): undefined reference to `fb_GetMouse@20'

Results:
Compilation failed

System:
FBIde: 0.4.6
fbc:   FreeBASIC Compiler - Version 0.18 (06-28-2007) for win32 (target:win32)
OS:    Windows XP (build 2600, Service Pack 2)
'/

At the moment can't investigate.
cha0s
Site Admin
Posts: 5317
Joined: May 27, 2005 6:42
Location: Illinois
Contact:

Postby cha0s » Jul 16, 2007 22:38

Integer: That means you've mismatched compiler and rtlib versions in linked objects.
Basic Coder
Posts: 180
Joined: Aug 02, 2006 23:37
Location: Australia

Postby Basic Coder » Jul 16, 2007 22:42

exige wrote:
from previous post:
"I am also having problem with grabbing one of the set
number of balls. The mouse will not get stuck where I
click, instead it sticks to the corners when you drag
that ball. I can't figure out what's causing it."

Anyone figured out my problem with grabbing one ball?



It seems to work ok for me unless I misunderstand the
problem? If the mouse-held ball is moved passed another
it often swaps balls, perhaps something to do with
the order of the balls in the ball list. I haven't had
time to study your code yet.

One neat feature might be to pick up a ball and when
you release the ball have it retain the velocity
(speed and direction) of the mouse before release?
So you can throw the balls using the mouse?

--
JC
h4tt3n
Posts: 638
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Jul 18, 2007 18:33

Lachie Dazdarian wrote:I'm not sure if you are aware of this tutorial:

http://www.petesqbsite.com/sections/express/issue21/index.html#bounce

It contains this demo of ball to ball collison:

Code: Select all

'Snooker balls bouncing like they should be. ;*)
'Relsoft 2003
'http://rel.betterwebber.com
'thanks to: Hugo Elias for writting that lil tute. Very *vague* I mght add. ;*)
'
'Notes: No friction is implemented as my daughter is... well, I have to cook
'       her breakfast. ;*)
'Ported to FB June 20, 2006

'Keys:
'LEFT and RIGHT arrows = change direction
'SPACE = shoot
'ESC = QUIT

DEFINT A-Z

TYPE ballType
    x       AS SINGLE               'the x coord
    y       AS SINGLE
    xv      AS SINGLE
    yv      AS SINGLE
    speed   AS SINGLE               '???
    mass    AS SINGLE               'the mass
    angle   AS INTEGER              'angle of direction
END TYPE

DECLARE SUB Normalize (nx!, ny!, X1!, Y1!, X2!, Y2!)
DECLARE FUNCTION Collide% (Ball() AS balltype)
DECLARE FUNCTION Dot! (ax!, ay!, bx!, by!)

CONST FALSE = 0, TRUE = NOT FALSE
CONST PI = 3.141593                 'wanna eat pie?
CONST FRICTION = .001



DIM balls(1) AS ballType

'Initialise balls

balls(0).x = 200
balls(0).y = 170
balls(0).xv = 0
balls(0).yv = 0
balls(0).speed = 2.6
balls(0).mass = 1.5
balls(0).angle = 218

balls(1).x = 100
balls(1).y = 80
balls(1).xv = 0
balls(1).yv = 0
balls(1).speed = 0
balls(1).mass = 2.5
balls(1).angle = 30

CLS
SCREEN 13,8,2

screenset 1,0

'Target the ball
DO
       
        LINE (0, 0)-(319, 199), 0, BF       'cls
        FOR i% = 0 TO 1                     'draw em ballz
            x = balls(i%).x
            y = balls(i%).y
            CIRCLE (x, y), 10, i% + 5
            PAINT (x, y), i% + 5
        NEXT i%
        tx% = COS(balls(0).angle * PI / 180) * 50       'Line of target
        ty% = SIN(balls(0).angle * PI / 180) * 50
        LINE (balls(0).x, balls(0).y)-(balls(0).x + tx%, balls(0).y + ty%), 15, , &HFAFA
       
        screensync
        screencopy
       
        IF MULTIKEY(&h4B) then balls(0).angle = (balls(0).angle - 1) MOD 360
        IF MULTIKEY(&h4D) then balls(0).angle = balls(0).angle + 1 MOD 360

       
LOOP WHILE NOT MULTIKEY(&h39)


    'Set initial ball vectors
    balls(0).xv = COS(balls(0).angle * PI / 180) * balls(0).speed
    balls(0).yv = SIN(balls(0).angle * PI / 180) * balls(0).speed

'Bouncy!!!!
dim xva!,xvb!,yva!,yvb!,impulsex!,impulsey!
DO

    LINE (0, 0)-(319, 199), 0, BF           'cls
    FOR i% = 0 TO 1                         'move the balls
        balls(i%).x = balls(i%).x + balls(i%).xv        'x move
        balls(i%).y = balls(i%).y + balls(i%).yv        'y move

        'the next if then else clause checks to see if the balls are
        'inside the screen of out of bounds, they bounce
        IF balls(i%).x < 10 THEN
            balls(i%).xv = -balls(i%).xv
        ELSEIF balls(i%).x > 310 THEN
            balls(i%).xv = -balls(i%).xv
        ELSE
        END IF
        IF balls(i%).y < 10 THEN
            balls(i%).yv = -balls(i%).yv
        ELSEIF balls(i%).y > 190 THEN
            balls(i%).yv = -balls(i%).yv
        ELSE
        END IF
        balls(i%).xv = balls(i%).xv - balls(i%).xv * FRICTION
        balls(i%).yv = balls(i%).yv - balls(i%).yv * FRICTION
        x = balls(i%).x
        y = balls(i%).y
        CIRCLE (x, y), 10, i% + 5       'Draw the balls
        PAINT (x, y), i% + 5
    NEXT i%

    IF Collide(balls()) = -1 THEN
            GOSUB calcVectors           'The ball to ball algo
            balls(0).xv = xva!          'set new velocities/vectors
            balls(0).yv = yva!
            balls(1).xv = xvb!
            balls(1).yv = yvb!
    END IF
    screensync
    screencopy
   
LOOP WHILE NOT MULTIKEY(&h1)


END

calcVectors:
    'Notes:
    'vectors: Impact,Impulse,
    '           balla,ballb,newa,newb
    'Points:    p1,p2

    'Real Floats: Mass1,Mass2,Impactspeed

    'Formula:
    '1. Impact= balla-ballb         'vector subtaction
    '2. impulse = Normalize(p1,p2)  'see normalize sub
    '3. impactspeed = Dot!(impact,impulse)
    '4. newimpulse=impulse*impactspeed*mass1*mass2
    '5. newa=balla+newimpulse/mass1
    '6. newb=ballb-newimpulse/mass2
    'PS the divide by 2 is for the frictionless model. ;*)


    impactx! = balls(1).xv - balls(0).xv
    impacty! = balls(1).yv - balls(0).yv
    Normalize impulsex!, impulsey!, balls(0).x, balls(0).y, balls(1).x, balls(1).y
    impactSpeed! = Dot!(impactx!, impacty!, impulsex!, impulsey!)

    impulsex! = impulsex! * impactSpeed! * balls(0).mass * balls(1).mass / 2
    impulsey! = impulsey! * impactSpeed! * balls(0).mass * balls(1).mass / 2

    xva! = balls(0).xv + impulsex! / balls(0).mass
    yva! = balls(0).yv + impulsey! / balls(0).mass

    xvb! = balls(1).xv - impulsex! / balls(1).mass
    yvb! = balls(1).yv - impulsey! / balls(1).mass
RETURN

FUNCTION Collide (Ball() AS ballType)
'returns id the ball collided using the distance formula.
'can be optimized a lot!!!
    Collide = 0
    dx! = Ball(1).x - Ball(0).x
    dy! = Ball(1).y - Ball(0).y
    dist! = SQR(dx! ^ 2 + dy! ^ 2)
    IF dist! <= 20 THEN
        Collide = -1
    END IF
END FUNCTION

FUNCTION Dot! (ax!, ay!, bx!, by!)
'returns the dot product between 2 vectors a and b.
    Dot! = (ax! * bx!) + (ay! * by!)
END FUNCTION

SUB Normalize (nx!, ny!, X1!, Y1!, X2!, Y2!)
'normalizes the components of a vector derived from 2 points (x1,y1) and
'(x2,y2)

    dx! = X2! - X1!
    dy! = Y2! - Y1!
    dist! = SQR(dx! ^ 2 + dy! ^ 2)
    nx! = dx! / dist!
    ny! = dy! / dist!
END SUB


Compiles with -lang qb. You'll have to do the usual changes to compile it with -lang fb (declare all variables, no variable suffixes, etc.).

Personally, this code is all I need in ball to ball collision. Relsoft is God! :P


Sorry, but I think this code is somehow flawed. Try changing the masses of the two balls to - say - 1 and 10, and you wil clearly see that the total momentum is increasing when in fact it should be conserved.
Lachie Dazdarian
Posts: 2230
Joined: May 31, 2005 9:59
Location: Croatia
Contact:

Postby Lachie Dazdarian » Jul 18, 2007 19:33

But I think it's a great start, wouldn't you say so?

If exige chooses to reinvent the wheel, be my guest.
h4tt3n
Posts: 638
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Jul 18, 2007 20:24

Yes, it's not a bad start. It's definitely better than my go at solving the problem so far ^^

Seriously, imho reinventing things over and over is very important. That's how you learn new skills. Your very own - say - ball to ball collision engine may not ever become the best one around, but essentially this is how you learn to program.
Mindlessly copy-pasting other peoples code is always the easiest and least educational way to solve a problem.

And occasionally something entirely new and useful emerges from this constant re-thinking, re-experimenting and reinventing.

So in other words: Go exige go! I'm coming along too... ^^

A few useful links:

http://hyperphysics.phy-astr.gsu.edu/hbase/elacol.html
http://en.wikipedia.org/wiki/Elastic_collision
relsoft
Posts: 1765
Joined: May 27, 2005 10:34
Location: Philippines
Contact:

Postby relsoft » Jul 19, 2007 4:52

Normalize the dot product.
exige
Posts: 3
Joined: Jul 15, 2007 20:52

Postby exige » Jul 20, 2007 13:13

I just want to say, Thank you everybody for your answers!

I have been busy with other things lately, but I will take time some day to try to solve my problem!

I think that I now have all the information I need to solve it! :D
h4tt3n
Posts: 638
Joined: Oct 22, 2005 21:12
Location: Denmark

Postby h4tt3n » Jul 21, 2007 21:08

Here is a really neat tutorial on elastic 2D collision, including full tutorial as .pdf file, a nice program that implements the method and full source code:

http://www.geocities.com/vobarian/2dcollisions/

I did a quick port of the most important part of it:

Code: Select all

''  physics engine that simulates bouncing balls using vector math
''  This code is based on the 2D elastic collision tutorial found at:
''  http://www.geocities.com/vobarian/2dcollisions/

Randomize Timer

Type Vector_2D
  As Single X, Y
End Type

Type Ball
  As Uinteger Col
  As Vector_2D Pos_New, Vel
  As Single Mass, Density, Radius
End Type

Declare Function Vector_Sub (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
Declare Function Vector_Add (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
Declare Function Vector_Magnitude (Byref vector As vector_2d) As Single
Declare Function Vector_Normalize (Byref vector As vector_2d) As Vector_2D
Declare Function Dot_Product (Byref v1 As vector_2d, Byref v2 As vector_2d) As Single
Declare Function Vector_Scalar_Mul (Byref s1 As Single, Byref v1 As vector_2d) As vector_2D
Declare Sub DoElasticCollision (Byref Ball_1 As Ball, Byref Ball_2 As Ball)

Const Pi = 4*Atn(1)
Dim As Vector_2D Dist
Dim As Single Distance, Dist_Min
Dim As String X_Clicked = Chr(255)+"k"
Dim As Integer Screen_x, Screen_Y, i, i2
Dim Ball(1 to 5) As Ball

''  screen settings
screen_x = 600          ''  screen width
screen_y = 400          ''  screen height
screenres screen_x, screen_y, 16

With Ball(1)
  .Mass = 10
  .Density = 0.001
  .Radius = (((.Mass/.Density)/((4/3)*pi))^(1/3))
  .Pos_New.X = 100
  .Pos_New.Y = Screen_Y\2
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(255, 32, 32)
End With

With Ball(2)
  .Mass = 20
  .Density = 0.001
  .Radius = (((.Mass/.Density)/((4/3)*pi))^(1/3))
  .Pos_New.X = 200
  .Pos_New.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(255, 32, 255)
End With

With Ball(3)
  .Mass = 40
  .Density = 0.001
  .Radius = (((.Mass/.Density)/((4/3)*pi))^(1/3))
  .Pos_New.X = 300
  .Pos_New.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(32, 32, 255)
End With

With Ball(4)
  .Mass = 80
  .Density = 0.001
  .Radius = (((.Mass/.Density)/((4/3)*pi))^(1/3))
  .Pos_New.X = 400
  .Pos_New.Y = (Screen_Y\2)
  .Vel.X = 0
  .Vel.Y = 0
  .Col = RGB(32, 255, 32)
End With

With Ball(5)
  .Mass = 160
  .Density = 0.001
  .Radius = (((.Mass/.Density)/((4/3)*pi))^(1/3))
  .Pos_New.X = 500
  .Pos_New.Y = (Screen_Y\2)
  .Vel.X = -2
  .Vel.Y = -(Rnd-Rnd)/3
  .Col = RGB(255, 255, 32)
End With

Do
 
  Screenlock
   
    cls
   
    For i = Lbound(Ball) to Ubound(Ball)
     
      With Ball(i)
       
        ''ball - wall collision detection
        If .Pos_New.X > screen_x-.Radius-1 Then
          .Pos_New.X = screen_x-.Radius-1
          .Vel.X = -.Vel.X
        Elseif .Pos_New.X < .Radius Then
          .Pos_New.X = .Radius
          .Vel.X = -.Vel.X
        End If
        If .Pos_New.Y > screen_y-.Radius-1 Then
          .Pos_New.Y = screen_y-.Radius-1
          .Vel.Y = -.Vel.Y
        Elseif .Pos_New.Y < .Radius Then
          .Pos_New.Y = .Radius
          .Vel.Y = -.Vel.Y
        End If
       
        ''  ball - ball collision detection
        For i2 = i+1 to Ubound(Ball)
          Dist = Vector_Sub(.Pos_New, Ball(i2).Pos_New)
          Distance = Vector_Magnitude(Dist)
          Dist_Min = .Radius+Ball(i2).Radius
          ''  elastic bounce
          If Distance <= Dist_Min Then
            DoElasticCollision(Ball(i), Ball(i2))
          End If
        Next
       
        .Pos_New = Vector_Add(.Pos_New, .Vel)
        Circle (.Pos_New.X, .Pos_New.Y), .Radius, .col,,,1,f
       
      End With
     
    Next
   
  screenunlock
 
  sleep 1, 1
 
Loop Until Multikey(1) Or Inkey = X_Clicked

End


'-------------------------------------------------------------------------------


Function Vector_Sub (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
  Dim As Vector_2D v
  v.x = v1.x-v2.x
  v.y = v1.y-v2.y
  Return v
End Function

Function Vector_Add (Byref v1 As Vector_2D, Byref v2 As Vector_2D) As Vector_2D
  Dim As Vector_2D v
  v.x = v1.x+v2.x
  v.y = v1.y+v2.y
  Return v
End Function

Function Vector_Magnitude (Byref vector As vector_2d) As Single
  Dim as Single x, y, r
  x = Vector.x^2
  y = Vector.y^2
  r = Sqr(x+y)
  Return r
End Function

Function Vector_Normalize (Byref vector As vector_2d) As Vector_2D
  Dim As Vector_2D v
  Dim as Single r
  r = Vector_Magnitude(Vector)
  v.x = vector.x/r
  v.y = vector.y/r
  Return v
End Function

Function Dot_Product (Byref v1 As vector_2d, Byref v2 As vector_2d) As Single
  Return v1.x*v2.x+v1.y*v2.y
End Function

Function Vector_Scalar_Mul (Byref s1 As Single, Byref v1 As vector_2d) As vector_2D
  Dim as Vector_2d v
  v.x = v1.x*s1
  v.y = v1.y*s1
  Return v
End Function

Sub DoElasticCollision (Byref Ball_1 As Ball, Byref Ball_2 As Ball)
 
  Dim As Vector_2D v_n, v_un, v_ut, v_v1tPrime, v_v2tPrime, v_v1nPrime, v_v2nPrime
  Dim As Single v1n, v1t, v2n, v2t, v1tPrime, v2tPrime, v1nPrime, v2nPrime
 
  ''  vector normal to collision surface
  v_n = Vector_Sub(Ball_2.Pos_New, Ball_1.Pos_New)
 
  ''  normalize vector - make it a unit vector
  v_un = Vector_Normalize(v_n)
 
  ''  unit tangenti vector
  v_ut.x = -v_un.y
  v_ut.y = v_un.x
 
  ''  old normal and tangential velocity vectors
  v1n = Dot_Product(v_un, Ball_1.vel)
  v1t = Dot_Product(v_ut, Ball_1.vel)
  v2n = Dot_Product(v_un, Ball_2.vel)
  v2t = Dot_Product(v_ut, Ball_2.vel)
 
  ''  new tangential velocity (uncanged)
  v1tPrime = v1t
  v2tPrime = v2t
 
  ''  new normal velocity
  v1nPrime = ((v1n * (ball_1.mass - ball_2.mass) + 2 * ball_2.mass * v2n)) / (ball_1.mass + ball_2.mass)
  v2nPrime = ((v2n * (ball_2.mass - ball_1.mass) + 2 * ball_1.mass * v1n)) / (ball_1.mass + ball_2.mass)
 
  ''  new normal and tangential velocity vectors
  v_v1nPrime = Vector_scalar_mul(v1nPrime, v_un)
  v_v1tPrime = Vector_scalar_mul(v1tPrime, v_ut)
  v_v2nPrime = Vector_scalar_mul(v2nPrime, v_un)
  v_v2tPrime = Vector_scalar_mul(v2tPrime, v_ut)
 
  ''  new ball velocities
  Ball_1.Vel = Vector_Add(v_v1nPrime, v_v1tPrime)
  Ball_2.Vel = Vector_Add(v_v2nPrime, v_v2tPrime)
 
End Sub
 

Return to “General”

Who is online

Users browsing this forum: No registered users and 3 guests