Udostępnij za pośrednictwem


Wytyczne dotyczące kodowania

W tym dokumencie opisano zalecane wytyczne dotyczące kodowania narzędzi World Locking Tools for Unity. Większość tych sugestii jest zgodne z zalecanymi standardami w witrynie MSDN.


Nagłówki informacji o licencji skryptu

Wszystkie skrypty opublikowane w narzędziach World Locking Tools for Unity powinny mieć dołączony standardowy nagłówek licencji, dokładnie tak jak pokazano poniżej:

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

Wszystkie pliki skryptów przesłane bez nagłówka licencji zostaną odrzucone.

Nagłówki podsumowania funkcji/metody

Wszystkie klasy publiczne, struktury, wyliczenia, funkcje, właściwości, opublikowane pola powinny być opisane jako ich przeznaczenie i użycie, dokładnie tak jak pokazano poniżej:

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

Ta reguła zapewnia prawidłowe generowanie i rozpowszechnianie dokumentacji dla wszystkich klas, metod i właściwości.

Wszystkie pliki skryptów przesłane bez odpowiednich tagów podsumowania zostaną odrzucone.

Reguły przestrzeni nazw

Wszystkie klasy i rozszerzenia powinny być objęte zakresem według przestrzeni nazw, wybrane odpowiednio z następujących przestrzeni nazw.

Microsoft.MixedReality.WorldLocking.Core — kod podstawy spełniający podstawową usługę World Locking Tools.

Microsoft.MixedReality.WorldLocking.Tools — opcjonalne funkcje uzupełniające programowanie na podstawie narzędzi world locking Tools. Przykłady to wizualizacje diagnostyczne i podstawowe implementacje procedur obsługi zdarzeń aplikacji.

Microsoft.MixedReality.WorldLocking.Examples — konkretne implementacje przedstawiające sposób korzystania z funkcji narzędzia World Locking Tools oraz korzyści uzyskane.

Powiązane funkcje w jednej z powyższych przestrzeni nazw mogą być pogrupowane przez rozszerzenie do nowej przestrzeni nazw podrzędnej.

Zalecenia

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

Pominięcie przestrzeni nazw dla interfejsu, klasy lub typu danych spowoduje zablokowanie zmiany.

Spacje a karty

Pamiętaj, aby używać czterech spacji zamiast kart podczas współtworzenia tego projektu.

Ponadto upewnij się, że spacje są dodawane dla funkcji warunkowych/pętli, takich jak if / while / dla

Zakazy

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

Zalecenia

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

Odstęp

Nie dodawaj dodatkowych spacji między nawiasami kwadratowymi i nawiasami:

Zakazy

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

Zalecenia

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

Konwencje nazewnictwa

Zawsze używaj PascalCase właściwości publicznych/chronionych/wirtualnych oraz camelCase prywatnych właściwości i pól.

Jedynym wyjątkiem od tego jest struktura danych, które wymagają serializacji pól przez JsonUtilityelement .

Zakazy

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

Zalecenia

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

Modyfikatory dostępu

Zawsze deklaruj modyfikator dostępu dla wszystkich pól, właściwości i metod.

Wszystkie metody interfejsu API aparatu Unity powinny być private domyślnie, chyba że trzeba je zastąpić w klasie pochodnej. W tym przypadku protected należy użyć.

Pola powinny zawsze mieć privatewartość , z metodami public dostępu właściwości lub protected .

Zakazy

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

Zalecenia

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

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

Używanie nawiasów klamrowych

Zawsze używaj nawiasów klamrowych po każdym bloku instrukcji i umieszczaj je w następnym wierszu.

Zakazy

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

Zakazy

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

Zalecenia

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

Klasy publiczne, struktury i wyliczenia powinny znajdować się we własnych plikach.

Jeśli klasa, struktura lub wyliczenie mogą być prywatne, wówczas można dołączyć ją do tego samego pliku. Włączenie pozwala uniknąć problemów z kompilacjami aparatu Unity i zapewnia, że wystąpi właściwa abstrakcja kodu. Zmniejsza również konflikty i zmiany powodujące niezgodność, gdy kod musi ulec zmianie.

Zakazy

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

Zalecenia

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

Zalecenia

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

Wyliczenie kolejności dla odpowiedniego rozszerzenia.

Ważne jest, aby w przypadku, gdy wyliczenie prawdopodobnie zostanie rozszerzone w przyszłości, aby uporządkować wartości domyślne w górnej części wyliczenia. Dzięki temu kolejność indeksów wyliczenia nie ma wpływu na nowe dodatki.

Zakazy

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

Zalecenia

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

