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:
Additional information
- https://www.jetbrains.com/help/rider/Settings_Code_Style_CSHARP.html
- https://www.jetbrains.com/help/rider/Settings_Unity_Engine.html
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}:
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:
- Serialized fields (with properties)
- Private fields
- Public properties
- Unity lifecycle methods (Awake, Start, OnEnable, OnDisable etc.)
- Public methods
- 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);
}
