6
\$\begingroup\$

By far, the biggest problem that has always dawned on my when programming games is how to structure my code. It just becomes an incredible mess, after a while. The reason for that is because I have no idea how different classes should interact with each other.

For example, say I have a Player class, a PlayerInput class and a Map class. The Player class contains information as to the location of the player, whereas the PlayerInput class handles changing that location, but by first making sure it is within a walkable area, using the Map class. How should I structure this?

My usual approach is to pass these components, as parameters, in the constructors of the parameters that need them, like so:

var map = new Map(); var player = new
Player(); var input = new
PlayerInput(player, map);

The problem with that is that it quickly gets messy; when you add new components, you have to go through your constructors and update them, and it doesn't work well if you have mirroring references:

var physics = new Physics(input); //Oops, doesn't work
var input = new Input(physics);

How should I manage my game objects and components?

\$\endgroup\$
0

3 Answers 3

11
\$\begingroup\$

No lie, architecting games (or any other simulation-style program) is harder than your average business app. When you have lots of different entities that all need to interact then in the code they'll need to, well, interact. Letting them do that in a maintainable way is tricky.

A component pattern can help, but even then figuring out how to wire the components up is non-trivial.

To your specific question:

Say I have a class Player, a class PlayerInput and a class Map. The player class contains information as to the location of the player, whereas the player input class handles changing that location, but by first making sure it's within a walkable area from the map class. How to structure this?

In this case, I generally order the dependencies by how the information flows:

  1. User input comes in. That defines how the player wants to move.
  2. The player's behavior and animation modify that to determine how it can move (things like preventing a jump if you're already in the air).
  3. The collision modifies that to determine how the player does move.

So collision refers to animation which refers to user input. Any of those dependencies can be decoupled by hiding them behind an interface. So, for example, the animation system just needs to know the direction the player wants to go. It doesn't care if that's coming from user input or from something else (like AI, a replay script, or demo mode) so all it needs is an instance of some IPlayerMovement interface.

and it doesn't work well if you have mirroring references:

If you can't avoid a circular reference altogether, you can always break it by introducing an interface:

// Problematic:
class A {
  A(B b) { /* ... */ }
}

// Bad:
class B {
  B(A a) { /* ... */ }
}


// No more cycle:
interface I {
}

class A {
  A(I i) { /* ... */ }
}

// Bad:
class B implements I {
  B(A a) { /* ... */ }
}
\$\endgroup\$
4
\$\begingroup\$

In your particular example, typically one system is more "important" than the other, or somehow the relationship is not exactly symmetric (one relies on the other more), and that's the opportunity to do something like the following:

var physics = new Physics();
var input = new Input(physics);
physics.attachInput(input);

This is not uncommon; for example in Java's Swing GUI system, the BoxLayout LayoutManager requires knowing about the component that it is laying out; so you must first initialize a component (which assumes a default LayoutManager), and then initialize a BoxLayout passing it that component, and finally do a setLayout of the BoxLayout on the component. It looks like the following (taken from this Java documentation page):

JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
\$\endgroup\$
1
\$\begingroup\$

Good question. One away around these types of interdependency on subsystems, like input, physic, rendering, etc, is to use the services & singleton pattern together. Each subsystem could be modeled as a service, and the game only has one instance of that service at a time (sub system) that gets created when first called.

At some point though, your going to have a chicken and egg problem if the creation of your subsystems depends on the creation of another subsystem that depends on the creation of the other.

So, avoid the above circular creation problem, and also use static singletons. For example: Input.getInstance(), Physics.getInstance(). Both of those getInstance methods could create and return the subsystem on the first call, but then return the sub system on other calls (singleton pattern). So the first time Input needs the physics sub system it gets it via the singleton service call. Read up on the Gang Of Four Design patterns for more information on those patterns.

\$\endgroup\$

You must log in to answer this question.

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