60
\$\begingroup\$

I'm working on a 2D game where you can move up, down, left and right. I have essentially two game logic objects:

  • Player: Has a position relative to the world
  • World: Draws the map and the player

So far, World depends on Player (i.e. has a reference to it), needing its position to figure out where to draw the player character, and which portion of the map to draw.

Now I want to add collision detection to make it impossible for the player to move through walls.

The simplest way I can think of is to have the Player ask the World if the intended movement is possible. But that would introduce a circular dependency between Player and World (i.e. each holds a reference to the other), which seems worth avoiding. The only way I came up with is to have the World move the Player, but I find that somewhat unintuitive.

What is my best option? Or is avoiding a circular dependency not worth it?

\$\endgroup\$
3
  • 4
    \$\begingroup\$ Why do you think a circular dependency is a bad thing? stackoverflow.com/questions/1897537/… \$\endgroup\$ Nov 14, 2012 at 18:51
  • \$\begingroup\$ @Fuhrmanator I don't think they're generally a bad thing, but I'd have to make things a bit more complex in my code to introduce one. \$\endgroup\$
    – futlib
    Nov 16, 2012 at 17:54
  • \$\begingroup\$ I mad a post about our little discussion, nothing new though: yannbane.com/2012/11/…... \$\endgroup\$
    – jcora
    Nov 18, 2012 at 13:46

9 Answers 9

62
\$\begingroup\$

The World should not draw itself; the Renderer should draw the World. The Player should not draw itself; the Renderer should draw the Player relative to the World.

The Player should ask the World about collision detection; or perhaps collisions should be handled by a separate class which would check collision detection not only against the static world but also against other actors.

I think the World should probably not be aware of the Player at all; it should be a low-level primitive not a god-object. The Player will probably need to invoke some World methods, perhaps indirectly (collision detection, or checking for interactive objects, etc).

\$\endgroup\$
13
  • 25
    \$\begingroup\$ @snake5 - There's a difference between "can" and "should". Anything can draw anything - but when you need to change code that deals with drawing, it's a lot easier to go to the "Renderer" class rather than search for the "Anything" that's drawing. "obsessing on compartmentalization" is another word for "cohesion". \$\endgroup\$
    – Nate
    Nov 13, 2012 at 13:11
  • 16
    \$\begingroup\$ @Mr.Beast, no, he isn't. He's advocating good design. Cramming everything in one blunder of a class makes no sense. \$\endgroup\$
    – jcora
    Nov 13, 2012 at 14:16
  • 23
    \$\begingroup\$ Whoa, I didn't think it would spark such a reaction :) I don't have anything to add to the answer, but I can explain why I gave it - because I think it's simpler. Not 'proper' or 'correct'. I didn't want it to sound that way. It's simpler for me because if I find myself tackling classes with too many responsibilities, a split is faster than forcing the existing code to be readable. I like code in chunks that I can understand, and refactor in reaction to problems like the one @futlib is experiencing. \$\endgroup\$
    – Liosan
    Nov 13, 2012 at 14:34
  • 12
    \$\begingroup\$ @snake5 Saying adding more classes adds overhead for the programmer is often completely wrong in my experience. In my opinion 10x100 line classes with informative names and well defined responsibilities is easier to read and less overhead for the programmer than a single 1000 line god class. \$\endgroup\$
    – Martin
    Nov 13, 2012 at 14:46
  • 7
    \$\begingroup\$ As a note about what draws what, a Renderer of some sort is necessary, but that doesn't mean the logic for how each thing is rendered is handled by the Renderer, each thing that needs to be drawn should probably inherit from a common interface such as IDrawable or IRenderable (or interface equivalent in whatever language you're using). The world could be the Renderer, I suppose, but that seems like it would be overstepping its responsibility, especially if it already was an IRenderable itself. \$\endgroup\$
    – zzzzBov
    Nov 13, 2012 at 16:17
37
\$\begingroup\$

Here is how a typical rendering engine handles these things:

There's a fundamental distinction between where an object is in space and how the object is drawn.

  1. Drawing an object

    You typically have a Renderer class that does this. It simply takes an object ( Model ) and draws in on the screen. It can have methods like drawSprite( Sprite ), drawLine(..), drawModel( Model ), whatever you feel like needing. It's a Renderer so it's supposed to do all these things. It also uses any API you have underneath so you can have for instance a renderer that uses OpenGL and one that uses DirectX. If you want to port your game to another platform, you simply write a new renderer and use that one. It's "that" easy.

  2. Moving an object

    Each object is attached to something we like to refer to as a SceneNode. You achieve this through composition. A SceneNode contains an object. That's it. What's a SceneNode? It's a simple class containg all the transformations ( position, rotation, scale ) of an object ( usually relative to another SceneNode ) together with the actual object.

  3. Managing the objects

    How are SceneNodes managed? Through a SceneManager. This class creates and keeps track of every SceneNode in your scene. You can ask it for a specific SceneNode ( usually identified by a string name like "Player" or "Table" ) or a list of all the nodes.

  4. Drawing the world

    This should be pretty obvious by now. Simply walk through every SceneNode in the scene and have the Renderer draw it in the right place. You can draw it in the right place by having the renderer store the transformations of an object before rendering it.

  5. Collision Detection

    This isn't always trivial. Usually you can query the scene about what object is at a certain point in space, or what objects will a ray intersect. This way you can create a ray from your player in the direction of the movement and ask the scene manager what's the first object that ray intersects. You can then choose to move the player to the new position, move him by a smaller amount ( to get him next to the colliding object ) or not move him at all. Make sure to have these queries handled by separate classes. They should ask the SceneManager for a list of SceneNodes, but it's another task to determine whether that SceneNode covers a point in space or intersects with a ray. Remember that the SceneManager only creates and stores nodes.

So, what is the player, and what is the world?

The Player could be a class containing a SceneNode, which in turn contains the model to be rendered. You move the player by changing the position of the scene node. The world is simply an instance of the SceneManager. It contains all the objects ( through SceneNodes ). You handle collision detection by making queries on the current state of the scene.

This is far from being a complete or accurate description of what happens inside most engines, but it should help you understand the fundamentals and why it's important to respect the OOP principles underlined by SOLID. Don't resign yourself to the idea that it's too hard to restructure your code or that it won't really help you. You will win a lot more in the future by carefully designing your code.

\$\endgroup\$
3
  • \$\begingroup\$ +1 - I've found myself building my game systems something like this, and find it to be quite flexible. \$\endgroup\$
    – Cypher
    Nov 13, 2012 at 17:46
  • \$\begingroup\$ +1, great answer. More concrete and to the point than my own. \$\endgroup\$
    – jcora
    Nov 13, 2012 at 18:24
  • \$\begingroup\$ +1, I learned so much from this answer & it even had an inspiring ending. Thanks @rootlocus \$\endgroup\$
    – joslinm
    Nov 13, 2012 at 19:19
16
\$\begingroup\$

Why would you want to avoid that? Circular dependencies should be avoided if you want to make a reusable class. But the Player is no class that needs to be reusable at all. Would you ever want to use the Player without a world? Probably not.

