One of my favorite things about game development is that you can end up collaborating with a diverse set of people from a variety of disciplines. But collaboration can often be tricky, especially as a programmer - you want your code to be easily understood, easy to update and maintain, and easily understood by people who may not have the same programming background you have. Here are some guidelines I’ve found to be really helpful while working on projects with other people - programmers and non-programmers alike.
Now, some caveats - because this is being primarily written around a college program that uses Unity, everything I’m writing will focus on Unity and C# - however the concepts are applicable to any language or framework. The guidelines are also focused on providing guidance to new programmers - of course there are situations where you will want to break or ignore these rules. I’m aware that these guidelines as a whole are very prescriptive, and they may not all be useful to you - but having some conventions around these concepts in your work that everyone on the project can agree to is invaluable.
And remember - you’re always collaborating with someone when you write code, even if it’s only with the version of yourself three months from now, and the version of yourself from three months ago.
Part I: Variables
Each language has its own conventions for what to name variables and functions, and it is always a good idea to follow those conventions. These provide a common language with the greater game development community, and when you’re seeking help from your networks or online forums, these conventions make your code more readable, which will make it more likely to get responses. Plus, using the conventions of a language identify your code as being in that language very quickly, which is invaluable when people look at your code. Those conventions exist for a reason (even if that reason is simply consistency), and your code style is probably not the right place to show off your creative flair.
For example, in the example below, it is immediately obvious to someone who’s programmed in C# for a while that the first line is a test variable, and the second line is a function to test a variable, because the first is in camelCase, and the second is in PascalCase.
C# Guideline: By default, variables should be camelCase - begin lower case, then use an upper-case letter for each new “word” of the name. For more information, check out the C# Naming Conventions.
Access - Conventions and Intention
Most languages have also developed conventions around access modifier naming - in C# (and many other languages), private variables are preceded by an underscore, and const variables are capitalized. Following these conventions or being consistent with your naming makes it clear how you meant the variables to be used.
C# Guideline: All private variables should begin with an underscore, and all const variables upper-case.
Access modifiers is a very important tool for showing your intention to the people you’re collaborating with. Of course, you could make all functions and variables public, but if you do that you’re not communicating your design intention to your collaborators. Beyond preventing your collaborators from accessing parts of the code you never intended them to access, using correct access modifiers limits the amount of information that’s bombarding your collaborators. The IDE won’t give as useful autocomplete suggestions if every variable and function is public - for example, if your network access code only has two public functions - Disconnect() and Connect(), your collaborators won’t have to scroll through a lot of variables related to internal settings to understand out how it’s meant to be used.
Guideline: Always declare your access modifiers - don’t use implicit access modifiers.
Sometimes you do need to expose a variable but don’t want it to be changed - and using a property in C# is the way to do that. A property is using public gets and private sets to control access to a variable - if something has a private set, it can be accessed anywhere, but only changed (or “set”) internal to the class.
Guideline: Use access modifiers to show your intention - public, private, and properties to make sure your code is accessed as you intend.
Finally, although it’s possible to use the same variable name in nested scopes, it is almost never a good idea. It can lead to confusion, and make it much harder to show what your intention is. In the example below, there are examples of two ways this can cause issues. In MainFunction, the same name for a parameter is being used for a class-level variable, which leads to the exampleString being used incorrectly. In SecondFunction(), a new variable that hides the class-level variable is being declared, and it’s not even a string.
Guideline: Never reuse the same variable name in a nested scope.
Abbreviations and Vague Names
Don’t use abbreviations. Don’t use vague names. 2019 IDE’s can auto-complete your variable names, and it’s always better when collaboratively coding to be as expressive as possible - except for very common abbreviations (‘i’ for iterator, for example).
Guideline: No abbreviations. No vagueness. Be as specific and complete as possible.
Also, it’s important to name your variables based on what they mean (function), versus how they’re used (implementation). What do I mean by this? It’s the difference between naming a variable clampForPlayerHealth (implementation), and maximumPlayerHealth (function) - they mean the same thing, but it’s much easier to understand what the latter means.
Guideline: Name based on function, not implementation.
And finally, you should name your variables as though they’re meant to be read. There’s not a huge difference between calling a float “currentAcceleration” and “accelerationCurrent”, except that the former is closer to how you would describe it in written or spoken English (adjective, then noun).
Guideline: Make your variable names as readable as possible.
Single Use Variables
Usually it’s a bad idea to create a single-use variable, especially if you’re just using something that’s already built-in to a basic language collection. For example, this is definitely unnecessary:
However, if you can more clearly show what’s happening logically in your game using a single-use variable, it’s almost always a good idea. This is pretty confusing:
This is much clearer, even though it does the same thing:
Guideline: Only use single-use variables to make the logic of your game clearer.
Organize Your Variables
Often I’ll see a lot of primitives with similar names referring to similar things - avoid using primitives except where necessary. In the example below, all the physics information about the player is in multiple different primitive variables.
This is bad for three reasons:
Difficult to read. Your variable names begin to get very long and very specific the more you do this - and you end up with blocks of variable names you only use in very specific situations.
Confusing to maintain or make changes. All the information is spread out, and it’s not obviously related to each other. Any time someone needs to make changes, they need to read all of the code to see if there are any inter-relations between these individual variables.
No ability to reuse structure. It’s easy to see that most everything displayed in your game will need to use this structure - and you’ll have to make more and more individual variables. Upkeeping that will be difficult, and any change you make to one, you have to make over and over for each copy.
Instead, if you have information that’s related to each other, you should logically encapsulate it into an object, either a struct or class in C#. Below, you can see the exact same logical structure as the example above, but just logically encapsulated to avoid any copied variable names.
Now, it’s very easy to access the information we need, and it’s really easy to read when we do access it! Plus, it’s completely reusable - we can have every object in the game that moves have a “physics information” variable, and any code we write to deal with movement will work with it right out the gate.
Guideline: Encapsulate related variables together into a struct or class.
The most important thing, however, is to be consistent. If you name your variable “playerHealth” in one class, don’t have a different class function named “healthOfBoss”. If you make your access struct for one portion read “box.x.position”, don’t make a different part be “sword_position.x”. You can choose to break any or all of the guidelines above, but whatever you do choose to do, stick with it, and work time into your process to go back over your code and update it to whatever conventions you’re using. The most common issue I’ve seen with student’s code is usually related to either unclear or ambiguous names, or not being consistent with the structure of those names.