Compartir a través de


Directrices de codificación

En este documento se describen las directrices de codificación recomendadas para World Locking Tools para Unity. La mayoría de estas sugerencias siguen los estándares recomendados de MSDN.


Encabezados de información de licencia de los scripts

Todos los scripts publicados en World Locking Tools para Unity deben tener adjunto el encabezado de licencia estándar, tal y como se muestra a continuación:

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

Cualquier archivo de script enviado sin el encabezado de licencia se rechazará.

Encabezados de resumen de función o método

Todas las clases públicas, estructuras, enumeraciones, funciones, propiedades y campos que se publiquen deben describirse en relación con su propósito y uso, tal y como se muestra a continuación:

    /// <summary>
    /// The Controller definition defines the Controller as defined by the SDK / Unity.
    /// </summary>
    public struct Controller
    {
        /// <summary>
        /// The ID assigned to the Controller
        /// </summary>
        public string ID;
    }

Esta regla garantiza que la documentación se genere y se propague correctamente para todas las clases, métodos y propiedades.

Todo archivo de script que se envíe sin las etiquetas de resumen adecuadas se rechazará.

Reglas de espacios de nombres

Todas las clases y extensiones deben tener como ámbito un espacio de nombres, elegido correctamente entre los siguientes.

Microsoft.MixedReality.WorldLocking.Core: código fundamental que cumple el servicio básico de World Locking Tools.

Microsoft.MixedReality.WorldLocking.Tools: características opcionales que complementan el desarrollo sobre World Locking Tools. Algunos ejemplos de estas son las visualizaciones de diagnóstico y las implementaciones de base de referencia de los controladores de eventos de aplicación.

Microsoft.MixedReality.WorldLocking.Examples: implementaciones específicas que muestran cómo usar las características de World Locking Tools y las ventajas que se obtienen.

Las características relacionadas dentro de alguno de los espacios de nombres anteriores pueden agruparse mediante la ampliación a un nuevo subespacio de nombres.

Hacer

namespace Microsoft.MixedReality.WorldLocking.Examples.Placement
{
    // Interface, class or data type definition.
}

Si se omite el espacio de nombres de una interfaz, clase o tipo de datos, el cambio se bloqueará.

Espacios frente a pestañas

Asegúrese de usar cuatro espacios en lugar de pestañas al contribuir a este proyecto.

Además, asegúrese de que se agreguen espacios para las funciones condicionales o de bucle, como if/while/for

Lo que debe evitar:

private Foo () // < - space between Foo and ()
{
    if(Bar==null) // <- no space between if and ()
    {
        DoThing();
    }
    
    while(true) // <- no space between while and ()
    {
        Do();
    }
}

Lo que es necesario hacer:

private Foo()
{
   if (Bar==null)
   {
       DoThing();
   }
   
   while (true)
   {
       Do();
   }
}

Espaciado

No agregue espacios adicionales entre los corchetes y los paréntesis:

Lo que debe evitar:

private Foo()
{
    int[ ] var = new int [ 9 ];
    Vector2 vector = new Vector2 ( 0f, 10f );
}

Lo que es necesario hacer:

private Foo()
{
    int[] var = new int[9];
    Vector2 vector = new Vector2(0f, 10f);
}

Convenciones de nomenclatura

Use siempre PascalCase para las propiedades públicas, protegidas o virtuales y camelCase para las propiedades y los campos privados.

La única excepción aplicable es para las estructuras de datos que requieren la serialización de los campos mediante JsonUtility.

Lo que debe evitar:

public string myProperty; // <- Starts with a lower case letter
private string MyProperty; // <- Starts with an uppercase case letter

Lo que es necesario hacer:

public string MyProperty;
protected string MyProperty;
private string myProperty;

Modificadores de acceso

Declare siempre un modificador de acceso para todos los campos, propiedades y métodos.

Todos los métodos de API de Unity deben ser private de forma predeterminada, a menos que tenga que invalidarlos en una clase derivada. En este caso, se debe usar protected.

Los campos siempre deben ser private, con los descriptores de acceso de propiedades public o protected.

Lo que debe evitar:

// protected field should be private
protected int myVariable = 0;

// property should have protected setter
public int MyVariable { get { return myVariable; } }

// No public / private access modifiers
void Foo() { }
void Bar() { }

Lo que es necesario hacer:

public int MyVariable { get; protected set; } = 0;

private void Foo() { }
public void Bar() { }
protected virtual void FooBar() { }

Uso de llaves

Use siempre llaves después de cada bloque de instrucciones y colóquelas en la línea siguiente.

No

private Foo()
{
    if (Bar==null) // <- missing braces surrounding if action
        DoThing();
    else
        DoTheOtherThing();
}

Lo que debe evitar:

private Foo() { // <- Open bracket on same line
    if (Bar==null) DoThing(); <- if action on same line with no surrounding brackets 
    else DoTheOtherThing();
}

