Managing a growing project

Started by
14 comments, last by a light breeze 5 years, 9 months ago

Hello all! Thanks for taking the time to read this post.

This is a bit of a strange subject, but a very important one nonetheless in my opinion. I've been programming for about 7 years now, and over this duration I've made over 10 small but complete games. Over the past few years, I started working on a bigger project, and this has been through two prototypes. I've noticed that every time one of these prototypes reaches a certain size, the code becomes more and more intertwined and interdependent to the point where productivity slows to a halt and eventually just stops. 

A lot of people tell me to "create small games" and "see them through to completion", but I have indeed done this, and this problem seems to persist. I'm not working on something overly ambitious or impossible to complete, but it always seems like when I try to create a game with any kind of substance or complexity, coding very quickly becomes this awful chore of backtracking, and progress slows and slows to a halt. I've done lots of research into programming patterns, and I write my code very meticulously with lots of comments and strict formatting, but I can't seem to jump this barrier.

For the first time ever, recently, I decided to COMPLETELY document every single aspect of my game, so that creating content won't involve me having to over-engineer and future-proof my code in the hope of future additions. This way, I can completely design the architecture around this content and then shut the doors and agree to not add any more content, no matter how tempting it may be. However, I'm not too sure how to go about starting a new project in a way that will ensure quality control and a nice balance of readability/cohesiveness/modularity.

I wondered if any of you have the same problem, and if so, how do you go about combatting it? Is there a tried and tested way create an architecture which allows the addition of code without causing a rippling echo throughout all existing code? Is there some form of diagramming or pre-planning which can help minimize this risk later down the line?

I eagerly await your reply!

Thank you,

Zuhane

Advertisement

What I've learned from the years I write code now is that it dosen't always depend on comments because comments don't make a good code base. Thinking about what should be achieved with the code base before writing the first line is much more important because I ended up refactoring a projects logical structure in the past more times I refactored code from changing content. This is why I splitted large projects into a more modular structure where I have a core code base that is modular extended by functionality of logical different topics.

In C++, this is quite simple by splitting the project into standalone projects and linking them together as static library while you havent this kind of convinience in C#, you can split everything up into several libraries to include into your final application.

My development tools for the game engine written in C# stay all in the same directory but have there own subdirectory. Anything is build into the root directory (Tools). I then have a core library that contains certain subset of C# code that any other of my tools shares partially. This way I have the same code base for all my tools.

Using my dev tools leaded to having a shell application that is dynamically loading my tools into one single program during runtime. It is capable to do all the build setup, remote attach to my game engine and even manages the code base by managing the modular parts in packages. I took this from NPM here but a little more simplified enabling my development process to dynamically drag in/out code packages/ modules of my current project depending on the requirements I currently have in this project.

If you are working in Unity, you are something bound to the engine (I took Unity in here because you tagged just C#) so its project structure and building pipeline. But managing your modular code in several subdirectories should work here too and you are also able to declare code as your core or main module.

Second, using namespaces. I know that this is a topic to discuss about because half the people tend to not use them while the other half overcomplicates namespaces. Namespaces are a great feature to logically split up your code into functional sections. This means that IO classes should live in there own namespace, encryption classes should live in there own namespace and so on.

Third and least, keep it simple and be as lazy as possible.Simple class names, simple but meaningfull function and variable names make life easier. Using consts for constant values makes it easier to change them in certain code base and most of the time you just need static functions (from my experience) so making a class for everything is pointless. With being lazy I mean that a lazy coder wont copy/paste the same code, do repetitive stuff by hand (just like filling out a table of key codes) using some kind of quick n dirty tool to do the work and writes as less comments as possible except for those that are meaningfull too ;)

These are some of my experiences I made, someone will have other experiences made and at the end you have to choose what fits best to you

Hey Zuhane.

I recognize your effort, 10 games is a certainly an accomplishment, no matter the size of the games in question.

I'm sure that throughout that endeavor, your experience in programming has grown. However, our experience grows in many ways, and programming, overall, is a big beast to tackle. So, like always, there may be areas in which we need to improve that we haven't been paying much attention to.

I see 2 possible problems here:

1) A classic problem of tightly coupled code. What this means is that your method clearly forces apply several changes in your codebase whenever you need to apply a small change somewhere. This is a problem that gets more and more noticeable, the larger the size of your code base, and is usually solved through programming patterns, software architecture, and similar software engineering solutions.

However, you mention you've done a lot of research into programming patterns, which either means you're lacking in your program architecture, or...

2) Perhaps you're attempting to make projects that are simply too big for one single person, and need to consider bringing additional programming brawn. You don't mention whether you're alone or not, so I don't know if this even applies.

I hope any of these alternatives can help you in your problem :)

Sorry for the very late reply! I've been looking into a few different solutions and didn't want to rush back in without considering options etc. I think you may possibly be right with point 2, but I've noticed that even fairly small projects seem to eventually fall under this curse.

I recently switched over to Unity to give it a try, as I've heard lots of good reviews, especially when it comes to modularising certain aspects of your game. However, after messing around with it, I found it much easier to make games using good old code instead of the Unity IDE. I think Unity's great in a lot of aspects, but it seems like everything is geared towards "nooby" graphical tech demos. I get that you could churn out a walking simulator or horror game rather quickly with Unity, but it doesn't seem to make life easier when it comes to make a game with complex interlinking systems. I thought about possibly adopting the component-system design for my game, as it seems to make games of my type easier to create:

 

