Skip to content

Code style

When using Unity and C# for developing XR applications, it is important to follow a consistent code style. This guide establishes code style conventions for XRC Toolkit packages, making the the codebase easier to understand, debug, and extend.

Make sure to modify the code style settings both for C# and Unity within Rider as described below.

Rider: C# settings

The following table shows the recommended JetBrains Rider settings for C# Code Style, for all XRC projects.

In Rider, go to File > Settings > Editor > Code Style > C# > Naming for setting your code style as shown in the following screenshot:

Code Style Screenshot

Additional information

Naming conventions

Classes and interfaces

Use PascalCase for all class and interface names:

// ✅ Correct
public class SphereSelectLogic : MonoBehaviour
public class SphereSelectInput : MonoBehaviour
public class SphereSelectFeedback : MonoBehaviour

// ❌ Incorrect
public class sphereSelectLogic : MonoBehaviour
public class sphere_select_logic : MonoBehaviour

Files

File names must exactly match the class name they contain:

SphereSelectLogic.cs    // Contains SphereSelectLogic class
SphereSelectInput.cs    // Contains SphereSelectInput class
SphereSelectFeedback.cs // Contains SphereSelectFeedback class

Namespaces

Follow the pattern XRC.Toolkit.{PackageName}:

namespace XRC.Toolkit.SphereSelect
{
    // Package implementation
}

Methods

Use PascalCase for all method names:

// ✅ Correct
public void SetRadius(float radiusValue)
private void OnRadiusChanged(Vector2 radiusChange)
private void OnSelectEntered(SelectEnterEventArgs args)
private void CreateFeedbackVisuals()

// ❌ Incorrect
public void setRadius(float radiusValue)
private void on_radius_changed(Vector2 radiusChange)

Private fields

Use m_ prefix followed by PascalCase:

// ✅ Correct
[SerializeField]
private XRDirectInteractor m_Interactor;

[SerializeField]
private float m_MinRadius = 0.0175f;

[SerializeField]
private Material m_SphereMaterial;

private SphereCollider m_SphereCollider;
private GameObject m_Sphere;

// ❌ Incorrect
private XRDirectInteractor interactor;
private float minRadius;
private Material _sphereMaterial;

Public properties

Use camelCase for public property names:

// ✅ Correct
public XRDirectInteractor interactor
{
    get => m_Interactor;
    set => m_Interactor = value;
}

public float minRadius
{
    get => m_MinRadius;
    set => m_MinRadius = value;
}

// ❌ Incorrect
public XRDirectInteractor Interactor { get; set; }
public float MinRadius { get; set; }

Parameters and local variables

Use camelCase for parameters and local variables:

// ✅ Correct
public void SetRadius(float radiusValue)
{
    var centerPositionLocal = m_SphereCollider.transform.InverseTransformPoint(defaultCenter.position);
    float radiusDifference = defaultRadius - m_Radius;
}

private void OnRadiusChanged(Vector2 radiusChange)
{
    var newRadius = m_SphereSelectLogic.radius + radiusChange.y * inputSensitivity * Time.deltaTime;
}

// ❌ Incorrect
public void SetRadius(float RadiusValue)
{
    var CenterPositionLocal = m_SphereCollider.transform.InverseTransformPoint(defaultCenter.position);
}

Field and property patterns

Standard property pattern

Serialized fields and their associated public properties should follow this pattern:

[SerializeField]
private float m_MinRadius = 0.0175f;

/// <summary>
/// The minimum radius of the sphere.
/// </summary>
public float minRadius
{
    get => m_MinRadius;
    set => m_MinRadius = value;
}

Read-only properties

For computed or derived values, use read-only properties:

private float m_Radius;

/// <summary>
/// The current radius of the sphere.
/// </summary>
/// <remarks>
/// For setting the radius, use <see cref="SetRadius"/>.
/// </remarks>
public float radius => m_Radius;

Unity inspector fields

Always use [SerializeField] for fields that should appear in the inspector, do not use public fields:

[SerializeField]
[Tooltip("The Input Action that will be used to resize the sphere")]
private InputActionProperty m_ChangeRadius;

[SerializeField]
private Color m_Color = new Color(0f, 0, 1f, 0.2f);

[SerializeField]
private float m_InputSensitivity = 0.9f;

