Game data container

Started by
10 comments, last by 0r0d 6 years, 5 months ago

A big game project contains a lot of runtime data like

  • List of all world objects
  • List of all players
  • Game states
  • Viewport
  • ...

I have to pass a lot of these data to most of my classes. So I have a huge list of parameters in every constructor.

  • What's the best practice to avoid this mass of parameters?
  • Is it recommended to summarize all data to one class (something like "GameData") and pass this container to all methods and classes?
  • Is there a common name for this container in game development?

I'm a bit worried, because if I pass this class to all classes and methods, I get something like a global variable.

Thanks a lot for your advice!

Advertisement
22 hours ago, templar_vii said:

I have to pass a lot of these data to most of my classes.

No, you don't. And that is how you solve the problem. :)

Don't try to look for one magical pattern or process to solve this issue. What you need to do is look at each of these things, and find ways to refactor to the code so that you don't need to pass them. Keep doing that until you're happy with the state of the code.

An example to get you started: an Enemy class may need to be able to know where the nearest Player is, to attack it. And it might need to know about all other world objects, so it can walk around them. Instead of passing in "list of all world objects" and "list of all players", pass in a GameWorld object, and ensure that GameWorld has functions like "FindNearestPlayer" or "FindNearbyWorldObjects" so the Enemy can query for what it needs from one single object that represents the environment.

Thanks a lot for your response.

But that means, you have a container, named "GameWorld" which contains all the data of the game?

No, in large systems you have a bunch of systems.  Most code works within that system only.  Sometimes code needs to access from another system, and when it does, that's the only time you pass along a reference to the system. In that case you have an interface to use the public parts of the system, and that one handle is enough to get the pieces you need.

Generally the interfaces are commands, "do this big thing", not actually touching the data or manipulating implementation details.

In the example of a character in the world, they are already in the world so they should already be in the same system, or have access to, other information about the game world they are in.  They could then query their game world to find the nearby items they are interested in.

Note that the character is not doing the scanning, they are giving a query command to the system and getting a result set back.

I understand.

There's a lot of refactoring work, but I guess it will be worth :)
I hope that makes my code clean enough, to publish my Project (https://www.microsoft.com/de-ch/store/p/high-treason/9wzdncrfjgpz) as open source in GitHub.

Thanks a lot for your detailed explanations!

On 10/31/2017 at 3:20 AM, templar_vii said:

 

I have to pass a lot of these data to most of my classes. So I have a huge list of parameters in every constructor.

 

Can you give examples of this in your code?  

That's exactly what the constructors look like in my project.
My constructors have up to 12 parameters.

But I guess, there's no way to pass less data.
Maybe I can group some classes in a superclass.
And I should better encapsulate the data access, as the "frob" has described.
But it will remain confusing.

1 hour ago, templar_vii said:

That's exactly what the constructors look like in my project.
My constructors have up to 12 parameters.

But I guess, there's no way to pass less data.
Maybe I can group some classes in a superclass.
And I should better encapsulate the data access, as the "frob" has described.
But it will remain confusing.

Sounds like you're doing something wrong.  I mean, sometimes I do have a need for a constructor with a bunch of parameters, but most of the time I just have 1 or 2 at most.  If I do find myself with that many, then I refactor it to get rid of them.

If you post an example of a constructor where you're passing in this many parameters, we can get a better idea of what we're dealing with here.  It's hard to give advice about code without seeing the code.

Usually too many dependencies, be it data or behaviors, are caused by classes that tries to do too much. Or, in some cases, that you need to pass data from class to class up a looooong stack of methods and functions. Either way, this might lead to code hard to maintain.

Some other things that might help:

  • Prefer composition over inheritance
  • Stay alert for opportunities to use some type of event bus for communication and data retrieval (good alternative if you keep up references to data only to pool their state)
  • Prefer horizontal relationships of classes, instead of vertical (classes that works together, but does not own or inherit from one another)
  • Mitigate responsibilities and abstraction in a way that a big system can point you to smaller systems that point to even smaller systems. Pass the most abstract level that still makes sense around constructors.
  • Avoid premature optimization. It might be the case that, trying to make things run fast, you made the code hard to work with even faster.
  • Specialize. Specialize. Specialize. Classes that do less often have less dependencies and are easier to coordinate with other classes. SRP (Single Responsibility Principle) is powerful.

 

I guess, I ignored the rule "don't have classes that tries to do too much".

Tahts an example of a terrible constructor.
The "StateMachine" is a class which manges diffrent game-states and the conditions to change from one state to an other.


public StateMachine(
        GameState gameState,   // Container of the current state, the last state and the events
        WorldEvents worldEvents,   // Helper to add or remove items into the world
        ResourceLoader resourceLoader,   // Language-Helper to get a  translatet text
        GameSettings gameSettings,   // Options of the game
        List<CommandBase> commands,   // All classes that gives the opurtunity fot user-interaction
        List<AiBase> aiAlgorithms,   // Logic of AI
        List<WorkerBase> helpers,   // Alle other helpers
        ServerCallbackSynchroniser serverCallbackSynchroniser,   // Helper class so sync repsonses from the server to the game-Thread
        GameLauncher gameLauncher)   // Helper class to start a battle

Why the hell is "AI-Algorithms" and all the other stuff needed in a simple class like "StateMachine" ? Why not just passing the "GameState" instance?

That's what I thought now by myself.

I have to tell, that this is my first big game project. It started small and grown. I did some code refactoring but I never cared about splitting classes. So the design get worse and worse. Some weeks ago I started reading "Clean Code" of "Robert C Martin" and I noticed theres something bad, but I had no idea what's the solution.

I very like all of your advices. If I follow them there's a chance.
Thanks a lot!

This topic is closed to new replies.

Advertisement