This post is available in audio form.
Transcript of the Audio:
Music in this clip is from Lunova Labs [used with permission]
Hi, Chris DeLeon here for HobbyGameDev. Under the topic of “Organizing Projects as You Go,” Peter K. writes today: “I’m in school studying Computer Science now, and now we’re getting into multi-files, header-files, etc. I know there is no simple answer here, but I wanted to know what you thought about breaking up projects into multiple files, and how much should be done with each file. I don’t want to have 600 lines in main.cpp, if I’m only writing 620 lines in the entire project, but on the other hand, having 40 files splitting those same 620 lines sounds like an equally large disaster.”
Well Peter I’m glad you write, this is very much a fair and good question, and I think common to people new to this idea of splitting up their project into multiple files.
At a high level, for a very short project, for a very small project, and oftentimes for demonstrations you’ll see all of the code stuck in one file in an intent to try to emphasize the order of the content, or to at least make connections between them easier to see than when they span between files.
That said, one of the reasons why people split files is when the components are so separated that there is no sense of order between them. When their connections are only across a few different functions or interfaces. Say if we have all of our code for our enemy as opposed to the rest of the code in our game, separating that enemy out into its own file is a good way to avoid wondering “does it matter where in the order the enemy’s stuff happens, does it matter the enemy’s relationship in the file’s ordering as opposed to say the player’s…” Because it doesn’t really make sense for either of those to be above the other, putting them in separate files helps make them all parallel in a way.
Oftentimes what I’ll do is sort of delay organizing a project, at least on a small team… As is often the case with the sort of advice that I’m speaking from, the experience that I’m coming back from, this would be totally inappropriate if you were at IBM or Microsoft or Google developing some huge software project on a team. But if you’re a student, or a hobbyist working on your own, then oftentimes you kind of want to start in one big file as kind of your scratch space while you’re still sorting out what it is you’re doing, while you’re trying out different ideas. You can flexibly iterate in that space a lot more rapidly, because you’re not architecting your way into an inflexible structure.
One of the unfortunate habits people sometimes make when they’re first learning about multiple files is to, from the outset, plan out which files they need, create those files, then start trying to fill those in like skeleton code. It’s worthwhile as an exercise to explore that approach to doing things, and like I said if you’re doing the direction of a bigger company that can very much be a sound habit to develop, but on small projects where we’re still figuring out what we’re doing as we’re doing it: find a way to make it work. Often within a single file is the most straightforward, maybe even the most hackish way to do that. Then once you’ve found something where you’re really happy with the way it works, and you like what it does, and that’s the foundation you want to build upon, then you refactor.
Refactoring is the word for when you make an overhaul to the code that basically, after a bunch of work you do, it still does the exact same thing [but the code is then easier to extend, maintain, explain, etc.]. So your code does something, you do a bunch of work to it, and afterwards, the code still does exactly what it did before all of your work. But the benefit is, when you refactor, that’s when you’re doing the work of separating out the separate parts, of pulling into separate files where it makes sense semantically separating objects from other objects, concepts from other concepts in the code. Networking code might be mostly in its own file, graphics code may be mostly in its own library if you want to have a layer between how you’re drawing it and how you’re interfacing with the graphics code you have access to.
There are different ways to bud it out. But really you want to wait until you figure out exactly what it is you’re doing, what works, before you start pulling it apart.
You can do this in layers as well, you don’t have to jump straight to doing extra files. One way I tend to do these things is, like I say, initially a lot of it lives in one big file, occasionally just a handful of big functions, and once I’ve got it doing basically what I need, I’ll begin by chunking out within the functions I’ll put comments heading each chunk of code to comment what I’m doing there. What’s this next block of code do, what is this nestled for-loop for, what am I accomplishing with each piece? Some of those, it becomes immediately apparent in doing that, oh that makes sense really as its own function. It shouldn’t be spelled out here, it should be pulled out into a function so I can label that functionality.
Sure enough, once you’ve done a few of these, and have at least a handful of functions you’ve pulled out of what was your master class, master function, whatever, you may realize that these can be grouped. Oh, all of these functions that I call have something to do with AI, or have to do with the way I draw the level code. Then I can just pull all of those level code drawing functions, or collision, other functions I pulled out about levels, I can shove all of those into their own file just for level stuff, or just for graphics stuff, just for network code.
That way you can get this process working. First figure out how to make it work, all it in one place, how can you make it work while maximizing the speed of your iterations and see them on the fly. After that, chunk that code by putting comment headers above each block to describe to yourself what you’re doing with it. You may then see from those that some of those would make more sense as their own function, so pull those apart as their own functions. Call those from where you had the comment header. Then if you start to have groupings of functions where they relate to each other and not so much to the rest of the code that’s there, pull those apart into their own file. That creates – then you’ve got a place for when you want to write more level code, you have a whole separate file to stick it in. When you want to write more enemy code, you have a whole separate file to keep track of that in.
It just kind of helps to keep the messes separated, and minimized. It will also make your code easier to parse by other people, easier for other folks to look at and make sense of it. If someone wants to download your source and modify how the weapons work, they can jump straight to the file that has weapons code in it, and not have to untangle how it all tangles together in one big overarching scheme.
But that’s it for me. Sort of a shorter message, but hopefully it’s been useful. There’s a bit of a process to it, and a lot of it’s personal preference or a lot of it’s experience. And the answer’s different if you’re working alone as a student or on a small project team as opposed to at a huge company or corporation where you’d have to architect and plan out these things in advance to distribute the workload better.
Thanks for the question, hopefully it will be useful to other folks!
This is Chris DeLeon for HobbyGameDev, signing out.
[Music from Lunova Labs, used with permission]
Subscribe by e-mail to receive an update every month of all new HobbyGameDev articles, videos, and game development resources