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
JsonUtility
element .
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 przypadkuprotected
należy użyć.
Pola powinny zawsze mieć
private
wartość , z metodamipublic
dostępu właściwości lubprotected
.
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
JsonUtility
klasę , 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żnaUNITY_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 jakUnityEngine.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.