Geeks With Blogs

News

Running with Code Like scissors, only more dangerous

There's already rudimentary collision detection in the app, but only for the sides.  What if I wanted the balls to detect when they collide?

In the demo video you can already see the balls colliding (and, incidentally, the gravity changing from down, then left, up, right, and down again).  But how did I get there?  Wouldn't you know that XNA has a built-in structure called BoundingSphere that can handle that for you?  I defined the bounding sphere as centered around the ball's center point (which was defined by the texture's dimensions), and used the z-coordinate as 0.  Intrinsically, then, the bounding sphere acts like a bounding circle as long as everything is on the same Z-plane.

Next, I created a couple interfaces that are going to help me out.  Interfaces such as IPhysical and IPhysicalSphere -- they provide necessary information for collidable objects.  Here they are:

`   1: public interface IPhysical`
`   2: {`
`   3:     Vector3 CenterOfMass`
`   4:     {`
`   5:         get;`
`   6:     }`
`   7:  `
`   8:     Vector3 Speed`
`   9:     {`
`  10:         get;`
`  11:         set;`
`  12:     }`
`  13:  `
`  14:     float Mass`
`  15:     {`
`  16:         get;`
`  17:     }`
`  18: }`
`  19:  `
`  20: public interface IPhysicalSphere : IPhysical`
`  21: {`
`  22:     BoundingSphere Bounds`
`  23:     {`
`  24:         get;`
`  25:     }`
`  26: }`

By implementing these properties, a separate class can be constructed to perform the necessary math for bouncing balls.  When first implemented, colliding balls would simply switch speed vectors.  However, that only works if balls collide straight on (so that movement vectors would be inverse of each other).  When balls collide like this:

(Awesome MS-Paint artwork, huh?)

When balls collide like that, they don't simply exchange vectors.  What actually happens is that the speed vectors are changed according to the normal line between the two circles (or the normal plane between the two spheres).

I created a static class to do this work for me:

`   1: public static class Collision`
`   2: {`
`   3:     public static void ApplyCollision(IPhysicalSphere sphereA, IPhysicalSphere sphereB)`
`   4:     {`
`   5:         Vector3 x = sphereB.CenterOfMass - sphereA.CenterOfMass;`
`   6:         x.Normalize();`
`   7:  `
`   8:         Vector3 v1 = sphereA.Speed;`
`   9:         float x1 = Vector3.Dot(x, v1);`
`  10:  `
`  11:         Vector3 v1x = x * x1;`
`  12:         Vector3 v1y = v1 - v1x;`
`  13:  `
`  14:         float m1 = sphereA.Mass;`
`  15:  `
`  16:         x = -x;`
`  17:         Vector3 v2 = sphereB.Speed;`
`  18:         float x2 = Vector3.Dot(x, v2);`
`  19:  `
`  20:         Vector3 v2x = x * x2;`
`  21:         Vector3 v2y = v2 - v2x;`
`  22:  `
`  23:         float m2 = sphereB.Mass;`
`  24:  `
`  25:         float combinedMass = m1 + m2;`
`  26:  `
`  27:         Vector3 newVelA = (v1x * ((m1 - m2) / combinedMass)) + (v2x * ((2f * m2) / combinedMass)) + v1y;`
`  28:         Vector3 newVelB = (v1x * ((2f * m1) / combinedMass)) + (v2x * ((m2 - m1) / combinedMass)) + v2y;`
`  29:  `
`  30:         sphereA.Speed = newVelA;`
`  31:         sphereB.Speed = newVelB;`
`  32:     }`
`  33: }`

This was inspired by a blog post I found that covered it exceptionally.  I wish I could name the offer but I couldn't find the author's name!

Calling this method to adjust the speed vectors is done whenever collisions are detected.  That part is handled in the Game class during the Update method:

`   1: for (int i = 0; i < m_balls.Count; i++)`
`   2: {`
`   3:     Ball b = m_balls[i];`
`   4:     for (int j = i + 1; j < m_balls.Count; j++)`
`   5:     {`
`   6:         Ball test = m_balls[j];`
`   7:         if (b.Bounds.Intersects(test.Bounds))`
`   8:         {`
`   9:             Collision.ApplyCollision(b, test);`
`  10:             b.Update(gameTime, GraphicsDevice.Viewport);`
`  11:             test.Update(gameTime, GraphicsDevice.Viewport);`
`  12:             b = null;`
`  13:             break;`
`  14:         }`
`  15:     }`
`  16:  `
`  17:     if (b != null)`
`  18:         b.Update(gameTime, GraphicsDevice.Viewport);`
`  19: }`

And that's about all there is to it!  New source code is up!  Here's the next demo:

One other thing -- as a note, the red ball with the orange gradient has a mass of 8.0f; the others have a mass of 4.0f.

Left by Al on Apr 30, 2009 9:13 PM