Code organization

Member ordering

Organize class members in this order:

  1. Serialized fields (with properties)
  2. Private fields
  3. Public properties
  4. Unity lifecycle methods (Awake, Start, OnEnable, OnDisable etc.)
  5. Public methods
  6. Private methods
public class SphereSelectLogic : MonoBehaviour
{
    // 1. Serialized fields
    [SerializeField]
    private XRDirectInteractor m_Interactor;

    [SerializeField]
    private float m_MinRadius = 0.0175f;

    // 2. Private fields
    private float m_Radius;
    private SphereCollider m_SphereCollider;

    // 3. Public properties
    public XRDirectInteractor interactor
    {
        get => m_Interactor;
        set => m_Interactor = value;
    }

    public float radius => m_Radius;

    // 4. Unity lifecycle methods
    private void Awake() { }
    private void Start() { }
    private void OnEnable() { }
    private void OnDisable() { }

    // 5. Public methods
    public void SetRadius(float radiusValue) { }

    // 6. Private methods
    private void OnSelectEntered(SelectEnterEventArgs args) { }
}

Unity-specific conventions

Component dependencies

Use [RequireComponent] to enforce dependencies:

[RequireComponent(typeof(SphereSelectLogic))]
public class SphereSelectInput : MonoBehaviour
{
    private SphereSelectLogic m_SphereSelectLogic;

    private void Awake()
    {
        m_SphereSelectLogic = GetComponent<SphereSelectLogic>();
    }
}

Tooltips

Add tooltips to serialized fields for designer clarity:

[SerializeField]
[Tooltip("The Input Action that will be used to resize the sphere")]
private InputActionProperty m_ChangeRadius;

[SerializeField]
[Tooltip("The scale factor applied to the user input when changing the radius value")]
private float m_InputSensitivity = 0.9f;

Input action properties

Use InputActionProperty for configurable input actions:

[SerializeField]
[Tooltip("The Input Action that will be used to resize the sphere")]
private InputActionProperty m_ChangeRadius;

/// <summary>
/// The input action property associated with changing the radius of the of sphere.
/// </summary>
public InputActionProperty changeRadiusAction
{
    get => m_ChangeRadius;
    set => m_ChangeRadius = value;
}

Formatting and structure

Braces

Use opening braces on new lines:

// ✅ Correct
public void SetRadius(float radiusValue)
{
    m_Radius = Mathf.Clamp(radiusValue, m_MinRadius, m_MaxRadius);

    if (m_Radius < defaultRadius)
    {
        float radiusDifference = defaultRadius - m_Radius;
        centerPositionLocal.x -= radiusDifference;
    }
}

// ❌ Incorrect
public void SetRadius(float radiusValue) {
    m_Radius = Mathf.Clamp(radiusValue, m_MinRadius, m_MaxRadius);
    if (m_Radius < defaultRadius) {
        float radiusDifference = defaultRadius - m_Radius;
        centerPositionLocal.x -= radiusDifference;
    }
}

Logical Grouping

Group related functionality with blank lines:

// Component setup
if (m_Interactor == null)
{
    m_Interactor = GetComponent<XRDirectInteractor>();
    if (m_Interactor == null)
    {
        Debug.LogError("Interactor not found");
    }
}

// Collider reference
m_SphereCollider = m_Interactor.gameObject.GetComponent<SphereCollider>();

XR interaction events

Use proper event subscription pattern for XR events:

private void OnEnable()
{
    m_SphereSelectLogic.interactor.hoverEntered.AddListener(OnHoverEntered);
    m_SphereSelectLogic.interactor.hoverExited.AddListener(OnHoverExited);
    m_SphereSelectLogic.interactor.selectEntered.AddListener(OnSelectEntered);
    m_SphereSelectLogic.interactor.selectExited.AddListener(OnSelectExited);
}

private void OnDisable()
{
    m_SphereSelectLogic.interactor.hoverEntered.RemoveListener(OnHoverEntered);
    m_SphereSelectLogic.interactor.hoverExited.RemoveListener(OnHoverExited);
    m_SphereSelectLogic.interactor.selectEntered.RemoveListener(OnSelectEntered);
    m_SphereSelectLogic.interactor.selectExited.RemoveListener(OnSelectExited);
}