Partilhar via


Diretrizes de codificação

Este documento descreve as diretrizes de codificação recomendadas para World Locking Tools for Unity. A maioria dessas sugestões segue os padrões recomendados do MSDN.


Cabeçalhos de informações de licença de script

Todos os scripts postados no World Locking Tools for Unity devem ter o cabeçalho de licença padrão anexado, exatamente como mostrado abaixo:

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

Todos os arquivos de script enviados sem o cabeçalho da licença serão rejeitados.

Cabeçalhos de resumo de função / método

Todas as classes públicas, structs, enums, functions, properties, fields posted devem ser descritas quanto à sua finalidade e uso, exatamente como mostrado abaixo:

    /// <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;
    }

Essa regra garante que a documentação seja gerada e disseminada adequadamente para todas as classes, métodos e propriedades.

Todos os arquivos de script enviados sem as tags de resumo adequadas serão rejeitados.

Regras de namespace

Todas as classes e extensões devem ter escopo por namespace, escolhido apropriadamente entre os namespaces a seguir.

Microsoft.MixedReality.WorldLocking.Core - Código básico que cumpre o serviço básico das Ferramentas de Bloqueio Mundial.

Microsoft.MixedReality.WorldLocking.Tools - Recursos opcionais que complementam o desenvolvimento sobre as Ferramentas de Bloqueio Mundial. Exemplos são visualizações de diagnóstico e implementações de linha de base de manipuladores de eventos de aplicativo.

Microsoft.MixedReality.WorldLocking.Examples - Implementações específicas demonstrando como usar os recursos das Ferramentas de Bloqueio Mundial e os benefícios obtidos.

Recursos relacionados dentro de um dos namespaces acima podem ser agrupados estendendo-se para um novo subnamespace.

O que deve fazer

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

Omitir o namespace de uma interface, classe ou tipo de dados fará com que sua alteração seja bloqueada.

Espaços vs separadores

Certifique-se de usar quatro espaços em vez de guias ao contribuir para este projeto.

Além disso, certifique-se de que os espaços são adicionados para funções condicionais / loop como if / while / for

O que não deve fazer

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

O que deve fazer

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

Espaçamento

Não adicione espaços adicionais entre colchetes e parênteses:

O que não deve fazer

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

O que deve fazer

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

Convenções de nomenclatura

Utilize PascalCase sempre para propriedades públicas/protegidas/virtuais, e camelCase para propriedades e campos privados.

A única exceção a isso é para estruturas de dados que exigem que os campos sejam serializados pelo JsonUtility.

O que não deve fazer

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

O que deve fazer

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

Modificadores de acesso

Sempre declare um modificador de acesso para todos os campos, propriedades e métodos.

Todos os métodos de API Unity devem ser private por padrão, a menos que você precise substituí-los em uma classe derivada. Neste caso protected deve ser usado.

Os campos devem ser privatesempre , com public ou protected acessórios de propriedade.

O que não deve fazer

// 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() { }

O que deve fazer

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

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

Usar chaves

Use sempre chaves após cada bloco de instruções e coloque-as na próxima linha.

O que não deve fazer

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

O que não deve fazer

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

O que deve fazer

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

Classes públicas, estruturas e enums devem ir em seus próprios arquivos.

Se a classe, struct ou enum pode ser tornada privada, então não há problema em ser incluído no mesmo arquivo. Essa inclusão evita problemas de compilação com o Unity e garante que a abstração de código adequada ocorra. Ele também reduz conflitos e alterações de quebra quando o código precisa ser alterado.

O que não deve fazer

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

O que deve fazer

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

O que deve fazer

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;
}

Encomende Enums para extensão apropriada.

É fundamental que, se um Enum for provável que seja estendido no futuro, ordene a inadimplência no topo do Enum. Essa ordenação garante que os índices Enum não sejam afetados com novas adições.

O que não deve fazer

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

O que deve fazer

   /// <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 nomes de Enum com "Tipo"

Os nomes Enum devem indicar claramente sua natureza usando o sufixo Tipo.

