Game Heirarchy

Started by
8 comments, last by haegarr 10 years, 2 months ago

Hey this is my first time posting on the forums but I've used this site as a resource before. Anyways I'm in the process of creating a small game. Here is my dilemma however... I'm not entirely sure how the different classes should interact with each other.

For example, lets say that I have a Player, Wall, and Collision Class. Here are my questions:

  • What classes should know of eachother?
  • How should the classes interact?
  • Should, for example, Player be able to call a function from Collision to determine if it's colliding with Wall? Or should Collision keep an updated copy of Wall's and Player's coordinates to determine if a collision is occuring?

Thank you in advance for any help.

Advertisement

What classes should know of eachother?

If you mean the classes in this example then: it doesnt matter at all.

If you mean classes in general: In OOP try to model the real world. If two classes dont depend on each other, dont call each others methods etc, then by connecting them you just create an extra node. They add up and make the code into spaghetti:)

(The collision class is also an extra "node" but later if you`ll have many different types of objects and more complex collisions it will make more sense having it)

How should the classes interact?

You can make a Player.collision( Wall) function but making a separate class would be nicer.

That class could query the coordinates it needs then change the player`s speed and position if necessary.

Should, for example, Player be able to call a function from Collision to determine if it's colliding with Wall? Or should Collision keep an updated copy of Wall's and Player's coordinates to determine if a collision is occuring?

The collision class could have a method that handles the collision between every player and every wall. It should keep references/pointers to the containers(lists/arrays) which contain the walls and players.

(The problem with keeping an updated copy is that its a copy, and it has to be updated:) Plus other things. Like what if new objects are being added and destroyed?)

Hey this is my first time posting on the forums but I've used this site as a resource before. Anyways I'm in the process of creating a small game. Here is my dilemma however... I'm not entirely sure how the different classes should interact with each other.

For example, lets say that I have a Player, Wall, and Collision Class. Here are my questions:

  • What classes should know of eachother?
  • How should the classes interact?
  • Should, for example, Player be able to call a function from Collision to determine if it's colliding with Wall? Or should Collision keep an updated copy of Wall's and Player's coordinates to determine if a collision is occuring?

Thank you in advance for any help.

Ideally, a class knows nothing of another class. Two classes should interact via an interface class or some other means. Instead of having the player call a function from collision to determine if it is colliding with the wall, have a function that takes the player and the wall as arguments to determine if the player is colliding with the wall (and expand this for all the things that can collide). This way, player doesn't need to know anything about collisions or walls, player just needs to know about player and wall just needs to know about wall.

Thank you for the replies.

If I'm understanding you both correctly, then I would do something like this?


class Player
{
public:
	void ChangePosition(int dX, int dY)
	{

		posX += dX;
		posY += dY;
	}

private:
	int posX;
	int posY;
};



class Collision
{
public:
	bool IsColliding(const Player &PlayerCol, const Wall &WallCol)
	{
		// code here
	}

};

class Wall
{
	int x;
	int y;
};

Your code is sort-of on track. As Azaral mentions, your collision engine should evaluate the player's speed and direction against any other objects. The collision engine then resolves the collisions and repositions objects appropriately. In a sense, objects "know" about the collision engine, but only to tell the collision engine how to represent them - bounded planes for a wall, a cylinder or capsule for a player, etc. Objects also know to ask the collision engine for their location, speed and direction.

A sample implementation could be for each object to have 2 representations: a renderable one, and a collision one. Set the position, speed and direction of the collision player. Call the collision engine to determine where that object ends up in the next delta time. Orient the renderable player in accordance with the results for the collision player. Render the renderable player. That's very simplistic and doesn't take into account the player's renderable action when he hits the wall (throw his arms up, moan, etc.)

As you surmise, the collision engine may have to evaluate collisions between various type objects: sphere-with-plane, cylinder-with-plane, cylinder-with-cylinder, etc. EDIT: But the collision engine does not know that one object is a "player" and another a "wall." It has in its list of objects a cylinder and bounded plane. The cylinder object has a location, speed and direction when it's (the collision engine) called; the collision engine evaluates whether the cylinder collides with the bounded plane, and sets the cylinder's location, speed and direction accordingly. The player object accesses that the cylinder information later.

EDIT: You may want to look at docs for various collision engines available: ODE, Bullet.. I'm sure others have favorites to recommend. But many of them are free and provide libraries for various platforms.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Thank you for the reply. Guess I'm gonna get some coding in and see if I can get anything close to what you're describing.