Remember that classes are nothing more than collections of functionality. The question is just how one does divide the functionality. Do whatever you need to do. If you need a circular decadency, then so be it. (Same goes for any OOP features by the way. Code things in a way that it serves a purpose, don't just follow paradigms blindly.)

Edit
Okay, to answer the question: you can avoid that the Player needs to know the World for collision checks by using callbacks:

World::checkForCollisions()
{
  [...]
  foreach(entityA in entityList)
    foreach(entityB in entityList)
      if([... entityA and entityB have collided ...])
         entityA.onCollision(entityB);
}

Player::onCollision(other)
{
  [... react on the collision ...]
}

The kind of physics you have described in the question can be handled by the world if you expose the velocity of the entities:

World::calculatePhysics()
{ 
  foreach(entityA in entityList)
    foreach(entityB in entityList)
    {
      [... move entityA according to its velocity as far as possible ...]
      if([... entityA has collided with the world ...])
         entityA.onWorldCollision();
      [... calculate the movement of entityB in order to know if A has collided with B ...]
      if([... entityA and entityB have collided ...])
         entityA.onCollision(entityB);
    }
}

However note that you probably will need a dependency on the world sooner or later, that is whenever you need functionality of the World: you want to know where the nearest enemy is? You want to know how far the next ledge is away? Dependency it is.

\$\endgroup\$
10
  • 4
    \$\begingroup\$ +1 Circular dependency isn't really an issue here. At this stage there's no reason to worry about that. If the game grows and the code matures, it will probably be a good idea to refactor those Player and World classes in sub-classes anyway, have a proper component-based system, classes for input handling, maybe a Rendered, etc. But for a start, no problem. \$\endgroup\$ Nov 13, 2012 at 13:19
  • 4
    \$\begingroup\$ -1, that's definitely not the only reason not to introduce circular dependencies. By not introducing them, you make your system easier to extend and to change. \$\endgroup\$
    – jcora
    Nov 13, 2012 at 13:25
  • 4
    \$\begingroup\$ @Bane You can't code anything without that glue. The difference is just how much indirection you add. If you have the classes Game -> World -> Entity or if you have the classes Game -> World, SoundManager, InputManager, PhysicsEngine, ComponentManager. It makes things less readable because of all the (syntactic) overhead and the by that implied complexity. And at one point you'll need the components to interact with each other. And that's the point where one glue class makes things easier than everything divided between many classes. \$\endgroup\$
    – API-Beast
    Nov 13, 2012 at 14:20
  • 3
    \$\begingroup\$ No, you're moving the goalposts. Of course something must call render(World). The debate is around whether all the code should be crammed inside one class, or whether code should be divided into logical and functional units, which are then easier to maintain, extend, and manage. BTW, good luck reusing those component managers, physics engines, and input managers, all cleverly undifferentiated and completely coupled. \$\endgroup\$
    – jcora
    Nov 13, 2012 at 15:24
  • 1
    \$\begingroup\$ @Bane There are other ways to divide things into logical chunks than introducing new classes, btw. You can just as well add new functions or divide your files into multiple sections separated by comment blocks. Just keeping it simple doesn't mean that the code will be a mess. \$\endgroup\$
    – API-Beast
    Nov 13, 2012 at 15:38
13
\$\begingroup\$

Your current design seems to go against the first principle of SOLID design.

This first principle, called the "single responsibility principle", is generally a nice guideline to follow in order not to create monolithic, do-everything objects that will always hurt your design.

To concretize, your World object is responsible both for updating and holding the game state, and for drawing everything.

What if your rendering code changes / has to change? Why should you have to update both of classes that actually don't have anything to do with rendering? As Liosan has already said, you should have a Renderer.


Now, to answer your actual question...

There are many ways of doing this, and this is only one way of decoupling:

  1. The world doesn't know what a player is.
    • It does have a list of Objects in which the player is located, however, but it does not depend on the player class (use inheritance to achieve this).
  2. The player is updated by some InputManager.
  3. The world handles movement and collision detection, applying proper physical changes and sending updates to objects.
    • For example, if object A and object B collide, the world will inform them and then they could handle it by themselves.
    • The world would still handle physics (if your design is like that).
    • Then, both objects could see whether the collision interests them or not. E.g., if object A was the player, and object B was a spike, then the player could apply damage to itself.
    • This can be solved in other ways, though.
  4. The Renderer draws all the objects.
\$\endgroup\$
6
  • \$\begingroup\$ You say that the world doesn't know what a player is, but it handles collision detection that may need to know properties of the player, if it is one of the objects colliding. \$\endgroup\$ Nov 13, 2012 at 14:04
  • \$\begingroup\$ Inheritance, the world must be aware of some kind of objects, that can be described in a general way. The problem isn't in that the world just has a reference to the player, but that it might depend on it as a class (i.e. use fields like health which only this instance of Player has). \$\endgroup\$
    – jcora
    Nov 13, 2012 at 14:06
  • \$\begingroup\$ Ah, you mean the world has no reference to the player, it just has an array of objects implementing ICollidable interface, together with the player if needed. \$\endgroup\$ Nov 13, 2012 at 14:12
  • 2
    \$\begingroup\$ +1 Good answer. But: "please ignore all the people who say good software design is not important". Common. Nobody said that. \$\endgroup\$ Nov 13, 2012 at 15:00
  • 2
    \$\begingroup\$ Edited! It did seem kind of unnecessary anyway... \$\endgroup\$
    – jcora
    Nov 13, 2012 at 15:19
1
\$\begingroup\$

The Player should ask the World about stuff like collision detection. The way to avoid the circular dependency is not to have the World have a dependency on Player. The World needs to know where it's drawing itself: you probably want that abstracted further away, perhaps with a reference to a Camera object which can in turn hold a reference to some Entity to track.

What you want to avoid in terms of circular references is not so much holding references to each other, but rather referring to each other explicitly in code.

\$\endgroup\$
1
\$\begingroup\$

Whenever two different types of objects can ask each other. They will depend on each other as they need to hold a reference to the other one to call its methods.

You can avoid circular dependency by having the World ask the Player, but Player cannot ask the World, or vice versa. In this way the World has references to the Players but players don't need reference to World. Or vice versa. But this won't solve the problem, because the World would need to ask the players whether they have something to ask, and tell them in the next call...

So you cannot really work around this "problem" and I think there is no need to worry about that. Keep the design stupid simple as long as you can.

\$\endgroup\$
0
\$\begingroup\$

Stripping out the details about player and world, you have a simple case of not wanting to introduce a circular dependency between two objects (which depending on your language may not even matter, see the link in Fuhrmanator's comment ). There are at least two very simple structural solutions that would apply to this and similar problems:

1) Introduce the singleton pattern into your world class. This will allow the player (and every other object) to easily find the world object without expensive searches or permanently held links. The gist of this pattern is just that the class has a static reference to the only instance of that class, which is set upon the object's instantiation and cleared upon it's deletion.

Depending on your development language and the complexity you want you could easily implement this as a superclass or interface and reuse it for many major classes you don't expect to have more than one of in your project.

2) If the language you're developing in supports it (many do), use a Weak Reference. This is a reference that does not affect things like garbage collection. It is usefull in exactly these cases, just be sure to not make any assumptions about whether the object you're weak referencing still exists.

