Computer Science Canada

[Tutorial]Circular Collision Data

Author:  Cervantes [ Sun May 30, 2004 5:57 pm ]
Post subject:  [Tutorial]Circular Collision Data

One of the greatest challanges I have faced in my programming life is getting two circles to bounce off each other as they would in real life: obeying the laws of physics. There have been many attempts at solving this riddle.
Homer_Simpson's approach
He uses angles, velocities, and weights. I took the basis of this code and tried to make my own Slime Volleyball program, one where the ball bounces correctly off the walls. I found this exceedingly difficult, and gave up. Cicular collision works well, but I find it difficult to do much else.
thoughtful's approach
He uses the method I like, x, y, vx, and vy. vx and vy are the x velocity, and y velocity. This style makes for easy bouncing off of a flat surface (a wall, for example). Simply make the vx or vy value negative, depending on where the wall is. thoughtful's collision between circles (in this case, pool balls) is flawless for this type of program: for when energy transfer is not 100%. I tried several times to modify his code into my slime volleyball program or also into a pong program where the paddles are circles, and failed. The collision just wasn't right.
rhomer's approach
This approach is nice and simple. It uses the x, y, vx, and vy style, and works well for the most part. The main problem is that the ball tends to go inside the player and do some wild things, or possibly even go through the player.

Cervantes' Approach
A while back tony taught me how to create gravity fields. When you have a gravity field (a spot that sucks whatever objects towards it) it is easy to reverse its effect, creating a repulsing field. Wink
Today I had a revelation: this could be used as a form of circular collision.
I was using it in a slime volleyball setting, so I'm going to be talking in terms of players and balls.
If the ball has collided with the player (determine using the distance formula) then make the player a repulsing gravity field. Also, have a maximum speed for the ball's velocity, so as to not let it get out of control.


setscreen ("offscreenonly")
var player :
        x, y, vy, GFstrength, GFdist, GFforce, GFangle : real %GF = Gravity Field -- Collisions between ball and players in this
        radius, score : int                         %uses repulsing gravity fields
        jump : boolean
    end record

player.x := maxx div 2
player.y := maxy div 2
player.vy := 0
player.GFstrength := 3
player.radius := 40
player.score := 0
player.jump := false

var ball :
        x, y, vx, vy : real
        radius : int
    end record

ball.x := 100
ball.y := 100
ball.vx := 0
ball.vy := 0
ball.radius := 12

var keys : array char of boolean
const max_speed := 7

    ball.x += ball.vx
    ball.y += ball.vy

    if ball.x <= 0 + ball.radius or ball.x >= maxx - ball.radius then
        ball.vx *= -1
    end if
    if ball.y <= 0 + ball.radius or ball.y >= maxy - ball.radius then
        ball.vy *= -1
    end if

    if ball.vx > max_speed then
        ball.vx := max_speed
    elsif ball.vx < -max_speed then
        ball.vx := -max_speed
    end if
    if ball.vy > max_speed then
        ball.vy := max_speed
    elsif ball.vy < -max_speed then
        ball.vy := -max_speed
    end if

    Input.KeyDown (keys)
    if keys (KEY_LEFT_ARROW) then
        player.x -= 2
    end if
    if keys (KEY_RIGHT_ARROW) then
        player.x += 2
    end if
    if keys (KEY_UP_ARROW) then
        player.y += 2
    end if
    if keys (KEY_DOWN_ARROW) then
        player.y -= 2
    end if

    if Math.Distance (player.x, player.y, ball.x, ball.y) < player.radius + ball.radius then
        player.GFdist := Math.Distance (player.x, player.y, ball.x, ball.y) %figure out the distance between the player and the ball
        player.GFforce := player.GFstrength * (1 / player.GFdist * player.GFdist) %inverse square.  it's a gravity field thing.
        if player.x = ball.x then %this must be here or a division by 0 error could occur. 
            if ball.y > player.y then
                player.GFangle := 90
                player.GFangle := 270
            end if
            player.GFangle := arctand ((player.x - ball.x) / (player.y - ball.y)) %determine the angle between the player and the ball
        end if
        if ball.y < player.y then %without this, if the ball hits the underside of the player, it will
            player.GFangle += 180 %go into the player and do some wild turns, then shoot out the top somewhere.
        end if
        ball.vx += player.GFforce * sind (player.GFangle) %finally, apply the force of the gravity field to the x vector of the ball
        ball.vy += player.GFforce * cosd (player.GFangle) %apply the force of the gravity field to the y vector of the ball
    end if
    drawfilloval (round (player.x), round (player.y), player.radius, player.radius, 42)
    drawfilloval (round (ball.x), round (ball.y), ball.radius, ball.radius, blue)
    delay (10)

end loop

Please, let me know of any errors in the collision data in this program. In this program, if the ball is moving really slowly, and it hits you (you are still) it will fire off with decent velocity away. I haven't gotten around to trying to fix this, but I have an idea of how to.

The methods of determining the colision data between two circles that I have seen so far all have their varying pros and cons. I hope this new method has very few cons. Wink

Author:  Tony [ Sun May 30, 2004 7:11 pm ]
Post subject: 

not bad Cervantes - I see you advancing in your programming skillz. I'm glad that you're able to take the consept from one program and modify it to apply properly to another, but there's one major flaw.

when two balls collide, an inpulse force is created that acts only once during the brief moment two balls are in physical contact.

a force field (such as gravity, or negative gravity as in the example) on the other hand act continuely (all the time), but are just not as effective at the distance.

and while the code works since the field's activation is limited to the actual contact moment, the reasoning behind the physics model is wrong Confused

Author:  Cervantes [ Sun May 30, 2004 9:46 pm ]
Post subject: 

true, its not an actual collision, but it works well enough. Since the gravity field has no effect until there is a collision between the player and the ball (Math.Distance < sum of 2 radii) the ball gets repulsed quickly and it doesn't really look like its going into the player at all.

tony wrote:
and while the code works since the field's activation is limited to the actual contact moment

it's very interesting playing slime volleyball if you take out the two if Math.Distance lines and have the repulsing gravity fields working non-stop Wink

Author:  TheZsterBunny [ Tue Feb 08, 2005 8:27 pm ]
Post subject: 


Nice effect.

How 'bout a tutorial on gravity fields? this looks like something worth learning.

well, the concept anyways.


Author:  evildaddy911 [ Sun Oct 16, 2011 12:11 pm ]
Post subject:  Re: [Tutorial]Circular Collision Data

umm... you should make it so your ball cant go off the screen, i find a simple if statement works well,


if player.y < (maxy - player.radius) then
        if keys ('w') then
              player.y := player.y + 5
        end if
end if

Author:  smool [ Thu Dec 08, 2011 10:16 am ]
Post subject:  RE:[Tutorial]Circular Collision Data

Why not calculate tangent of cirlce at point of collision and use angle of incidence = angle of reflection to calculate a bounce?