Question about designing classes in an OOP game

Started by
4 comments, last by assaf 15 years, 4 months ago
Hi everybody, As a not-so-experienced OOPer, I'd like the advice of the more experienced programmers on this board. Suppose I'm writing a Billiard simulation (I'm working in ActionScript). I've got a class that describes my billiard ball. Now, I'll frequently be checking for collisions between two balls, so there will have to be some routing of the form DoCollision(ball1, ball2). In terms of "correctness of design", where should that routine go? Should it be part of the billiard ball class (this breaks the symmetry of the collision)? Should I create a new class called CollisionsClass and do all of my collision logic there? How would a professional programmer choose to do it? And are there any performance issues I should take into account (is the "correct" way less efficient, computationally speaking?) I'd appreciate your advice - thanks! Assaf.
Advertisement
Is the ability to collide a fundamental, core part of a ball? One that is so tied with the very existence of the ball that removing it would also remove the point of defining a ball in the first place?

This depends on your design choices. It's equally possible to consider that the essence of a ball is that it has a radius, location and color, or that the ball's purpose is to collide with other balls. Choose whichever suits you best.

Fundamental parts go into the ball class. Non-fundamental parts go outside the ball class and inside the class which needs to have balls collide (such as the game logic model).

Of course, since collisions are symmetric, you will want to use a static function that takes two balls as arguments, instead of a non-static function, regardless of whether that function is a member of the ball class or of another class.
You could use either way. However, if there are only going to be collisions between billiard balls, then I suggest that you place the method in the BilliardBall class and call it so:

ball1->DoCollision(ball2);

If there are more collisions, for example between a ball and say a wall or something, then it would be better to create a CollisionsClass or even an ICollidable interface with a virtual method such as:

virtual void Collide(ICollidable* object) = 0;

and let all the billiard balls inherit from this class as well as any other classes that you want to collide with each other. Then you can test for the types of collision, ie ball against ball or ball against wall, and react to them. This method will then allow you to test for collisions in an abstract way.
ToohrVyk, I'm basically asking whether the collision should be a fundamental part of the ball :). But I understand your answer.

Greywolf, thanks for suggesting interfaces. I've never used them before but they seem to make the sort of sense I was looking for.

Thank you for your time!

Assaf.
Quote:Original post by assaf

so there will have to be some routing of the form DoCollision(ball1, ball2).


While that is one way to define the function, what is the actual collision check problem?

If objects are not moving, they cannot collide.

If objects are moving, what does that look like? In simplest form, one object moves from P1 to P2 and somewhere in between it may intersect one of other objects.

You are not interested in collision alone, but response as well. Let's say you have 2 balls with radius of 5 and coordinates of center (1,2) and (2,3). Which ball collided with which? What is the response? Even more, if one ball is moving fast enough, it may in one time step advance through another ball, resulting in no collision.

So two improvements need to be made to this. Not only can our ball be moving, so can others. And furthermore, movement, despite being calculated using fixed time step, is not discrete.

When balls move, they outline a capsule:
/-\-----.|A|    B|\-/-----/
So whenever a ball moves, you need check its capsule for collisions. And since all balls might be moving, you need to check all capsules vs. all capsules at the same time.

This makes the problem considerably different that simple collision function of 2 circles.

First, you need to define collision function as function of:
- Old positions of 2 balls
- New positions of 2 balls
- Their respective radii
Reponse will also require mass, velocity, rotation, intertia or whatever your collision requires.

Next, you need to advance the simulation at same time for all balls, checking for collisions between all of them. When you find a collision, you store collision data along with time at which it occured. When you have tested all (all means the set of collisions you have determined relevant), you pick the earliest collision, and resolve it, calculating response, and advance entire simulation to that step.

Then you restart the entire process, until your earliest collision (or none) is later than end of current time step.

Obviously, you also need to check for collisions with edges of table, and so forth. But that's a fairly trivial and straight-forward approach to advance such simulation. Of course, general problem of physics simulation is considerably more complex. Billiard just has the luxury of having simple enough interactions.

Quote:In terms of "correctness of design", where should that routine go?
Into the place that will iterate over all possible pairs of collisions between all possible types and have information of old positions and new positions, and have the ability to partially advance the time step.

Quote:Should it be part of the billiard ball class (this breaks the symmetry of the collision)?

Symmetry can be applied in your collision check iterator. The loop that compares all pairs.

Quote:Should I create a new class called CollisionsClass and do all of my collision logic there?

Does collision class have access to all required data?

Quote:And are there any performance issues I should take into account (is the "correct" way less efficient, computationally speaking?)
Depends. The problem in general is n^2, and this cost may need to be paid several times per frame. Hard to say.

Other ways to improve the simulation is to check collisions based on what can collide, and not restarting entire simulation on collision but only correct for affected parts.

The other way however is to not mess with accurate collisions, but just check if two circles intersect, and hope your time step is small enough. With adequately short time step, this can work reasonably well.

[Edited by - Antheus on December 3, 2008 5:55:25 AM]
Hi Antheus,

Thanks for the detailed reply!

I think I can handle the collision detection & response, it's just the program structure that's bothering me, since I basically don't work with pros off whom I can pick good coding habits.

For the collision I'm solving for the position of the center of mass and checking whether the two balls just touch within the time interval of my simulation (i.e., solving |(Cm1+t*velocity1) - (Cm2 + t*velocity2)| = R1+R2)), and it seems to be working quite nicely - but thank you for the tips!

Assaf.

This topic is closed to new replies.

Advertisement