What is the most organised way to create a class/file structure for a game?

Started by
7 comments, last by BiiXteR 8 years ago

Note : I do know this is mostly opinion based, however I would like to see other peoples opinions on it, and how they prefer to create a class/file structure in their games.

So far I've only made simple games where I have mostly just placed all code in my main.cpp, which doesn't look very nice.

I'm wondering how you create a nice file and class structure for your games.

To example :

Do you create a window class for your window, a render class for your renderer or do you throw in both window and rendering in a single class?

Do you maybe ( for some reason... :3 ) find it better to throw everything into your main.cpp file?

I mostly want to know who you do the file structure for the "core" engine, such as window, renderer, text and input. (I might have missed some things)

I do not "need" to know how you setup your Entity, player (etc) classes, since it would probably be a much broader question that it already is.

Advertisement

I generally produce one file for class or type, except in languages like C++ where I generally produce one .h/.cpp (and maybe .inl) tuple.

Free functions a grouped into one file (as above) based on functionality (e.g., string-related functions go into one file, hash-computation functions into another file). Small types like enumerations or delegates generally go in the file that they are associated with (window property enumerations would go into the window class file), except in some circumstances where they are useful on their own or to a wide variety of types.

ive been looking into this recently. here's what i've come up with so far. this is for real time non-mission based games. turn based and mission based (run a mission, then between-mission menus, etc) are similar.

modules and their public methods from highest level to lowest level: note that constructors and destructors might be used for init and end methods.

1. program(init, run, end) . program.run is the main menu (continue, new, load, settings, help, quit).

2. game (init run end). game.run is the main game loop.

3. render_all, process_input, update_all, and play_sfx_all. render_all simply sets up the camera and such, then calls the render routine for all visible objects.

process_input handles input. if a game object is selected, its input handler (interactions menu or whatever) is called. update_all makes the calls to update the game objects, game clock, and game world. play_sfx_all plays/stops the ambient sfx for any nearby game objects. a list of game objects might have a play_or_stop_sfx method. it would play or stop the sfx for each object in the list.

4. game specific code that uses more than one type of game object, such as distance from player object to some NPC object.

5. lists of game objects: PCs, targets/NPCs/monsters, projectiles, dropped objects, etc.

most games i've done have a player, targets, and projectiles at the very least.

Caveman 3.0 has players, NPCs/monsters, projectiles, and world objects.

SIMSpace v8.0 has galaxies, star systems, starports, targets, projectiles, and particles (streaming stars).

Airships! v1.0 just has a player and targets. projectiles are just another kind of target. and the player is a target too. player info is copied to their target as needed. this way generic target methods can be used for the player as well as targets. the game has many more variables for the player's ship than for other types of targets, otherwise a player object would not be needed. just a variable for which target was the player's ship. and a single target list could be used. this would work for a simple game like doom, where there's not much additional data tracked for the player vs a target - IE just handful of ints for inventory levels and current weapon. pretty much everything else: hp, dp, location, orientation, etc is data that both player and targets have, in that type of game.

Typical methods for lists of game objects: add, remove, render, update, play_sfx, handle_input, etc. not all types of objects require all methods. these may be list or list entry methods, whichever makes more sense. list entries need not be separate objects. the list itself can be the fundamental data type in some/many/all cases. most games have a few lists for different types of objects.

6. camera object. can_see_location() method. does clip range and frustum cull, plus any other game specific culling used.

7. low level game specific routines such as world coordinate systems for large game worlds.

8. low level generic game library or engine code such as render queue, timers, asset pools, animation systems, etc.

9. directx and win APIs (or OpenGL and Linux, etc).

entities should be composed of generic drawing and location info that you can pass to and from the physics and AI engines, and can pass to the render engine.

write generic code that uses locations and drawing info. to process game objects, extract their locations and drawing info, and pass that on to the generic routines.

when i say "composed of", i mean composition vs inheritance, not components as in ECS.

as for what to put in separate files:

any module that you want to be able to link by itself to a program should be a separate file.

modules that use other modules should auto-include. so your generic GUI module which uses your custom font module should auto-include your custom font module.

APIs that are typically used together can go in a single file (compiles and links faster). but for organization sake, you may want separate modules, along with a wrapper module (basically just a .h file that includes the other modules). this keeps the files S.R.P friendly.

in the end, its a mater of personal preference. Caveman 1.0 had about 20 code modules. all nicely divvied up as to what they did and such.

Caveman 3.0 is just 3: gamelib, audio, and game. all the generic modules except audio are in the gamelib. all the game specific code (about 125,000 lines of it) is in the game module. all the code is still divvied up into separate modules with good SRP. they just appear one after the other in the same file, that's all.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

for game object lists, load and save might be added to the list of add, remove, render, input handler, update, and play_sfx as possible public methods.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

for globals like quit game, quit program, current _view (game state in multi-state game), i was declaring locally and passing down, but i think i may favor a global struct with public load and save methods, as it more closely parallels the organization of object list code.

it would be very nice if you could get it down to just two basic types of modules:

1. wrapper menu modules with init, run, and end,

2. game object/data modules with add/remove/render/input/update/load/save/play_sfx as possible public methods.

using a global struct makes the globals a data module. the primary motivation for this was that declaring them locally, i had no nice load and save mechanism. but as a separate module, they're just another module with public load and save methods to be called by the main load and save routines.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Long story short: separate everything. And everything should handle one thing.

If you're using C++, then you would make a yourName.h and yourName.cpp for your classes.

  • sound.h, sound.cpp
  • image.h, image.cpp
  • event.h, event.cpp
  • window.h, window.cpp
  • etc.

I can say this because I've been down that road, but never ever put everything in main.cpp if you're working on a bigger game. Not only is it hard to look at, but it'll kill your motivation too. Nobody likes walls of text, right? If your project is a pie, then you would put the slices on different plates. This is known as the Single-Responsibility Principle (SRP).

In my opinion, if something can be separated, then it definitely should be separated. Don't worry too much about having a lot of files. Heck, don't worry about splitting even the most trival things apart. If you look at SDL, it follows this SRP design pattern. And the bonus is that you will have smaller files that are easier to look at, manage, edit, and what have you.

Long story short: separate everything. And everything should handle one thing.

If you're using C++, then you would make a yourName.h and yourName.cpp for your classes.

  • sound.h, sound.cpp
  • image.h, image.cpp
  • event.h, event.cpp
  • window.h, window.cpp
  • etc.

I can say this because I've been down that road, but never ever put everything in main.cpp if you're working on a bigger game. Not only is it hard to look at, but it'll kill your motivation too. Nobody likes walls of text, right? If your project is a pie, then you would put the slices on different plates. This is known as the Single-Responsibility Principle (SRP).

In my opinion, if something can be separated, then it definitely should be separated. Don't worry too much about having a lot of files. Heck, don't worry about splitting even the most trival things apart. If you look at SDL, it follows this SRP design pattern. And the bonus is that you will have smaller files that are easier to look at, manage, edit, and what have you.

What about rendering, I'm still kinda confused on where I should do that.

Should I do it in window.cpp/h or should I do it in it's own class?

Do rendering in its own class. It's not the window's responsibility to render. And rendering can occur without a window at all.

Do rendering in its own class. It's not the window's responsibility to render. And rendering can occur without a window at all.

Alright, thank you :)

This topic is closed to new replies.

Advertisement