O que não deve fazer

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

O que deve fazer

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

Rever o uso do Enum para Bitfields

Se houver a possibilidade de um enum exigir vários estados como um valor, por exemplo, Handedness = Left & Right. Em seguida, o Enum precisa ser decorado com BitFlags para permitir que ele seja usado corretamente

O dossiê Handedness.cs tem uma implementação concreta para isso

O que não deve fazer

public enum Handedness
{
    None,
    Left,
    Right
}

O que deve fazer

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

Melhores práticas, incluindo recomendações Unity

Algumas das plataformas-alvo deste projeto devem ter em consideração o desempenho. Com isso em mente, sempre tenha cuidado ao alocar memória em códigos frequentemente chamados em loops de atualização apertados ou algoritmos.

Encapsulamento

Sempre use campos privados e propriedades públicas se o acesso ao campo for necessário de fora da classe ou struct. Certifique-se de co-localizar o campo privado e a propriedade pública. Essa localização torna mais fácil ver, de relance, o que respalda a propriedade e que o campo é modificável por script.

Se você precisar ter a capacidade de editar seu campo no inspetor, é uma prática recomendada seguir as regras de Encapsulamento e serializar seu campo de suporte.

A única exceção a isso é para estruturas de dados que exigem que os campos sejam serializados pelo JsonUtility, onde uma classe de dados é necessária para ter todos os campos públicos para que a serialização funcione.

O que não deve fazer

public float MyValue;

O que deve fazer

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

O que deve fazer

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

O que não deve fazer

private float myValue1;
private float myValue2;

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

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

O que deve fazer

// 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 }
}

Utilizar for em vez de foreach quando possível

Em alguns casos, um foreach é necessário, por exemplo, ao fazer looping sobre um IEnumerable. Mas, para benefício de desempenho, evite foreach quando puder.

O que não deve fazer

foreach(var item in items)

O que deve fazer

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

Armazene valores em cache e serialize-os na cena/pré-fabricados sempre que possível.

Com o HoloLens em mente, é melhor otimizar o desempenho e as referências de cache na cena ou pré-fabricados para limitar as alocações de memória em tempo de execução.

O que não deve fazer

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

O que deve fazer

[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);
}

Cache referências a materiais, não chame o ".material" cada vez.

Unity criará um novo material cada vez que você usar ".material", que causará um vazamento de memória se não for limpo corretamente.

O que não deve fazer

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

O que deve fazer

// 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 a propriedade "SharedMaterial" do Unity, que não cria um novo material cada vez que ele é referenciado.

Use a compilação dependente da plataforma para garantir que o Toolkit não interrompa a compilação em outra plataforma

  • Use WINDOWS_UWP para usar APIs específicas da UWP e não Unity. Essa definição impedirá que eles tentem executar no Editor ou em plataformas não suportadas. Esta definição é equivalente e UNITY_WSA && !UNITY_EDITOR deve ser usada a favor.
  • Use UNITY_WSA para usar APIs Unity específicas da UWP, como o UnityEngine.XR.WSA namespace. Isso será executado no Editor quando a plataforma estiver definida como UWP e em aplicativos UWP criados.

Este gráfico pode ajudá-lo a decidir qual #if usar, dependendo dos seus casos de uso e das configurações de compilação esperadas.

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

Prefira DateTime.UtcNow a DateTime.Now

DateTime.UtcNow é mais rápido do que DateTime.Now. Em investigações de desempenho anteriores, descobrimos que o uso de DateTime.Now adiciona uma sobrecarga significativa, especialmente quando usado no loop Update(). Outros acertaram na mesma questão.

Prefira usar DateTime.UtcNow, a menos que você realmente precise das horas localizadas (um motivo legítimo pode ser você querer mostrar a hora atual no fuso horário do usuário). Se você estiver lidando com horários relativos (ou seja, o delta entre alguma última atualização e agora), é melhor usar DateTime.UtcNow para evitar a sobrecarga de fazer conversões de fuso horário.