But anyway, working with Unity seemed to make life easier at first. I could create all these cool physics sandboxes, use fancy rendering and graphics, etc. However, when it actually comes down to making an interesting game, it seems easier to just stick with XNA. I'm still at a bit of a dilemma and aren't sure what to do.

 

 

For well documented code, I suggest that you just have fairly well descriptive classes, functions, and namespaces. Insert a comment here and there to make it more readable. You could use generic classes as "components" and build on that.

-----Deo Gratias et Ave Maria!-----

Decide on your interface first. By that I mean when you are designing, decide the return type and argument types once and for all. As long as those stay the same your implementation can change without breaking other code too bad. But yeah sounds like you actually want to plan this time, so make a good design plan. It doesn't have to be 100% object oriented. Just specify all the pieces as prototypes and leave the implementation for later. Data first style kinda. Get all libraries you will need together so you can use types from them in your prototypes. One way to plan is to go ahead and write your program in non-existant functions and objects, pretending that they do certain things. If you are good at makebelieve, all you have to do is implement your "skeleton" and it is done.

hello

This is a massive topic, and books have been written on the subject, and something that all of us manage better in some projects than others.

One thing that is common I think, is the tendency that the bigger and more complex a program, the more difficult it is to reason about and maintain. Techniques that work fine for small games often don't scale. The priorities in a large scale project can be different, and things like modularity and simplicity become more and more important.

The general solution to manage this complexity is usually to try and break down parts of the system into smaller, simpler modules. If you can force your modules to only communicate over a simple interface to each other, you can turn a task of making one massive program into a task of making, say, 10 smaller programs, each of which is easier to follow and debug.

This idea was part of the inspiration for object orientated programming, but the idea can scale above the level of individual classes. In fact, I would say that worrying about the details of comments or functions is too low level, there is a big danger that you won't see the forest for the trees. Try to think very high level and enforce that abstraction.

An example of this in practice working well is a plugin architecture used in many successful apps. It isolates a system into a host app providing some services, and a selection of smaller programs which use those and communicate only over a simple interface.

The other technique which becomes more and more useful is to split your work into code that is specific to a project, and library code which you will be able to reuse many times. Often you will implement something several times in different projects, get a feel for it, then decide it is time to make a generic version you can reuse, perhaps with templates (if you use c++). With a large library it makes the task of writing specific project code much less daunting, more concise, and you (hopefully) get to fix the bugs once in the library code instead of debugging the same thing over and over.

Thanks for the replies peeps :)

Is there something more that I can do from the outset? Maybe something I can refer back to, such as a generalized diagram? I feel like there's multiple forces at play within my game and don't know exactly how to display these so I can refer back.

For example, the ordering needs to be taken into account - as there's collisions, animation changes, physics updates, attribute changes (HP, MP, armour, etc), deletions, additions, etc. I'd like some way to translate this to diagram form if possible. I find it difficult to visualize as there are multiple entities, copies, inheritance dependencies, etc.

Another compartmentalized problem is the various screen states. Although I feel like this is easier and could probably just be doodled.

There also seems to be this exponential feature creep problem I'm having where, for example, if the game has 10 weapons and 2 enemies, that results in (10 x 2 = 20) tests I have to run to make sure a new feature addition works. I'm using purely arbitrary and hypothetical measurements here, but imagine a few additions of props, passive abilities, outside influences, etc, and you end up with this (8 x 5 x 7 x 3 x 2 x 9 x 30) sort of scenario where it becomes impossible to just "wing it" every time you add a new item. So far, everything works, but I don't like the fact that it works - I don't trust it :P Is there a more concrete way to make sure things function?

Also, when you mention modularity, would you say this seems to be the way to go? It seems that game design progressed through various iterations (object-oriented, data-driven, etc) and that this new component-system design seems to really reduce the risk of errors when features are added or removed - at the expense of more boilerplate and scaffolding at the project start (which I am totally fine with if it saves me hassle down the road) - I once heard a wise man say something about good code, and it went along the lines of "if you add or remove a feature, it should just pluck out or insert in perfectly without leaving so much as a ripple in the rest of the code" - obviously coding isn't quite that simple, but this design principle apparently creates that effect.

It seems that as the project has grown, I've spent less and less time writing actual code and more time chasing chains of references, dependencies, etc. It's frustrating because I know how to create all the parts, but the problem is more complex than the sum of its parts!

 

 

 

The truth is, large projects with several people are easier to manage as a hierarchy than small to medium sized projects with a single person. For perspective, Imagine 7 people completing single individual tasks, now imagine one person failing at all 7 tasks. You need to go easy on yourself and realize that you're wearing all of the hats. You're the one having to design, code, document, refactor, test, etc. So, don't feel as if it's a 'you' problem, and that you can't maintain structure, it's inherent for everyone except highly experienced solo developers.

Clearly define the scope of the project, this step is probably the most important. Use programs like DIA, or VISIO or even mindmeister.com to design the structure of all of your namespaces, classes, methods, and variables while keeping in mind future proofing code interdependence, especially if you're using middleware. This should be a working diagram that the game reflects, not a diagram that reflects the game. The difference being that the diagram is the first stop for any design decisions, someone should be able to open this diagram and build every piece of the game without ever speaking to you. Essentially this will be your design document, the holy bible for your creation.

I believe that if you stay disciplined enough to hold the design document at a higher priority than the actual game, you will alleviate the majority of your issues.

The only other advice I can give you is to create a vertical slice while you implement the design document before you start building the full project, use it as a evolutionary prototype to iron out potential conflicts. This is a good place to start building any necessary tools to help automate building the full scale game.

This topic is closed to new replies.

Advertisement