This standard uses Unity’s internal coding standard as its foundation and extends Microsoft's Framework Design Guidelines, which defines a number of rules not covered by this document.
Definitions
camelCase: *words capitalized, except the first
PascalCase: all *words capitalized
UPPERCASE: all letters in all *words capitalized
*A “word” may only contain letters and numbers (no underscores or other symbols)
Encoding
Text file encoding should use UTF8 with no BOM, using LF (unix) line endings
Tabs should use 4-wide tab stops, using spaces only (no tab characters)
Lines should not contain any trailing whitespace
End of files should always include a single newline
Files
The style and standard of any imported libraries or external code should be maintained
Use PascalCase for file names, typically matching the name of the dominant type in the file (or if none is dominant, use a reasonable category name).
Naming
Use PascalCase for all symbol names, except where noted.
No ‘Hungarian notation’ or other prefixes, except where noted.
Spell words using correct US-English spelling.
Use descriptive and accurate names, even if it makes them longer. Favor readability over brevity.
Avoid abbreviations when possible unless the abbreviation is commonly accepted.
Acronyms are PascalCase, unless they are exactly two letters, in which case they are UPPERCASE. (ex. GetCpuCycles(), IOStream)
Do not capitalize each word in so-called closed-form compound words. (Microsoft’s FDG example)
Use semantically interesting names rather than language-specific keywords for type names (i.e. GetLength > GetInt).
Use a common name, such as value/item/element, rather than repeating the type name, in the rare cases when an identifier has no semantic meaning and the type is not important (i.e. newElements > newInts)
Type Naming
Make type names unambiguous across namespaces and problem domains by avoiding common terms or adding a prefix or a suffix. (ex. use 'PhysicsSolver', not 'Solver')
Avoid unnecessary prefixes/suffixes, even when other types in the namespace have a prefix (ex. use 'LightEstimationData', not 'ARLightEstimationData', even though 'ARSession' exists)
Avoid naming types after terms that represent different concepts in different domains (ex. use 'AndroidInput', not 'Input')
Do not use namespaces to enable reusing an existing type name, as resolving conflicts in user code requires using aliases or fully qualified type names.
Methods and Parameters
When a method takes one or more arguments that imply a binary condition, consider the following options:
If the method name clearly conveys a binary condition, then use a bool argument (e.g., void SetActive(bool active))
If the method name conveys a condition that could conceivably have more than two states:
You can create multiple named method variants (e.g., void Repaint() and void RepaintImmediately(), T GetComponent() and T GetComponentReadOnly()).
(+) Avoids adding new enumerated type to the API surface.
(-) Increases number of named methods (and thus number of search points in API reference).
(-) API surface will grow if support for new modes is added later.
You can create a single method and add an enum for the condition (e.g., void ApplyChanges(InteractionMode mode = InteractionMode.UserAction)).
(+) More robust to future support of additional modes.
(+) Single named API point communicates common intent, parameter conveys implementation/execution details.
(-) Adds more enumerated types to the API, which may not be widely used.
In most cases, prefer overloads rather than additional methods with different names (e.g., void GetItems(List<T> items) and void GetItems(T[] items, int length) instead of void GetItemsDynamic(List<T> items) and void GetItemsPreAllocated(T[] items, int length)).
Readability Examples
HorizontalAlignment instead of AlignmentHorizontal (more English-readable)
CanScrollHorizontally instead of ScrollableX ('x' is somewhat obscure reference to the x axis)
DirectionalVector instead of DirVec (unnecessary and use of nonstandard abbreviation)
Common Abbreviations
param (parameter)
arg (argument)
id (identifier)
db (database)
ok (okay)
Spacing
When dealing with whether to put spaces before open parenthesis:
If it looks like a function call, no space (function calls, function definitions, typeof(), sizeof())
If it opens a scope, add a space (if, while, catch, switch, for, foreach, using, lock, fixed)
No spaces immediately inside any parens or brackets (e.g. no 'if ( foo )' or 'x = ( y * z[ 123 ] )')
Comma and semicolon spacing as in English ('int a, float b' and 'for (int i = 0; i < 10; ++i)')
Exactly one space is required after the // in a C++ style comment.
Do not add a space between a unary operator and its operand (!expr, +30, -1.4, i++, --j, &expr, *expr, (int)obj, etc.).
Do not add spaces around member access operators (a.b, a->b, etc.).
Spaces are required both before and after all other operators (math, assignment, comparison, lambdas, etc.).
Wrapping
Wrap code once it gets to around 120 columns wide to keep side-by-side diffs sane (not a hard limit; use your judgment).
When necessary, break lines after boolean operators in conditional expressions, after ';' in for-statements, and after ',' in function calls
Comments
Documenting the 'why' is far more important than the 'what' or 'how'.
Document anything that would surprise another engineer (or yourself in six months when you've forgotten it).
/*C-Style comments*/ are not permitted. They are reserved for commenting out big hunks of code locally (never to be committed).
No "divider" comments (i.e. long ----- and ////// comments just to break up code).
No "category" comments (i.e. // Functions // Private Data // Globals etc.).
Only use /// (triple slash) comments if you are writing xmldoc, and never for ordinary comments that you want to stand out
Usings
Located at file scope at the top of the file, never within a namespace.
Three groups, which are, top to bottom: System, non-System, aliases. Keep each group sorted.
Strip unused 'usings' except the 'minimally-required set', which is marked with *required below.
Only use aliases when required by the compiler for disambiguation, and not for hiding rarely-used symbols behind a prefix.
Always drop explicit namespace qualifications on types when a 'using' can be added (i.e. almost all of the time).
using System; // | Not required, but strongly encouraged using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using Company.BuildSystem; // | Start of non-System group using Microsoft.Win32; using UnityEngine; using Component = UnityEngine.Component; // | Start of aliases group using Debug = UnityEngine.Debug; namespace UnityEditor // | Full contents of namespace indented {
Enums
Use a singular type name, and no prefix or suffix (e.g. no E- prefix or -Enum suffix).
Constant names should have no prefix or suffix.
Do not specify constant values unless absolutely required (e.g. for version-safe protocols - rare).
enum WindingOrder // | Drop redundant access specifiers (leave off 'internal' at file scope) { // | Opening brace is always on its own line at the same level of indentation as its parent Clockwise, // | Code within the braces always indented one tab stop CounterClockwise, Charm, Singularity, // | Trail last element in a list with ',' } // | Closing brace is on its own line at same level of indentation as parent // | Put exactly one blank line between multi-line types
Flags Enums
Use a plural type name, and no prefix or suffix (e.g. no E- prefix and no -Flag or -Flags suffix).
Constant names should have no prefix or suffix.
Use column-aligned bit shift expressions for the constants (instead of 2, 4, 8, etc.
public enum VertexStreams { Position = 1 << 0, Normal = 1 << 1, Tangent = 1 << 2, Color = 1 << 3, UV = 1 << 4, }
Interfaces
Name interfaces with adjective phrases, or occasionally with nouns or noun phrases.
- Nouns and noun phrases should be used rarely and they might indicate that the type should be an abstract class, and not an interface.
Use 'I' prefix to indicate an interface.
Ensure that the names differ only by the 'I' prefix on the interface name when you are defining a class-interface pair, where the class is a standard implementation of the interface.
public interface IThingAgent { string operationDescription { get; } float scale { get; }
Methods
Give methods names that are verbs or verb phrases.
Parameter names are camelCase
bool DoThing(string propertyDescription, int spinCount); }
Classes
Name classes and structs with nouns or noun phrases.
No prefix on class names (no 'C' or 'S' etc.).
class Example {
Fields
Use prefix + PascalCase for non-public field naming.
Prefixes: m_ = instance field, s_ = static readwrite field, k_ = const
Also prefix static/instance readonly with k_ if the intent is to treat the field as deeply const.
Drop redundant initializers (i.e. no '= 0' on ints, '= null' on ref types, etc.).
Never expose public fields which are not const or static readonly. These fields should be published through a property.
Use readonly where const isn't possible.
static readonly Vector3 k_DefaultLength = new Vector3(1, 2, 3); // | When it enhances readability, you can try to column-align blocks of variable definitions at symbol name and assignment tab stops. This is not a hard rule. const int k_MaxCount = DisplayData.MaxItems; static int s_SharedCount; // | Note no "= 0". All memory is zero'd out by default, so do not redundantly assign. int m_CurrentCount; public const int totalCount = 123; // | In the UnityEngine and UnityEditor namespaces (old conventions), public fields are camelCase with no prefix [FDG Exception] // | In the Unity namespace, public fields are PascalCase with no prefix public string defaultName { get { return Environment.MachineName; } } // | In the UnityEngine and UnityEditor namespaces (old conventions), public properties are camelCase with no prefix [FDG Exception] // | In the Unity namespace, public properties are PascalCase with no prefix [Example] // | Drop 'Attribute' postfix when applying an attribute public int currentCount { get { return m_CurrentCount; } // | Getters are always trivial and do not mutate state (this includes first-run cached results); use a full method if you want to do calculations or caching set { m_CurrentCount = value; } } // | Put exactly one blank line between multi-line methods and properties public string description { get // | For multiline method bodies, the 'get' and 'set' keywords must be on their own line { return string.Format( "shared: {0}\ncurrent: {1}\n", s_SharedCount, m_CurrentCount); } }
Events
Do not declare new delegate types. Use Action<...> instead.
Do not expose public delegate fields. Use events instead.
Include one participle-form verb in the event name (generally ending in -ed or -ing, ex. occurred, loading, started, given)
*EventArgs struct parameters are not necessary, but they should be used if the data sent to the event has the possibility of needing to be changed. [FDG Exception]
public event Action<ThingHappenedEventArgs> thingHappened; [Description("I do things"), DebuggerNonUserCode] // | Attributes always go on a line separate from what they apply to (unless a parameter), and joining them is encouraged if they are short public void DoThings(IEnumerable<IThingAgent> thingsToDo, string propertyDescription) // | For types that are already internal (like class Example), use public instead of internal for members and nested types { List<IThingAgent> doneThings = new List<IThingAgent>(); string indent = new string(' ', 4); // | When appropriate, separate code blocks by a single empty line IList<string> doneDescriptions = new List<string>(); foreach (IThingAgent thingToDo in thingsToDo) { if (!thingToDo.DoThing(propertyDescription, m_CurrentCount)) break; // | Braces not required for single statements under if or else, but that single statement must be on its own line using (File.CreateText(@"path\to\something.txt")) // | Use @"" style string literal for paths with backslashes and regular expression patterns using (new ComputeBuffer(10, 20)) // | Don't use braces for directly nested using's { // | Braces required for deepest level of nested using's doneThings.Add(thingToDo); } } foreach (IThingAgent doneThing in doneThings) // | Dirty details about allocs at https://q.unity3d.com/questions/1465/when-does-using-foreach-in-c-cause-an-allocation.html { // | Braces are required for loops (foreach, for, while, do) as well as 'fixed' and 'lock' doneDescriptions.Add(doneThing.operationDescription); Debug.Log(indent + "Doing thing: " + doneThing.operationDescription); // | Prefer a + b + c over string.Concat(a, b, c) } Debug.Log("System Object is " + typeof(object)); // | Always use lowercase `object` for the System.Object class. Debug.Log("Unity Object is " + typeof(UnityEngine.Object)); // | Always use a fully qualified name for Unity's Object type, and never 'Object' } public void ControlFlow(string message, object someFoo, WindingOrder windingOrder) // | Use c# aliases of System types (e.g. object instead of Object, float instead of Single, etc.) { for (int i = 0; i < k_MaxCount; ++i) // | Using i and j for trivial local iterators is encouraged { // all of this is nonsense, and is just meant to demonstrate formatting // | Place comments about multiple lines of code directly above them, with one empty line above the comment to visually group it with its code if ((i % -3) - 1 == 0) // | Wrap parens around subexpressions is optional but recommended to make operator precedence clear { ++m_CurrentCount; s_SharedCount *= (int)k_DefaultLength.x + totalCount; do // | 'while', 'do', 'for', 'foreach', 'switch' are always on a separate line from the code block they control { i += s_SharedCount; } while (i < m_CurrentCount); } else // | 'else' always at same indentation level as its 'if' { Debug.LogWarning("Skipping over " + i); // | Drop 'ToString()' when not required by compiler goto skip; // | Goto's not necessarily considered harmful, not disallowed, but should be scrutinized for utility before usage } } skip: // | Goto label targets un-indented from parent scope one tab stop // more nonsense code for demo purposes switch (windingOrder) { case WindingOrder.Clockwise: // | Case labels indented under switch case WindingOrder.CounterClockwise: // | Braces optional if not needed for scope (but note indentation of braces and contents) if (s_SharedCount == DisplayData.MaxItems) // | Constants go on the right in comparisons (do not follow 'yoda' style) { string warningDetails = someFoo.ToString(); for (int i = 0; i < s_SharedCount; ++i) { Debug.LogWarning("Spinning a " + warningDetails); } } break; // | 'break' inside case braces, if any case WindingOrder.Charm: Debug.LogWarning("Check quark"); // | Indentation is the same, with or without scope braces break; case WindingOrder.Singularity: { string warningDetails = message; // | (this seemingly pointless variable is here solely to require braces on the case statements and show the required formatting) if (message == Registry.ClassesRoot.ToString()) { // Already correct so we don't need to do anything here // | Empty blocks should (a) only be used when it helps readability, (b) always use empty braces (never a standalone semicolon), and (c) be commented as to why the empty block is there } else if (m_CurrentCount > 3) { if (s_SharedCount < 10) // | Braces can only be omitted at the deepest level of nested code Debug.LogWarning("Singularity! (" + warningDetails + ")"); } else if (s_SharedCount > 5) // | 'else if' always on same line together throw new IndexOutOfRangeException(); else if ((s_SharedCount > 7 && m_CurrentCount != 0) || message == null) // | Always wrap subexpressions in parens when peer precedence is close enough to be ambiguous (e.g. && and || are commonly confused) throw new NotImplementedException(); break; } default: throw new InvalidOperationException("What's a " + windingOrder + "?"); } }
Parameterized Types
When only a single parameterized type is used, naming it 'T' is acceptable.
For more than one parameterized type, use descriptive names prefixed with 'T'.
Consider indicating constraints placed on a type parameter in the name of the parameter.
public static TResult Transmogrify<TResult, TComponent>( // | When wrapping params, do not leave any on line with function name TComponent component, Func<TComponent, TResult> converter) // | When wrapping, only indent one stop (do not line up with paren) where TComponent : Component { return converter(component); } }
Structs
Name classes and structs with nouns or noun phrases.
No prefix on class names (no 'C' or 'S' etc.).
Structs may be mutable, but consider immutability when appropriate. [FDG Exception]
struct MethodQuery { public string name { get; set; } public IEnumerable<Type> paramTypes { get; set; } public Type returnType { get; set; } public override string ToString() // | Methods generally are not permitted in structs, with exceptions like this noted in the data-oriented programming guidelines. { var paramTypeNames = paramTypes // | Prefer fluent function call syntax over LINQ syntax (i.e. y.Select(x => z) instead of 'from x in y select z') .Select(p => p.ToString()) // | Prefer breaking long fluent operator chains into one line per operator .Where(p => p.Length > 2) .OrderBy(p => p[0]) .ToArray(); return string.Format( "{0} {1}({2})", returnType, name, string.Join(", ", paramTypeNames)); } }
EventArgs
Always use structs for EventArgs types, and never extend System.EventArgs [FDG Exception]
Make EventArgs structs immutable
See the event example above for when to define EventArgs structs.
struct ThingHappenedEventArgs { public string thingThatHappened { get; } public ThingHappenedEventArgs(string thingThatHappened) { this.thingThatHappened = thingThatHappened; } }
Attributes
Mark up all attributes with an AttributeUsage, as narrow as possible.
Postfix attribute class names with "Attribute".
[AttributeUsage(AttributeTargets.Property)] public class ExampleAttribute : Attribute { // | Empty types have braces on their own lines }
Exceptions
Postfix exception class names with "Exception".
Do not inherit from ApplicationException
public class ExampleException : Exception { public ExampleException() {} public ExampleException(string message) : base(message) {} public ExampleException(string message, Exception innerException) : base(message, innerException) {} } }