Codierungsrichtlinien
In diesem Dokument werden die empfohlenen Codierungsrichtlinien für World Locking Tools for Unity beschrieben. Die meisten dieser Vorschläge folgen den empfohlenen Standards von MSDN.
Skriptheader mit Lizenzinformationen
Alle in World Locking Tools for Unity geposteten Skripts müssen den standardmäßigen Lizenzheader enthalten, genau wie unten gezeigt:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
Alle ohne Lizenzheader übermittelten Skriptdateien werden abgelehnt.
Header mit Funktions-/Methodenzusammenfassung
Alle bereitgestellten öffentlichen Klassen, Strukturen, Enumerationen, Funktionen, Eigenschaften und Felder müssen hinsichtlich ihres Zwecks und ihrer Verwendung beschrieben werden, genau wie unten dargestellt:
/// <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;
}
Diese Regel stellt sicher, dass die Dokumentation für alle Klassen, Methoden und Eigenschaften ordnungsgemäß erstellt und verteilt wird.
Alle ohne ordnungsgemäße Zusammenfassung übermittelten Skriptdateien werden abgelehnt.
Namespaceregeln
Alle Klassen und Erweiterungen müssen auf einen Namespace festgelegt werden, wobei eine geeignete Auswahl aus den folgenden Namespaces zu treffen ist.
Microsoft.MixedReality.WorldLocking.Core: Basiscode, der den grundlegenden Dienst der World Locking Tools erfüllt.
Microsoft.MixedReality.WorldLocking.Tools: Optionale Funktionen zur Ergänzung der Entwicklung auf der Basis von World Locking Tools. Beispiele sind Diagnosevisualisierungen und Basisimplementierungen von Anwendungsereignishandlern.
Microsoft.MixedReality.WorldLocking.Examples: Spezifische Implementierungen, die die Verwendung der World Locking Tools-Features veranschaulichen und die damit erzielten Vorteile aufzeigen.
Zugehörige Features innerhalb von einem der oben genannten Namespaces können durch Erweiterung in einem neuen untergeordneten Namespace gruppiert werden.
Empfohlen
namespace Microsoft.MixedReality.WorldLocking.Examples.Placement
{
// Interface, class or data type definition.
}
Wenn Sie den Namespace für eine Schnittstelle, eine Klasse oder einen Datentyp weglassen, wird Ihre Änderung blockiert.
Leerzeichen anstelle von Tabstopps
Achten Sie bei Ihren Beiträgen zu diesem Projekt darauf, dass Sie vier Leerzeichen anstelle von Tabstopps verwenden.
Stellen Sie außerdem sicher, dass Leerzeichen für bedingte Funktionen/Schleifenfunktionen wie „if“, „while“, „for“ hinzugefügt werden.
Sie sollten auf keinen Fall
private Foo () // < - space between Foo and ()
{
if(Bar==null) // <- no space between if and ()
{
DoThing();
}
while(true) // <- no space between while and ()
{
Do();
}
}
Sie sollten
private Foo()
{
if (Bar==null)
{
DoThing();
}
while (true)
{
Do();
}
}
Abstand
Fügen Sie keine zusätzlichen Leerzeichen zwischen eckigen Klammern und runden Klammern hinzu:
Sie sollten auf keinen Fall
private Foo()
{
int[ ] var = new int [ 9 ];
Vector2 vector = new Vector2 ( 0f, 10f );
}
Sie sollten
private Foo()
{
int[] var = new int[9];
Vector2 vector = new Vector2(0f, 10f);
}
Namenskonventionen
Verwenden Sie immer PascalCase
für öffentliche/geschützte/virtuelle Eigenschaften und camelCase
für private Eigenschaften und Felder.
Die einzige Ausnahme sind Datenstrukturen, bei denen die Felder über
JsonUtility
serialisiert werden müssen.
Sie sollten auf keinen Fall
public string myProperty; // <- Starts with a lower case letter
private string MyProperty; // <- Starts with an uppercase case letter
Sie sollten
public string MyProperty;
protected string MyProperty;
private string myProperty;
Zugriffsmodifizierer
Deklarieren Sie immer einen Zugriffsmodifizierer für alle Felder, Eigenschaften und Methoden.
Alle Unity-API-Methoden sollten standardmäßig als
private
definiert sein – es sei denn, Sie müssen sie in einer abgeleiteten Klasse überschreiben. In diesem Fall mussprotected
verwendet werden.
Felder sollten immer
private
privat sein, mit Eigenschaftenzugriffpublic
oderprotected
.
Sie sollten auf keinen Fall
// 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() { }
Sie sollten
public int MyVariable { get; protected set; } = 0;
private void Foo() { }
public void Bar() { }
protected virtual void FooBar() { }
Verwenden von geschweiften Klammern
Verwenden Sie nach jedem Anweisungsblock stets geschweifte Klammern, und platzieren Sie sie in der nächsten Zeile.
Tue nicht
private Foo()
{
if (Bar==null) // <- missing braces surrounding if action
DoThing();
else
DoTheOtherThing();
}
Sie sollten auf keinen Fall
private Foo() { // <- Open bracket on same line
if (Bar==null) DoThing(); <- if action on same line with no surrounding brackets
else DoTheOtherThing();
}
Sie sollten
private Foo()
{
if (Bar==true)
{
DoThing();
}
else
{
DoTheOtherThing();
}
}
Öffentliche Klassen, Strukturen und Enumerationen müssen in eigenen Dateien gespeichert werden.
Wenn die Klasse, die Struktur oder die Enumeration als privat gekennzeichnet werden kann, dann kann sie in dieselbe Datei aufgenommen werden. Durch diese Einbindung werden Kompilierungsprobleme mit Unity vermieden und eine ordnungsgemäße Codeabstraktion gewährleistet. Außerdem werden Konflikte und Breaking Changes reduziert, wenn Code geändert werden muss.
Sie sollten auf keinen Fall
public class MyClass
{
public struct MyStruct() { }
public enum MyEnumType() { }
public class MyNestedClass() { }
}
Sie sollten
// Private references for use inside the class only
public class MyClass
{
private struct MyStruct() { }
private enum MyEnumType() { }
private class MyNestedClass() { }
}
Empfohlen
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;
}
Anordnen von Enumerationen für eine ordnungsgemäße Erweiterung
Wenn eine Enumeration in Zukunft erweitert werden soll, ist es wichtig, die Standardwerte am Anfang der Enumeration anzuordnen. So wird sichergestellt, dass Enumerationsindizes nicht durch neue Hinzufügungen beeinträchtigt werden.
Sie sollten auf keinen Fall
public enum SDKType
{
WindowsMR,
OpenVR,
OpenXR,
None, <- default value not at start
Other <- anonymous value left to end of enum
}
Sie sollten
/// <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
}
Enumerationsnamen müssen auf „Type“ enden
Die Art einer Enumeration sollte durch die Verwendung des Suffixes „Type“ im Namen eindeutig angegeben werden.
Sie sollten auf keinen Fall
public enum Ordering
{
First,
Second,
Third
}
public enum OrderingEnum
{
First,
Second,
Third
}
Sie sollten
public enum OrderingType
{
First = 0,
Second,
Third
}
Überprüfen der Verwendung von Bitfeldern für eine Enumeration
Wenn eine Enumeration mehrere Zustände als Wert erfordert, z. B. "Händigkeit = Links & Rechts". dann muss die Enumeration mit BitFlags versehen werden, damit sie ordnungsgemäß verwendet werden kann.
Die Datei „Handedness.cs“ enthält eine konkrete Implementierung für diesen Fall.
Sie sollten auf keinen Fall
public enum Handedness
{
None,
Left,
Right
}
Sie sollten
[flags]
public enum HandednessType
{
None = 0 << 0,
Left = 1 << 0,
Right = 1 << 1,
Both = Left | Right
}
Best Practices, einschließlich Unity-Empfehlungen
Einige der Zielplattformen dieses Projekts erfordern eine Berücksichtigung der Leistung. Vor diesem Hintergrund sollten Sie bei der Speicherzuweisung in häufig aufgerufenem Code mit kurzen Aktualisierungsschleifen oder Algorithmen vorsichtig sein.
Kapselung
Verwenden Sie immer private Felder und öffentliche Eigenschaften, wenn von außerhalb der Klasse oder Struktur auf das Feld zugegriffen werden muss. Achten Sie darauf, dass das private Feld und die öffentliche Eigenschaft nebeneinander platziert werden. So ist auf einen Blick zu erkennen, worauf die Eigenschaft beruht und dass das Feld per Skript geändert werden kann.
Wenn Sie das Feld im Inspector bearbeiten möchten, empfiehlt es sich, die Regeln für die Kapselung zu befolgen und das zugrunde liegende Feld zu serialisieren.
Die einzige Ausnahme sind Datenstrukturen, bei denen die Felder über
JsonUtility
serialisiert werden müssen, wobei eine Datenklasse zwingend alle öffentlichen Felder enthalten muss, damit die Serialisierung funktioniert.
Sie sollten auf keinen Fall
public float MyValue;
Sie sollten
// private field, only accessible within script (field is not serialized in Unity)
private float myValue;
Tun
// Enable private field to be configurable only in editor (field is correctly serialized in Unity)
[SerializeField]
private float myValue;
Tue nicht
private float myValue1;
private float myValue2;
public float MyValue1
{
get{ return myValue1; }
set{ myValue1 = value }
}
public float MyValue2
{
get{ return myValue2; }
set{ myValue2 = value }
}
Sie sollten
// 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 }
}
Verwenden von for
anstelle von foreach
(falls möglich)
In einigen Fällen ist „foreach“ erforderlich, z. B. beim Durchlaufen von „IEnumerable“. Aus Leistungsgründen sollten Sie „foreach“ jedoch nach Möglichkeit vermeiden.
Sie sollten auf keinen Fall
foreach(var item in items)
Sie sollten
int length = items.length; // cache reference to list/array length
for(int i=0; i < length; i++)
Zwischenspeichern von Werten und Serialisierung in Szene/Prefab (falls möglich)
Mit Blick auf das HoloLens-System empfiehlt es sich, die Leistung zu optimieren und Verweise in der Szene oder dem Prefab zwischenzuspeichern, um die Speicherbelegung während der Laufzeit zu begrenzen.
Sie sollten auf keinen Fall
void Update()
{
gameObject.GetComponent<Renderer>().Foo(Bar);
}
Sie sollten
[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);
}
Zwischenspeichern von Materialverweisen anstelle eines immer neuen Aufrufs von „.material“
Unity erstellt bei jeder Verwendung von „.material“ neues Material, was zu einem Speicherleck führt, wenn keine ordnungsgemäße Bereinigung erfolgt.
Sie sollten auf keinen Fall
public class MyClass
{
void Update()
{
Material myMaterial = GetComponent<Renderer>().material;
myMaterial.SetColor("_Color", Color.White);
}
}
Sie sollten
// 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);
}
}
Verwenden Sie alternativ die Unity-Eigenschaft „SharedMaterial“, die nicht bei jedem Verweis neues Material erstellt.
Verwenden einer plattformabhängigen Kompilierung, um Buildfehler durch das Toolkit auf anderen Plattformen zu vermeiden
- Verwenden Sie
WINDOWS_UWP
, um UWP-spezifische, Nicht-Unity-APIs zu nutzen. Diese Definition verhindert die Ausführung im Editor oder auf nicht unterstützten Plattformen. Diese Definition ist äquivalent zuUNITY_WSA && !UNITY_EDITOR
und sollte bevorzugt verwendet werden. - Verwenden Sie
UNITY_WSA
, um UWP-spezifische Unity-APIs zu nutzen, z. B. denUnityEngine.XR.WSA
-Namespace. Die Ausführung erfolgt im Editor, wenn die Plattform auf UWP festgelegt ist, und in erstellten UWP-Apps.
Anhand der folgenden Tabelle können Sie entscheiden, welche #if
-Option Sie in Abhängigkeit von Ihren Anwendungsfällen und den erwarteten Buildeinstellungen verwenden sollten.
Definieren | UWP IL2CPP | UWP .NET | Editor |
---|---|---|---|
UNITY_EDITOR |
False | Falsch | Wahr |
UNITY_WSA |
True | True | True |
WINDOWS_UWP |
True | True | Falsch |
UNITY_WSA && !UNITY_EDITOR |
Wahr | True | Falsch |
ENABLE_WINMD_SUPPORT |
Wahr | True | False |
NETFX_CORE |
Falsch | True | Falsch |
„DateTime.UtcNow“ gegenüber „DateTime.Now“ bevorzugen
„DateTime.UtcNow“ ist schneller als „DateTime.Now“. In vergangenen Leistungsuntersuchungen haben wir festgestellt, dass die Verwendung von „DateTime.Now“ erheblichen Zusatzaufwand verursacht, insbesondere bei Verwendung in der Update()-Schleife. Dieses Problem ist auch bei anderen Benutzern aufgetreten.
Verwenden Sie lieber „DateTime.UtcNow“ – es sei denn, Sie benötigen tatsächlich die lokalisierten Zeiten (ein legitimer Grund könnte sein, dass Sie die aktuelle Zeit in der Zeitzone des Benutzers anzeigen möchten). Bei relativen Zeitangaben (d. h. dem Delta zwischen der letzten Aktualisierung und dem aktuellen Zeitpunkt) sollte am besten, „DateTime.UtcNow“ verwendet werden, um den Zusatzaufwand für die Zeitzonenumrechnung zu vermeiden.