How to structure a game in OOP

Started by
19 comments, last by L. Spiro 10 years, 10 months ago

Hi, I have rewritten my project several times to adapt to new game structures. I do this to conform to rules such as, no static or global variables, no singleton patterns and the single responsibility principle.

The application consist of class objects. I often find myself creating a "super" class to only make one instance of it. Such as a Renderer class. This class handles the window, resolution, fullscreen toggle and draws the scene.

Another class is the Asset Manager, one instance is created and it handles loading files and preparing them for later use. It keeps an array of all assets. Since the Asset Manager needs the device to create resources (VB, IB, Textures) in the managed pool, I keep a private pointer to the device in Asset Class and make a copy in the constructor. This cross object dependancy is to conform to the "rules", maybe there is a better solution?

The Entity class has one instance, it has a list of (pointers to) Player objects (struct). These player structs contain information such as what Skeleton to use, what Mesh to use, and character variables.

When rendering I pass the list of Entities to the renderer which looks up the Asset Manager for the resources and draws them as described in the Player object.

There are also other classes such as the animator and gui but I kept those out to keep it short. Is this a good game structure? What should I pay attention to, and are there any other game structures I should consider?

The pseudo-code:


main(){
InputOutputClass IO;
DeviceClass Renderer;
AssetClass Assets(Renderer.GetDevice());
EntityClass Entities;
GuiClass Gui(Renderer.GetDevice());

MainLoop(Renderer, Assets, Entities, IO, Gui);

}
Advertisement

I do this to conform to rules such as, no static or global variables, no singleton patterns and the single responsibility principle.

Sorry that has got to be the absolute worst reason to rewrite your code. The end user doesn't care about any of those things, only results.

Look - forget the "rules" and begin to think only about results. I'm going to be brutally honest with you - nobody else will ever look at your source code for whatever game you're making. If you're lucky they *might* look at the current version of the game as a finished product running on a platform. They won't know or care if you followed the "rules".

Anyway you're design is pathologically simple and uses far too much syntactic sugar to do far too little. Please think about the kind of game and features you need and design your classes around that. Are you making a 3d game with many 3d actors? Can they all share the same skeleton in a scene graph class? Can they share keyframe animations, what about textures, etc.

Ultimately what should force you to scrap a design and start over is if it doesn't scale in terms of size or speed (or both).

Sorry that has got to be the absolute worst reason to rewrite your code

Totally agree 100%. The only reason you should rewrite this stuff is if you find it stops you from extending your game or is inflexible in some way. Refactoring just to avoid other peoples opinions of what is bad software engineering is just a time suck when what you should be concentrating on is getting things done.

Sure some people don't like statics, globels or singletons but that doesn't mean they should be avoided entirely. Each has its use cases.

I often find myself creating a "super" class to only make one instance of it.

Premature abstraction is the object-oriented root of all evil.

Use a good refactoring tool, go over the code and try to remove all unnecessary abstractions. Then, go over the code again and see if you find code duplications. Those are the candidate points for introducing new abstractions. Repeat this process a few times and you should end up with a much cleaner framework.

openwar - the real-time tactical war-game platform

But if I don't bundle things into an object, I would have to either keep them global or have massive amounts of arguments to pass around, creating a huge web of dependancies. Is it bad to make a class if only one instance will be made from it? Surely classes are not only for abstraction?

Is this class just a pod?
couldn't you just have a struct with a global instance?

The render class holds everything from device to shaders. It's the glue between my assets and the Graphics API. And in this class I have functions like 'ChangeResolution(MyStruct Params)' and Render(MyClass *Entities).

I read that the only difference between struct and class is that members are public in struct by default and private in classes by default. So changing they key word 'class' to 'struct' has no meaning.

Bundling stuff in classes or structs is the same, but is it proper to use it in this case?

Yes, it's good practice to put your state inside of objects, even if there's only one instance of the class. The question is of course where to put each individual variable. If you're not satisfied with the architecture but you don't know what to do, one approach is to just shuffle the code around and see what happens. Experiment with merging and splitting classes and functions. Look for new names, and try to make them as concrete as possible. Poke around and see if you find any redundant duplications or code smells. It usually takes a few iterations before the architecture settles.

openwar - the real-time tactical war-game platform

It sounds to me like you have a lot of questions but few answers. If you want my opinion, before you start coding, take a step back and draw your application on paper. Make sure you understand the interactions between the different subsystems, and how you want them to communicate with eachother. Get a real understanding of how you WANT the system to work, then you can refine your design. However, if you just jump in and start making a mess, and hope to figure it out as you go along, you're going to run into problems, and just make your code messier.

These are some things I like to avoid in my design:

  • Global state. There's just no need. Any situation where a Singleton is a solution, there is often a better solution that doesn't use a Singleton. This is an opinion I formed after using Singletons for a long time, and gradually realizing how evil they are the hard way.
  • Avoid global event systems. They are a nightmare to debug. Prefer a callback/delegate architecture. Another thing I had to learn the hard way.
  • Make sure your subsystems are compartmentalized. For instance, there is no reason for a sound object to contain a pointer to a physics object. Keep your systems separate and join them using a higher level interface.
  • Make your APIs easy to understand, and difficult to use incorrectly.

Good luck.

  • For instance, there is no reason for a sound object to contain a pointer to a physics object. Keep your systems separate and join them using a higher level interface.

Well, this is where it gets tricky for me. The APIs for sound and graphics are different systems. I can encapsulate them in my own classes and simplify the interfaces for my use. But for example there has to be a place in the code where a sound is triggered when the animation passes a point. We are now far down a method in the animation class and we discover that we need to trigger a sound. Where is the pointer to the sound interface when we are in the animation system?

Globals?

Singletons?

Coupling by having a pointer to the sound system as a member in the animation class?

Having a higher level interface that calls the DoAnimation() method, I need return values in order to call DoSound() afterwards. But I just shuffle the burden up the food chain. Now I have to code a ton of different states that must be passed and returned and bloat the calling code, making it more complex.

This topic is closed to new replies.

Advertisement