Lo que es necesario hacer:

private Foo()
{
    if (Bar==true)
    {
        DoThing();
    }
    else
    {
        DoTheOtherThing();
    }
}

Las clases, estructuras y enumeraciones públicas deben incluirse en archivos propios.

Si la clase, la estructura o la enumeración pueden convertirse en privadas, entonces pueden incluirse en el mismo archivo. Esta inclusión evita problemas de compilación con Unity y garantiza que se produzca la abstracción de código adecuada. También reduce los conflictos y los cambios importantes cuando es necesario cambiar código.

Lo que debe evitar:

public class MyClass
{
    public struct MyStruct() { }
    public enum MyEnumType() { }
    public class MyNestedClass() { }
}

Lo que es necesario hacer:

// Private references for use inside the class only
public class MyClass
{
   private struct MyStruct() { }
   private enum MyEnumType() { }
   private class MyNestedClass() { }
}

Hacer

MyStruct.cs

// Public Struct / Enum definitions for use in your class.  Try to make them generic for reuse.
public struct MyStruct
{
   public string Var1;
   public string Var2;
}

MyEnumType.cs

public enum MuEnumType
{
    Value1,
    Value2 // <- note, no "," on last value to denote end of list.
}

MyClass.cs

public class MyClass
{
    private MyStruct myStructreference;
    private MyEnumType myEnumReference;
}

Ordenación de las enumeraciones para la extensión adecuada.

Si hay probabilidades de que una enumeración pueda ampliarse en el futuro, es fundamental ordenar los valores predeterminados en la parte superior de esta. Esta ordenación garantiza que los índices de las enumeraciones no se vean afectados al agregar nuevos elementos.

Lo que debe evitar:

public enum SDKType
{
    WindowsMR,
    OpenVR,
    OpenXR,
    None, <- default value not at start
    Other <- anonymous value left to end of enum
}

Lo que es necesario hacer:

   /// <summary>
   /// The SDKType lists the VR SDK's that are supported by the MRTK
   /// Initially, this lists proposed SDK's, not all may be implemented at this time (please see ReleaseNotes for more details)
   /// </summary>
   public enum SDKType
   {
       /// <summary>
       /// No specified type or Standalone / non-VR type
       /// </summary>
       None = 0,
       /// <summary>
       /// Undefined SDK.
       /// </summary>
       Other,
       /// <summary>
       /// The Windows 10 Mixed reality SDK provided by the Universal Windows Platform (UWP), for Immersive MR headsets and HoloLens. 
       /// </summary>
       WindowsMR,
       /// <summary>
       /// The OpenVR platform provided by Unity (does not support the downloadable SteamVR SDK).
       /// </summary>
       OpenVR,
       /// <summary>
       /// The OpenXR platform. SDK to be determined once released.
       /// </summary>
       OpenXR
   }

Finalizar los nombres de las enumeraciones con "Type"

Los nombres de las enumeraciones deben indicar claramente su naturaleza mediante el sufijo Type.

Lo que debe evitar:

public enum Ordering
{
    First,
    Second,
    Third
}
public enum OrderingEnum
{
    First,
    Second,
    Third
}

Lo que es necesario hacer:

public enum OrderingType
{
    First = 0,
    Second,
    Third
}

Revisión del uso de las enumeraciones para campos de bits

Si existe la posibilidad de que una enumeración requiera varios estados como valor, por ejemplo, Handedness = Left & Right. esta debe decorarse con marcas de bits (BitFlag) para poder usarla correctamente.

El archivo Handedness.cs tiene una implementación concreta al respecto.

Lo que debe evitar:

public enum Handedness
{
    None,
    Left,
    Right
}

Lo que es necesario hacer:

[flags]
public enum HandednessType
{
   None = 0 << 0,
   Left = 1 << 0,
   Right = 1 << 1,
   Both = Left | Right
}

Procedimientos recomendados, incluidas las recomendaciones de Unity

Algunas de las plataformas de destino de este proyecto requieren tener en cuenta el rendimiento. Con esto en mente, tenga siempre cuidado al asignar memoria en código al que se llama con frecuencia en algoritmos o bucles de actualización estrictos.

Encapsulación

Use siempre campos privados y propiedades públicas si se requiere acceso al campo desde fuera de la clase o estructura. Asegúrese de coubicar el campo privado y la propiedad pública. Esta ubicación facilita poder ver de un vistazo qué respalda la propiedad y si un script puede modificar el campo.

Si necesita poder editar el campo en el inspector, el procedimiento recomendado es seguir las reglas de encapsulación y serializar el campo de respaldo.

La única excepción a esto son las estructuras de datos que requieren que JsonUtility serialice los campos, donde una clase de datos debe tener todos los campos públicos para que la serialización funcione.

Lo que debe evitar:

public float MyValue;

Lo que es necesario hacer:

// private field, only accessible within script (field is not serialized in Unity)
private float myValue;

Hacer

// Enable private field to be configurable only in editor (field is correctly serialized in Unity)
[SerializeField] 
private float myValue;

No

private float myValue1;
private float myValue2;

public float MyValue1
{
    get{ return myValue1; }
    set{ myValue1 = value }
}

public float MyValue2
{
    get{ return myValue2; }
    set{ myValue2 = value }
}

Lo que es necesario hacer:

// Enable field to be configurable in the editor and available externally to other scripts (field is correctly serialized in Unity)
[SerializeField]
[ToolTip("If using a tooltip, the text should match the public property's summary documentation, if appropriate.")]
private float myValue; // <- Notice we co-located the backing field above our corresponding property.

/// <summary>
/// If using a tooltip, the text should match the public property's summary documentation, if appropriate.
/// </summary>
public float MyValue
{
    get{ return myValue; }
    set{ myValue = value }
}

Usar for en lugar de foreach cuando sea posible

En algunos casos se requiere foreach; por ejemplo, al recorrer en bucle un elemento IEnumerable. Sin embargo, para obtener un mejor rendimiento, evite foreach siempre que sea posible.

Lo que debe evitar:

foreach(var item in items)

Lo que es necesario hacer:

int length = items.length; // cache reference to list/array length
for(int i=0; i < length; i++)

Almacenamiento en caché y serialización de valores en la escena o elemento prefabricado cuando sea posible.

Con HoloLens en mente, los mejor es optimizar el rendimiento y las referencias de caché en la escena o elemento prefabricado para limitar las asignaciones de memoria en tiempo de ejecución.

Lo que debe evitar:

void Update()
{
    gameObject.GetComponent<Renderer>().Foo(Bar);
}

Lo que es necesario hacer:

[SerializeField] // To enable setting the reference in the inspector.
private Renderer myRenderer;

private void Awake()
{
   // If you didn't set it in the inspector, then we cache it on awake.
   if (myRenderer == null)
   {
       myRenderer = gameObject.GetComponent<Renderer>();
   }
}

private void Update()
{
   myRenderer.Foo(Bar);
}

Almacenamiento en caché de referencias a materiales para no llamar a ".material" cada vez.

Unity creará un nuevo material cada vez que use ".material", lo que provocará una fuga de memoria si no se limpia correctamente.

Lo que debe evitar:

public class MyClass
{
    void Update() 
    {
        Material myMaterial = GetComponent<Renderer>().material;
        myMaterial.SetColor("_Color", Color.White);
    }
}

Lo que es necesario hacer:

// Private references for use inside the class only
public class MyClass
{
   private Material cachedMaterial;

   private void Awake()
   {
       cachedMaterial = GetComponent<Renderer>().material;
   }

   void Update() 
   {
       cachedMaterial.SetColor("_Color", Color.White);
   }
   
   private void OnDestroy()
   {
       Destroy(cachedMaterial);
   }
}

Como alternativa, use la propiedad "SharedMaterial" de Unity, que no crea un nuevo material cada vez que se le hace referencia.

Usar la compilación dependiente de la plataforma para garantizar que el kit de herramientas no interrumpa la compilación en otra plataforma.

  • Utilice WINDOWS_UWP para usar API que no son de Unity y no son específicas de UWP. Esta definición impedirá que se intente ejecutar en el editor o en plataformas no compatibles. La definición es equivalente a UNITY_WSA && !UNITY_EDITOR y debe usarse en lugar de esta.
  • Utilice UNITY_WSA para usar las API de Unity específicas de UWP, como el espacio de nombres UnityEngine.XR.WSA. Este se ejecutará en el editor cuando la plataforma esté establecida en UWP y en aplicaciones integradas para UWP.

Este gráfico puede ayudarle a decidir el elemento #if que debe utilizar, en función de los casos de uso y de la configuración de compilación esperada.

Definir UWP IL2CPP UWP .NET Editor
UNITY_EDITOR False False True
UNITY_WSA True True True
WINDOWS_UWP True True False
UNITY_WSA && !UNITY_EDITOR True True False
ENABLE_WINMD_SUPPORT True True False
NETFX_CORE False True False

Preferencia de DateTime.UtcNow frente a DateTime.Now

DateTime.UtcNow es más rápido que DateTime.Now. En investigaciones de rendimiento anteriores se descubrió que el uso de DateTime.Now agrega una sobrecarga considerable, sobre todo cuando se usa en el bucle Update(). Otros han tenido el mismo problema.

Es preferible usar DateTime.UtcNow, a menos que realmente necesite las horas localizadas (un motivo válido puede ser que quiera mostrar la hora actual en la zona horaria del usuario). Si está trabajando con horas relativas (es decir, la diferencia entre alguna última actualización y el momento actual), es mejor usar DateTime.UtcNow para evitar la sobrecarga de realizar conversiones de zona horaria.