In your particular case, your Player(s) could hold a weak reference to the world. The benefit of this (as with the singleton) is that you don't need to go searching for the world object somehow each frame, or have a permanent reference that will hamper processes affected by circular references such as garbage collection.

\$\endgroup\$
0
\$\begingroup\$

As the others have said, I think your World is doing one thing too many: it is trying to both contain the game Map (which should be a distinct entity) and be a Renderer at the same time.

So create a new object (called GameMap, possibly), and store the map level data in it. Write functions in it that interact with the current map.

Then you also need a Renderer object. You could make this Renderer object the thing that both contains GameMap and Player (as well as Enemies), and also draws them.

\$\endgroup\$
-6
\$\begingroup\$

You can avoid circular dependencies by not adding the variables as members. Use a static CurrentWorld() function for the player or something like that. Do not invent an interface different from the one implemented in World already though, this is completely unnecessary.

It is also possible to destroy the reference before/while destroying the player object to effectively stop the problems caused by circular references.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I'm with you. OOP is too overrated. Tutorials and education quickly jumps to OO after a learning the basic control flow stuff. OO programs are generally slower than procedural code, because there is bureaucracy between your objects, you have a lot of pointer accesses, which causes shitload of cache misses. Your game works but very slow. The real, very fast and feature rich games using plain global arrays and hand optimized, fine tuned functions for everything to avoid cache misses. Which can result in tenfold increase in performance. \$\endgroup\$
    – Calmarius
    Apr 18, 2013 at 20:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .