Organizing Game Code

Started by
7 comments, last by Alberth 7 years, 2 months ago

What would be a good way to organize code for a game (specifically in Python 3.x)? Currently, I'm working on a 2D game. I have one file for gameobjects (gameobjects.py), one file for components (components.py), and a main file (main.py) where I write the game and import everything else (object creation, input, drawing, and updating happens here). Should I be breaking down things further for readability? Like each gameobject/component in its own file? Or all object creation/input/updating/drawing logic in its own file instead of main.py? What would be a better approach? I only ask because I find my files getting quite large lately.

Advertisement

I think the only requirements are that you don't have cyclic imports, and you have to consider it easy to work in.

Giving general considerations is somewhat hard, as both requirements are very much dependent on the actual code, and ones' preferences.

For Python, it makes sense to make a package for it, ie https://docs.python.org/3.6/tutorial/modules.html#packages which gives you room to organize things as you see fit.

With class hierarchies, you have the "everything in one file" option (ie what you have now), the "each class in a new file" option (eg Java does this), and the "something in-between" option. The first option causes large files and making it harder to find a specific class, but importing is simple, and it has convenient editing (as one file contains everything). The second option causes lots of small files. Finding a class is simple, but editing a bit harder, since you may have to open a lot of files to make some change over the entire class hierarchy. If you regularly need to import all classes, that's inconvenient too, as you get long import lists.

The third option is in-between both extremes. In general, here it makes sense to split off common parts to a new file (eg base classes), so you can import that file into several other files. For the non-common code, any reason to split is a good one, like big/small, type of case it deals with, level in the game, and many others.

It's useful to document how you split up the file, so others can understand the system you used for creating the files.

With bigger projects, it makes sense to delegate details to separate parts, to avoid the main code having to deal with all the details. Eg you could make a "Level" class with level description (initial forces, win conditions, initial map, etc), a "World" class, containing the objects that exists in the game world, a "Render" class that takes the world and displays it to the screen.

It doesn't need to stay with just 1 level of parts, if you consider it useful, you can create sub-parts, sub-sub-parts, etc.

How to organize your project is actually one of the harder parts of writing code. That's probably why a language like Java is very rigid about how source files are laid out on the host filesystem.

My favourite source layout for Python is to break the code into sensible subpackages (which is a folder with an __init__.py file), each subpackgae consisting of sensible modules. By sensible, I mean of a reasonable size for editing and functions and classes grouped coherently. Sure, vague words, but the reality is 'it depends.'. Corresponding to each subpackage is a unit test suite, with plenty of tests for each module. I consistently use 'from packge.subpackage import module' format for importing names, which miraculously self-documents where the code comes from in the host source files, namespaces things correctly in the (not infrequent) event of name conflicts, and makes unittest.Mock work without anger because you're going to want to mock out dependencies in other subprojects. I also tend to have a public subpackage interface (exported through the __init__.py file) and a private interface used only in the unit tests.

I put as little as possible in the main file. I prefer many smaller modules over one big one, with the important metric of a module size being the number symbols exposed by its public interface and not lines of code.

Stephen M. Webb
Professional Free Software Developer

Also look into using virtual environments. They will allow you to isolate your Python projects, and keep you from "polluting" the global Python environment with a ton of project-specific modules.

Thanks for the reply's. For now I'm thinking about just keeping my components and game objects in their own separate files for simplicity, until I find that things get out of hand. For my main file though, I would like to separate it into three files; one for input, one for updating, and for drawing. Is this a sensible thing to do? Also, how could I ensure that all three files will be able to interact/change common objects? Will I need a file that loads all my objects/resources to then some how call these files?

For my main file though, I would like to separate it into three files; one for input, one for updating, and for drawing. Is this a sensible thing to do?

If it makes sense to you, it is.

The architecture of the solution follows how you see the solution of the problem that you are solving. In other words, if you see "input", "update", and "draw" as different things, your solution will split them in that way, and your architecture will have these things as some kind of separate entities.

If you see them as 3 variations of the same thing, for example, you'd make a solution that expresses that idea, and the architecture will show that as well.

Also, how could I ensure that all three files will be able to interact/change common objects? Will I need a file that loads all my objects/resources to then some how call these files?

Make a common "main" file. It imports the 3 parts, creates the objects, links them to each other, and says "let's play" to whatever fires off the game.

For imports, think in layers. Each file lives in some layer. It may only import files from layers beneath it. In that way, it's impossible to get circular imports.

Thanks for the reply's. For now I'm thinking about just keeping my components and game objects in their own separate files for simplicity, until I find that things get out of hand. For my main file though, I would like to separate it into three files; one for input, one for updating, and for drawing. Is this a sensible thing to do? Also, how could I ensure that all three files will be able to interact/change common objects? Will I need a file that loads all my objects/resources to then some how call these files?

That seems like a sensible separation of responsibilities to me. Following Bregma's suggestion, you could even make "input", "graphics", and "game" into sub-packages, each with its own modules for handling functionality related to that engine component. For instance, the "game" package would be a great place to put the aforementioned gameobjects.py and components.py modules.

For my main file though, I would like to separate it into three files; one for input, one for updating, and for drawing. Is this a sensible thing to do?

If it makes sense to you, it is.

The architecture of the solution follows how you see the solution of the problem that you are solving. In other words, if you see "input", "update", and "draw" as different things, your solution will split them in that way, and your architecture will have these things as some kind of separate entities.

If you see them as 3 variations of the same thing, for example, you'd make a solution that expresses that idea, and the architecture will show that as well.

Also, how could I ensure that all three files will be able to interact/change common objects? Will I need a file that loads all my objects/resources to then some how call these files?

Make a common "main" file. It imports the 3 parts, creates the objects, links them to each other, and says "let's play" to whatever fires off the game.

For imports, think in layers. Each file lives in some layer. It may only import files from layers beneath it. In that way, it's impossible to get circular imports.

Currently, my main.py file imports from a file called myutil.py which contains utility functions to help me load resources such as images (among other things). But I'm also importing myutil.py in my components.py file. All files are currently in the same folder as I have not restructured my project yet. What would be a better way to handle this situation when I do decide to structure my project? If I create a package for myutil and components they will exist on the same directory level below main, which I'm assuming I should avoid if the components package will depend on the myutil package?. Should I code in a way to avoid this situation completely?

Import hierarchy and package hierarchy are two different things (related in some complicated way, no doubt). You can decide to make both hierarchies the same, but it's probably a bad idea, since the hierarchies address different things.

The import hierarchy solves the "which file can I import" question.

The package hierarchy reflects the structure of your solution. At some point you have too many files to understand their structure, so you move a few together in a package directory for easier navigation and understanding.

As related code imports each other, and you group related code into packages, the import hierarchy will have (mostly) "layers" of packages. If you have a "building_data" package and a "image_loading" package, then it makes sense to have imports from "building_data" to "image_loading", but not the other way around. This means you can start thinking packages when importing. "I am in package A, it needs to import stuff from package B, does that make sense to me?" If yes, all is fine. If no, it's time to reconsider some of your ideas.

There is no need however in the above example to have package A "above" package B, in the package hierarchy. On the other hand, don't go overboard, and make A a sub-package from B. Python won't mind, but your brain will start to hurt as it's counter-intuitive in the design

This topic is closed to new replies.

Advertisement