Collision in a pinball style game

Started by
17 comments, last by GameDev.net 18 years, 8 months ago
Hey Guys! This is my first post. well here it goes: I have been working on this 2D graphics demo/game that is similar to a pinball machine. I have pinballs that bounce around and they collide with the window border and other polygons. I have now wanted to add collision between pinballs and cirle primitives, and eventually with other pinballs. I was just wondering if anyone has any good algorithms for calculating vector collision between a movin circle, and a stationary circle, as well as between 2 moving circles. When collision occurs the pinball should be reflected off the surface. Methods that include the trivial rejection of objects that obviously aren't going to be collided with would be preffered. Thanks a lot! [Edited by - TAkAw on July 22, 2005 11:08:04 PM]
Advertisement
Quote:I have been working on this 2D graphics demo/game that is similar to a pinball machine. I have pinballs that bounce around and they collide with the window border and other polygons. I have now wanted to add collision between pinballs and cirle primitives, and eventually with other pinballs. I was just wondering if anyone has any good algorithms for calculating vector collision between a movin circle, and a stationary circle, as well as between 2 moving circles. When collision occurs the pinball should be reflected off the surface. Methods that include the trivial rejection of objects that obviously aren't going to be collided with would be preffered. Thanks a lot!
Collision between moving circles and spheres is a pretty easy problem to solve. Call the circle center C, the radius r, and the velocity V. The circle center traces out the path C+tV over time, where t is the time parameter.

We are interested in the squared distance between the centers of two circles, 0 and 1. The function is:

f(t) = (D+tV)2

Where D = C0-C1 and V = V0-V1. Expanding gives:

f(t) = (V.V)t2+2(D.V)t+(D.D)

We are interested in the times at which the squared distance between the circles is exactly the squared sum of their radii. Thus we have:

(V.V)t2+2(V.D)t+(D.D) = (r0+r1)2

A quadratic which can be solved for the first time of intersection, if any. The coefficients also have geometric significance, which can be used for early outs. For further efficiency you could add a broad-phase step, but for a pinball game I doubt you'd need it.

You should probably double-check my math (I may have switched something around somewhere), but that's the general idea. Here's a simple example of swept circles in action:




[Edited by - jyk on August 15, 2005 12:00:33 PM]
Quote:Original post by jyk

(V.V)t2+(V.D)t+(D.D) = (r0+r1)2

A quadratic which can be solved for the first time of intersection, if any. The coefficients also have geometric significance, which can be used for early outs.


So I have this quadratic, and that gives me the time if intersection in the frame if there is one, but the quadratic gives a number that isn't necessarily between 0 and 1. which is needed to calc the new position. Mabye I just don't understand, please clarify if you can.

Collisions outside the range [0, 1] can be ignored, as they occur either before or after the timestep.

I should clarify that this assumes V is the displacement vector for that frame. Another way to do it is to have V be in units per second, and use a time range of [0, frameTime] instead of [0, 1].

Does that help at all? Let me know if you need further clarification...
ok so say the pinball moves 10 pixels per frame, and that time, t, is the result of the quadratic equation, and a collision has been confirmed, would the t you plug into the parametric position equation be scaled at all for how far the ball moves per frame, or should I just trivialy reject times that are outside [0, 1]?
Let me try explaining it in a slightly different way. Forget for the moment about what V actually represents. The t you get from solving the quadratic tells you how far along V you got before a collision occured. If t < 0, the collision occured along negative V; in terms of time it occured 'in the past' and can be ignored. If t = .5, the circle made it halfway along V before colliding. If t = 1, it made it all the way along V before colliding. And so on.

In all cases you can ignore t < 0, so the lower bound for your time interval is always 0. The upper bound depends on what V represents.

If V is in units per second, then the upper bound is the elapsed time for that frame. For example, if your game runs at 60hz, the valid interval for t would be [0, 1/60]. In this case t is the 'actual' time at which the collision occured.

If V only represents the displacement over that frame, then the upper bound is 1, and the valid interval for t is [0, 1]. In this case t is the 'normalized' time of intersection.

In either case, as long as you use V consistently you can just plug t as-is into the parametric equation for motion to get your new position.

Now that I try to explain it, it is a little confusing. From your example (10 pixels per frame), it sounds like V is a displacement vector, in which case you should reject collisions outside the range [0, 1]. You can then simply update your position like this:

ball.position += t*V;