Just FYI, another option - instead of the collision engine determining the player orientation, you can set it up to just report collisions, and put your collision resolution code somewhere else if it's important to know what the player ran into to determine the reaction. That report can include, e.g., what/when/where the player collided.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I'm not sure Collision justifes an entire seperate class. Collision doesn't have any state information, its entire purpose is simply to check whether a "Player" and "Wall" are occupy the same space at a given time. Even something like CollisionEngine might not be enough to justify an entirely seperate class, unless you can imagine parameters which justify instantianting different CollisionEngines objects depending on different parameters. Remember, even if you are using OO, not everything HAS to be OO. For example, consider something like the sin() function. We don't do x = new Integer(5), y = x.sin(), we just call sin(x) :-) Standard imperative/functional programming, defined in static/global scope outside of either class, might make more sense in this situation .

Perhaps if you had something like a GameEngine class, you might have a function called isColliding(Player, Wall). GameEngine would obviously, have to know about Player and Wall, so it would make perfect sense to put it there. Or perhaps something like PhysicsEngine, which coudl/would do more than just test for Collisons

I would create a header file with a namespace like Tools or something. Inside this namespace I would have the collision functions.

I would also have another set of headers that define the collidable types, IE AABB, a circle, etc. I would have these have NO default constructor to force their initialization upon instantiation. I would also have as much data possible in the collidable type be pointers to the relevant data. For example, an AABB would have pointers for the position that point to the position of the entity it is for. Then if possible have the width and height be pointers as well, but they would probably just be non-pointers. I would define a function for each type of collision possible. For example, I would have three functions, one for AABB vs AABB, one for Circle for Circle, and one for Circle vs AABB. They would all be overloads of HasCollided (or in your case, isCollided). Then when I run collision detection, I just take the collidable data from each object and the correct function will be ran. It would require minimal code at the front end of it, just a simple function call. The collidable data should also not be inherited but be part of the regular data for the object. By having as much as possible in the collideable data be pointers, they will automatically have the current values without needing to be updated manually.

You could end up with a lot of collision functions to define and that can be alliviated somewhat by more involved programming, but this I think is the simplest way to do it and it is quite effective.

For handling collisions, this could be another piece of collision data, which I would also have separate from the collision detection data. Then you would have the same function overload process for how to handle all the different collision types.

With this you would call HasCollided( a.GetCollisionDetectionData(), b.GetCollisionDetectionData() );

and if that is true then run HandleCollision( a.GetCollisionData(), b.GetCollisionData() );

I apologize in advance if this doesn't make any sense and I sound like I'm rambling. One too many beers and it's bedtime as well lol.

Collision detection in an own class (or probably more precise, as an own sub-system) has its right because collision is something that can happen between 2 (or more) equal objects. Collision is usually done as a pairwise comparison, and none of the potential colliders is superior to the other. Without external coordination object A would be asked to collide with object B, and in a later step object B would be asked for collision with object A (let PC and static colliders away and look at collisions of 2 NPCs, for example). Moreover, in case of multiple collisions of a particular collider a more complex resolution need to be run. A collision is not handled simply by just looking whether 2 objects intersect. In fact, a separate collision class / sub-system deals with the entire situation, while a single collider naturally looks centric on self.

Writing classes principally agnostic of all other classes and then glueing them together with external functions is as wrong as not using external function at all. It is wrong because it beaks encapsulation needlessly. The collision system, for example, needs to track which pairs of colliders are already tested, which collisions were determined, and how far the resolution process has advanced. These things are totally internal to collision detection; there is no general need to externalize them.

External functions / methods are meaningful if none of the participating classes is superior, and in the case of functions if there is no need for additional persistent state to be stored. Looking at the possibility for the need to compare different collision volume kinds, there is again no real reason why one of the kinds should be superior to the other. This again means that the check should not be implemented in the one or the other of the both volume classes. However, we have the necessity to store state, and hence implementing the comparators as methods of a higher ranking class in the collision system is fine, because we have this higher ranking class anyway. If you wish, you can implement them as external functions, that's right, but there is no win in doing so (at least I don't see any win). Arguing with a simpler maintenance will not work here, because we speak of a sub-system that needs to handle collision in its entirety, so that new collision volumes will need to adapt the system (at least a dispatcher) anyway.

Just my 2 Cents.

This topic is closed to new replies.

Advertisement