-
Notifications
You must be signed in to change notification settings - Fork 0
Best Practices
Below are the general style guidelines that should be followed throughout the project code.
Token Type | Style |
---|---|
Namespace | PascalCase |
Class | PascalCase |
Enums | EPascalCase |
Interfaces | IPascalCase |
Public Field/Member Variable | camelCase |
non-Serialized Proteced/Private Field/Member Variable | _camelCase |
Serialized Proteced/Private Field/Member Variable | camelCase |
Property | PascalCase |
Class Method | PascalCase |
Method Parameter | camelCase |
Local Variables | camelCase |
Note: All files and assets should be formatted using PascalCase
along with the appropriate prefix and/or post-fix specified below.
Asset or File Type | Prefix | Post-fix | Notes |
---|---|---|---|
Scripts | None | None | Generally, a script file should primarily contain a single class type, and the name of the file should match the name of that class. |
Materials | None | None | |
Textures and Sprites | None | None | |
Spritesheets | None | None | |
Prefabs | None | None | |
Audio Sources | None | None | |
Animations | None | None | |
Models | None | None | |
Fonts | None | None | |
Scenes | None | None | |
ScriptableObject Instances | None | None |
All git feature branches should be written in kebab-case
and in the form unityid/feature-name
.
Spaces. It is better for consistent readability. A tab should be equal to 4 spaces.
NOTE: While I know this is a hotly debated topic, I am willing to die on this hill.
Scope brackets for namespaces, classes, methods, if/else if/else statements, for statements, etc. should start on a new line as shown below.
namespace BuildABot
{
class MyClass : Monobehaviour
{
public void MyMethod()
{
for (int i = 0; i < 5; i++)
{
// ...
}
}
}
}
When writing if/else if/else statements with only a single line of instructions, brackets can be skipped entirely and all of the code can be placed on the same line as shown here:
if (someCondition) doTheThing();
Generally speaking, every function should be documented since so many eyes and experience levels will be looking at the code of the project. At the very least, all public and protected functions should have some amount of documentation associated with them. While it is understandable to skip documentation on small functions with highly descriptive names and low complexity or in the sake of time when under crunch, it is generally encourage to document as you go for longer term code quality and potential documentation pages generation.
In Unity and C#, something similar to JavaDoc comments are used. Here is an example from the Utility class:
/**
* Executes the provided action after the specified delay.
* <param name="context">The monobehaviour context used to execute the action.</param>
* <param name="seconds">The number of seconds to wait before performing the action.</param>
* <param name="action">The action to perform.</param>
* <returns>The coroutine enumerator.</returns>
*/
public static IEnumerator DelayedFunction(MonoBehaviour context, float seconds, Action action)
{
// ...
}
Basically, HTML tags are used in place of things like @param
. Otherwise it is pretty similar.
Additionally, if you prefer you can use ///
at the start of each line instead of the JavaDoc style.
/// Executes the provided action after the specified delay.
/// <param name="context">The monobehaviour context used to execute the action.</param>
/// <param name="seconds">The number of seconds to wait before performing the action.</param>
/// <param name="action">The action to perform.</param>
/// <returns>The coroutine enumerator.</returns>
vs the JavaDoc style
/**
* Executes the provided action after the specified delay.
* <param name="context">The monobehaviour context used to execute the action.</param>
* <param name="seconds">The number of seconds to wait before performing the action.</param>
* <param name="action">The action to perform.</param>
* <returns>The coroutine enumerator.</returns>
*/
The former is closer to traditional C# documentation style. I personally prefer the JavaDoc style, but either is fine and should both be understood by C# IDEs. Just be consistent.
When declaring local variables, you either have the option of explicitly declaring the type of the variable or you can use var
instead to allow the compiler to automatically deduce the type of the variable based on what is assigned to its value. While both are acceptable, it should be preferred to use explicit type names for better code readability. If the type of the variable is not readily known or is excessively verbose (such as the pair types used when iterating maps), it is ok to use var
.
For anyone unfamiliar with this syntax, here is an example of declaring strings in each way:
string someText = "Hello World";
var someOtherString = "Goodbye World";
All classes and types declared in the project should be declared inside of a namespace named BuildABot. If a more granular level of encapsulation is desired, use the namespace BuildABot.SomeSubsystem. More granular namespaces should not contain just a single type, and should be reserved for larger systems. Generally, only something that you could see being broken off into a standalone package should have a deeper namespace.
It is important that we keep our types within our own namespace to avoid polluting the global namespace and potentially conflicting with existing types or content from third party libraries or other vendors.
Examples:
using UnityEngine;
namespace BuildABot
{
public class MyClass
{
//...
}
}
using UnityEngine;
namespace BuildABot.SomeSubsystem
{
public class MyOtherClass
{
//...
}
}
Classes that share a namespace will automatically have access to each other, but any classes outside of the namespace will either need to include a using declaration at the top of their file or method like
using BuildABot;
or they can simply append the namespace at the front of the type when using it like
BuildABot.MyClass.SomeMethod();