I don't know that I've explained this very well, so I'll be glad to offer further help if needed.
yea that's great and I was able to implement collision with stationary circular pillars. I am still having trouble understanding how to determine collision between 2 moving pinballs. I assume it's similar to what I've already done, but not sure how to take it to the next step.
Quote:yea that's great and I was able to implement collision with stationary circular pillars. I am still having trouble understanding how to determine collision between 2 moving pinballs. I assume it's similar to what I've already done, but not sure how to take it to the next step.
You might go back and take another look at my first reply in this thread; the equation I gave actually does incorporate the velocities of both circles (that is, they can both be moving).
Ok so i have pinball collision but It's still not quite right, It will work if both balls are going twoards one another (a head on collision) but if only one of the balls is colliding (from behind) I need to change the velocity of the ball that was collided into accordingly. I included my current source for the function, any suggestions?

void Pinball::PinballCollision(std::list<Pinball> balllist, bool& collision){	for (std::list<Pinball>::iterator iter = balllist.begin(); iter != balllist.end(); ++iter)	{				Vector2D b0(mCircle.GetCenter().GetX(), mCircle.GetCenter().GetY());		Vector2D q(iter->GetCircle().GetCenter().GetX(), iter->GetCircle().GetCenter().GetY());		Vector2D v(mVelocity);		Vector2D d(q - b0);		//parts of the quadratic		float r(mCircle.GetRadius() + iter->GetCircle().GetRadius());		float a(v * v);		float b(v * d * -2.0f);		float c(d * d - std::pow(r, 2));		float b2_4ac(std::pow(b, 2) - 4.0f * a * c);		if (b2_4ac >= 0)//the pinball does collide with the circle.		{			float pt = (-b + std::sqrt(b2_4ac)) / (2 * a);			float mt = (-b - std::sqrt(b2_4ac)) / (2 * a);			float t;			if (pt < mt)			{				//pt is the closest intersection of the circle.				t = pt;			}			else 			{				//mt is the closest intersection of the circle, or the circle is grazed.				t = mt;			}			if ((t > 1) || t < 0)			{				continue;			}			Vector2D bh(b0 + v * t);			//reflect the ball			Vector2D n(bh - q);			mVelocity = (n * ((-2.0f * (v * n)) / (n * n))) + v;			collision = true;		}	}}
Here's some code you can look at for comparison. It doesn't actually adjust the objects' velocities, but returns all the information you need to do it yourself. It also returns valid information for both the swept and static case, which can be useful.

// --------------------------------------------------------------------------------------template <class T> struct Sweep2{    enum {STATIC,          STATIC_INVALID_NORMAL,          SWEPT};              int         type;    T           t;    Vector2<T>  normal;    T           distance;    int         numPoints;    Vector2<T>  points[2];};// --------------------------------------------------------------------------------------template <class T> bool Circle2Circle2(    const Vector2<T> C0,            // Circle 0 center    T r0,                           // Circle 0 radius    const Vector2<T> V0,            // Circle 0 velocity    const Vector2<T> C1,            // Circle 1 center    T r1,                           // Circle 1 radius    const Vector2<T> V1,            // Circle 1 velocity    T tMax,                         // Max time    Sweep2<T>& sweep,               // Output info    T epsilon = Math<T>::EPSILON)   // Epsilon{    // 0 is moving and 1 is stationary, and the normal points from 1 to 0    Vector2<T> d = C0 - C1;    T r = r0 + r1;    T c = d.Dot(d) - r * r;        // Swept    if (c > (T)0.0)    {        Vector2<T> V = V0 - V1;        T b = d.Dot(V);        if (b >= (T)0.0) // Circles moving apart            return false;        T a = V.Dot(V);        if (a < epsilon) // Relative velocity too small to solve robustly            return false;                    T disc = b * b - a * c;        if (disc < (T)0.0) // Circles do not ever intersect            return false;        sweep.t = -b - Math<T>::Sqrt(disc);        if (sweep.t > tMax * a) // Circles intersect after max time            return false;                sweep.type = Sweep2<T>::SWEPT;        sweep.t /= a;        sweep.normal = Vector2<T>::Normalize(d + sweep.t * V);        sweep.numPoints = 1;        sweep.points[0] = (C0 + C1 + sweep.t * (V0 + V1) + (r1 - r0) * sweep.normal) * (T)0.5;    }    // Static    else    {        T l = d.Length();                // Invalid normal        if (l < epsilon)        {            sweep.type = Sweep2<T>::STATIC_INVALID_NORMAL;            sweep.distance = r;        }        // Valid normal        else        {            sweep.type = Sweep2<T>::STATIC;            sweep.normal = d / l;            sweep.distance = r - l;        }    }        return true;}// --------------------------------------------------------------------------------------

This topic is closed to new replies.

Advertisement