Unity (C#) Style Guide

Rev. 1.1

To be followed in all public or open source projects made by Carter Games at all times. If you have been refered to this page it is likey you have missed something or not formatted your code following this style guide and therefore will need to update it.

Introduction

This style guide is focused on readability for people who can’t read compact or cluttered code. This includes extra spacing & order structure to make it easy to find things in a hurry. You’ll note this style is different to the most common ones because of this, if you are contributing to any project form 2023 onward, you’ll need to follow this guide.

Note that this guide is focused on Unity C# code rather than general C#

Naming

On the whole, naming should follow C# standards.


Namespaces

Namespaces are all PascalCase, multiple words concatenated together, without hyphens ( – ) or underscores ( _ ). The exception to this rule are acronyms like GUI or HUD, which can be uppercase. Try to only use namespaces where the logic in question is a seperate system or mechanic where possible:

Prefer
CarterGames.Assets.AudioManager
Avoid
com.cartergames.assets.audiomanager

Classes & Interfaces

Classes and interfaces are written in PascalCase.

MyClass

Methods & Parameters.

Methods are written in PascalCase. All arguments/parameters should be in camelCase without hyphens ( – ) or underscores ( _ ).

Prefer
DoSomething();
DoSomethingWithInt(int myValue);
Avoid
doSomething();
DoSomethingWithInt(int MyValue);

Coroutines.

Coroutines are written in PascalCase with a prefix of Co_ for clarity.

Prefer
private IEnumator Co_DoSomething();
private IEnumator Co_DoSomethingWithInt(int myValue);
Avoid
private IEnumator doSomething();
private IEnumator DoSomethingWithInt(int MyValue);

Fields

All non-static fields are written camelCase. Per Unity convention, this includes public fields as well.

public class MyClass 
{
    public int publicField;
    private int myPrivate;
    protected int myProtected;
}
Prefer
private int myPrivateVariable
Avoid
private int _myPrivateVariable

Static, readonly or constant fields are the exception and should be written in PascalCase:

public static int TheStaticAnswer = 42;
public readonly int TheReadOnlyAnswer = 42;
public const int TheConstAnswer = 42;

Properties

All properties are written in PascalCase.

public int PageNumber 
{
    get { return pageNumber; }
    set { pageNumber = value; }
}

Actions / Events

Actions are written in PascalCase.

Unique Exception: Where the Evt.cs class is in the project, use it instead of traditional actions. The Evt.cs class has extra checks to avoid over-subscribing and is easier to manage.

Evt/Actions should be named based on what they do, like “OpeningDoor”, “DoorOpened” etc.

// Event before
public readonly Evt OpeningDoor = new Evt();
public event Action OpeningDoor;

// Event after
public readonly Evt DoorOpened = new Evt();
public event Action DoorOpened;  


public readonly Evt<int> PointsScored = new Evt<int>();
public readonly Evt<CustomEventArgs> ThingHappened = new Evt<CustomEventArgs>();

public event Action<int> PointsScored;
public event Action<CustomEventArgs> ThingHappened;

Misc

  • In code, acronyms should be treated as words.
  • Avoid using β€œObject”, β€œAction” or β€œEvent” as class names.
Prefer
XmlHttpRequest
string url
findPostById
Avoid
XMLHTTPRequest
string URL
findPostByID

Declarations

Access Level Modifiers

Access level modifiers should be explicitly defined at all times (private/internal/public etc.)


Using Lines

Keep using lines at the top of your file. Remove unsed lines (most IDE’s show which are not in use).

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

Fields & Variables

Prefer single declaration per line, its easier to follow.

Prefer
private string username;
private string twitterHandle;
Avoid
private string username, twitterHandle;

Properties

Prefer shorthand where possible. Only use braces & vertical spacing where additional logic is required. If a property logic is getting large, consider making it a method instead. You may use [field: SerializeField] to see properties in the inspector, but this should only be used for debugging purposes.

Examples
private string Username { get; set; }
private string Username => userName;

private string TwitterHandle
{
    get => twitterHandle;
    set => twitterHandle = value;
}

Classes

Exactly one class per source file. Even if it makes classes which are just a declaration. Its easier to find files that digging through other classes to find a hidden class at the bottom of a file.


Interfaces

All interfaces should be prefaced with the letter I.

Prefer
IRadialSlider
Avoid
RadialSlider

Enums / FlagEnums

  • No prefix or suffix.
  • Use [InspectorName] attribute to change the name if needed.
  • Only store in a class if it is only local to the class in question and not used or called outside of said class, otherwise store in a seperate file for ease of navigation.
Prefer
public enum Direction
{
    North,
    South,
    East,
    West,
}

public enum AttackModes
{
    // Decimal                         // Binary
    None = 0,                          // 000000
    Melee = 1,                         // 000001
    Ranged = 2,                        // 000010
    Special = 4,                       // 000100

    MeleeAndSpecial = Melee | Special  // 000101
}
Avoid
public enum DirectionEnum
{
    North,
    South,
    East,
    West,
}

Actions / Events

  • Methods handling events should use the prefix “On” and then be named based on the event they are handling, e.g. “OnDoorOpened”.
  • Event raising can happen where appropiate in your code or with a raising method with the prefix Raise, e.g. “RaiseDoorOpened”.
// These are event raising methods, e.g. RaiseDoorOpened, RaisePointsScored, etc.
public void RaiseDoorOpened()
{
    DoorOpened.Raise();    // Evt
    DoorOpened?.Invoke();  // Action
}


public void RaisePointsScored(int points)
{
    PointsScored.Raise(points);   // Evt
    PointsScored?.Invoke(points); // Action
}


// These are event listening methods, e.g. OnDoorOpened, OnPointsScored, etc.
private void OnDoorOpened()
{
    // Logic here...
}

private void OnPointsScored(int points)
{
    // Logic here...
}

Spacing

Spacing is especially important, as code needs to be easily readable. Seperate elements of your methods where needed to help with clarity & readability. If a method is large, consider spliting the logic into smaller methods for readability.


Indentation

Indentation should be done using tabs β€” never spaces.


Blocks

Indentation for blocks uses 4 spaces (1 Tab) for optimal readability

Prefer
for (int i = 0; i < 10; i++) 
{
    Debug.Log("index=" + i);
}
Avoid
for (int i = 0; i < 10; i++) 
{
  Debug.Log("index=" + i);
}

Line Wraps

Indentation for line wraps should use 4 spaces (1 Tab) (not the default 8/2 tab)

Prefer
CoolUiWidget widget =
    someIncrediblyLongExpression(that, reallyWouldNotFit, on, aSingle, line);
Avoid
CoolUiWidget widget =
        someIncrediblyLongExpression(that, reallyWouldNotFit, on, aSingle, line);

Line Length

Lines should be no longer than 120 characters long. You can set this in your IDE if needed.


Vertical Spacing

There should be two blank lines between methods to aid in visual clarity and organization. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods. If there is a section comment after or before a method, only one blank line should be used.

private int health;
private int mana;

/// <summary>
// The health of the user
/// </summary>
public int Health => health;


/// <summary>
// The mana of the user
/// </summary>
public int Mana => mana;

/* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
|   Methods
───────────────────────────────────────────────────────────────────────────────────────────────────────────── */

/// <summary>
// Sets the user's health
/// </summary>
public void SetHealth(int value)
{
    health = value;
}


/// <summary>
// Sets the user's mana
/// </summary>
public void SetMana(int value)
{
    mana = value;
}

Brace Style

All braces get their own line as it is a C# convention. It is also a lot easier to read at a glance.

Prefer
public class MyClass
{
    public void DoSomething()
    {
        if (someTest)
        {
          // ...
        }
        else
        {
          // ...
        }
    }
}
Avoid
public class MyClass {
    public void DoSomething() {
        if (someTest) {
          // ...
        } else {
          // ...
        }
    }
}

Conditional statements are always required to be enclosed with braces when more then 1 line long. When the logic enclosed is a return/continue it can be on the same line as the statement. If the enclosed logic is a single line that isn’t a return/continue etc you should have braces for clarity.

Prefer
if (someTest) 
{
    DoSomething();
}  

if (someTest)
{
    DoSomethingElse();
}

if (someTest) return;
Avoid
if (someTest)
    doSomething();  

if (someTest) doSomethingElse();

if (someTest)
{
    return;
}

Switch Statements

Switch-statements come with default case by default. If the default case is never reached, be sure to remove it. You don’t need braces for ease case in the switch statement.

Prefer
switch (variable) 
{
    case 1:
        break;
    case 2:
        break;
}
Avoid
switch (variable) 
{
    case 1:
        break;
    case 2:
        break;
    default:
        break;
}

Language

Use US English spelling, even though its the wrong spelling for me being in the UK, the US spelling is more commonly used.

Prefer
private string color = "red";
Avoid
private string colour = "red";

File & Folder Structure

Folders & File Placement

All classes should be in their own files regardless of complexity, as this make the files easier to find. Any editor code should be placed in the editor folder, with runtime under the runtime folder.

[Editor]
    [Inspectors]
        - MyScriptEditor.cs
[Runtime]
    [MyScripts]
        - MyScript.cs

Systems should be grouped with any logic that is relevant to said system in folders where appropiate for ease of navigation. Once again, editor logic must be under the editor root folder.

[Runtime]
    [Scoring]
        [Data]
            - ScoreData.cs
            - ...
        [UI]
            - ScoreUI.cs
            - ...
        - ScoreManager.cs

Class Structure

Use section comments to split your fields, properties etc. You may break this rule to keep elements of an interface implemetation together. You may remove sections if no elements exist in them. Try your best to follow the following order in your classes.

[Using]
    1-2 spaces between the last using statement & the namespace line.

[Namespace]

    [ClassName]

        [Constant Fields -> Fields]
            Both under the same section comment with 1-2 line(s) between the constants & non-constants.

        [Properties]

        [Events/Actions/Delegates]

        [Unity Methods]

        [Interface Implementations]
            1 Section comment per implementation. Properties for the interfaces should be with their implementations.

        [Inheritance/Overrides]
            If using a base class and overriding logic.

        [Methods]
            You may split the section comments under this section as you wish to aid with navigation.

        [Coroutines]
            You may split the section comments under this section as you wish to aid with navigation.

        [Any Other Sections]
            If you have additional sections you wish to add, you may.
using UnityEngine;


namespace Example
{
    public class MyClass
    {
        // Each of these that are in use should be seperated with section comments...
        // You may remove section comments for sections that are not in use...
        /* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
        |   Fields
        ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */



        /* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
        |   Properties
        ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */
        
        
        
        /* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
        |   Events
        ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */
        
        
        
        /* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
        |   Unity Methods
        ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */
        
        
        
        /* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
        |   Methods
        ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */
        
        
   

Section Comments

Section comments should be used to split up section of a class to make it easier to navigate. Template below (Copy for your own use, should start 2-tabs indented (8 spaces), your title should start 3 spaces from the | character, which should also be 2-tabs/8 spaces indented)

        /* ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
        |   MyHeaderNameHere!
        ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */

Comments

  • Comments should be used to help a user understand what it does and why it does it, don’t comment everything for the sake of it unless its a public element.
  • Use the // comment to keep the explanation next or 1 line above the logic in question.
  • Use a Tooltip instead of a comment for serialized fields if explination is needed.
  • Avoid Regions unless your class is really big. If your class is big, consider ways you can reduce its size by splitting out loigic into other classes for readability.
  • Use a summary XML tag in front of public methods/functions/proerties etc. for output documentation/Intellisense, regardless of complexity.
/// <summary>
// Sets the user's mana
/// </summary>
public void SetMana(int value)
{
    mana = value;
}

Header Statement

The following copyright statement should be included at the top of every source file.

/*
 * Copyright (c) 2018-Present Carter Games
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 *    
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

Acknowledgments

This style guide is formatted based on a similar guide on github for another group which I wish to mention as a basis for this style guide as it would be rude not to. You may see it here: https://github.com/kodecocodes/c-sharp-style-guide/blob/master/README.markdown

Change Log

Rev 1.1

  • Added extra clarity on some sections.
  • Changed the requirement for spaces to allow tabs (don’t know why I put it as spaces xD)
  • Added extra allowances for return/continue when using braces with if statements.

Rev 1.0

  • Initial release of the Unity C# style guide.