End Enum names with "Type" (End Enum names with "Type" (End Enum names with "Type"

Nazwy wyliczenia powinny wyraźnie wskazywać ich charakter przy użyciu sufiksu Typ.

Zakazy

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

Zalecenia

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

Zapoznaj się z użyciem wyliczenia dla pól bitowych

Jeśli istnieje możliwość, aby wyliczenie wymagało wielu stanów jako wartości, na przykład Ręka = Lewa i Prawa. Następnie wyliczenie musi być ozdobione funkcją BitFlags, aby umożliwić poprawne jego użycie

Plik Handedness.cs ma konkretną implementację

Zakazy

public enum Handedness
{
    None,
    Left,
    Right
}

Zalecenia

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

Najlepsze rozwiązania, w tym zalecenia dotyczące aparatu Unity

Niektóre platformy docelowe tego projektu wymagają uwzględnienia wydajności. Mając to na uwadze, zawsze należy zachować ostrożność podczas przydzielania pamięci w często nazywanym kodem w ciasnych pętlach aktualizacji lub algorytmach.

Hermetyzacja

Zawsze używaj pól prywatnych i właściwości publicznych, jeśli wymagany jest dostęp do pola spoza klasy lub struktury. Pamiętaj, aby współlokować pole prywatne i właściwość publiczną. Ta lokalizacja ułatwia błyskawiczne wyświetlanie informacji o właściwości i tym, że pole można modyfikować za pomocą skryptu.

Jeśli musisz mieć możliwość edytowania pola w inspektorze, najlepszym rozwiązaniem jest przestrzeganie reguł hermetyzacji i serializacji pola zapasowego.

Jedynym wyjątkiem są struktury danych, które wymagają serializacji pól przez JsonUtilityklasę , gdzie klasa danych musi mieć wszystkie pola publiczne, aby serializacja działała.

Zakazy

public float MyValue;

Zalecenia

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

Zalecenia

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

Zakazy

private float myValue1;
private float myValue2;

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

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

Zalecenia

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

Użyj for zamiast foreach , jeśli jest to możliwe

W niektórych przypadkach wymagany jest foreach, na przykład w przypadku pętli na platformie IEnumerable. Jednak w celu uzyskania korzyści z wydajności unikaj foreach, kiedy to możliwe.

Zakazy

foreach(var item in items)

Zalecenia

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

Buforuj wartości i serializuj je w scenie/prefab, gdy jest to możliwe.

Mając na uwadze urządzenie HoloLens, najlepiej jest zoptymalizować pod kątem wydajności i odwołań pamięci podręcznej w scenie lub prefab, aby ograniczyć alokacje pamięci środowiska uruchomieniowego.

Zakazy

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

Zalecenia

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

Buforuj odwołania do materiałów, nie nazywają "material" za każdym razem.

Aparat Unity utworzy nowy materiał za każdym razem, gdy używasz formatu ".material", co spowoduje wyciek pamięci, jeśli nie zostanie prawidłowo oczyszczony.

Zakazy

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

Zalecenia

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

Alternatywnie należy użyć właściwości "SharedMaterial" aparatu Unity, która nie tworzy nowego materiału za każdym razem, gdy się do niego odwołuje.

Użyj kompilacji zależnej od platformy, aby upewnić się, że zestaw narzędzi nie uszkodzi kompilacji na innej platformie

  • Użyj WINDOWS_UWP polecenia , aby używać interfejsów API specyficznych dla platformy UWP, innych niż Unity. Ta definicja uniemożliwi im próbę uruchomienia w edytorze lub na nieobsługiwanych platformach. Ta definicja jest równoważna UNITY_WSA && !UNITY_EDITOR i powinna być używana na korzyść.
  • Służy UNITY_WSA do używania interfejsów API aparatu Unity specyficznych dla platformy UWP, takich jak UnityEngine.XR.WSA przestrzeń nazw. Spowoduje to uruchomienie w Edytorze, gdy platforma jest ustawiona na platformę UWP i w wbudowanych aplikacjach platformy UWP.

Ten wykres może pomóc w podjęciu decyzji #if o użyciu, w zależności od przypadków użycia i oczekiwanych ustawień kompilacji.

Definiowanie UWP IL2CPP Platforma UWP .NET Redaktor
UNITY_EDITOR Fałsz Fałsz Prawda
UNITY_WSA Prawda Prawda Prawda
WINDOWS_UWP Prawda Prawda Fałsz
UNITY_WSA && !UNITY_EDITOR Prawda Prawda Fałsz
ENABLE_WINMD_SUPPORT Prawda Prawda Fałsz
NETFX_CORE Fałsz Prawda Fałsz

Preferuj dateTime.UtcNow nad dateTime.Now

DataTime.UtcNow jest szybszy niż DateTime.Now. W poprzednich badaniach wydajności ustaliliśmy, że użycie elementu DateTime.Now zwiększa znaczne obciążenie szczególnie w przypadku użycia w pętli Update(). Inni trafili w ten sam problem.

Preferuj używanie elementu DateTime.UtcNow, chyba że rzeczywiście potrzebujesz zlokalizowanych godzin (uzasadniony powód może być tym, że chcesz pokazać bieżącą godzinę w strefie czasowej użytkownika). Jeśli masz do czynienia z czasem względnym (czyli różnicą między ostatnią aktualizacją a teraz), najlepiej użyć metody DateTime.UtcNow, aby uniknąć narzutów związanych z wykonywaniem konwersji strefy czasowej.