Programmierrichtlinien – MRTK2

In diesem Dokument werden die Programmierprinzipien und Konventionen beschrieben, die bei der Mitwirkung an MRTK zu befolgen sind.


Philosophie

Seien Sie prägnant und streben Sie nach Einfachheit

Die einfachste Lösung ist oft die beste. Dies ist ein übergeordnetes Ziel dieser Richtlinien und sollte das Ziel aller Codierungsaktivitäten sein. Ein Teil der Einfachheit besteht darin, präzise und mit vorhandenem Code konsistent zu sein. Versuchen Sie, Ihren Code einfach zu halten.

Leser sollten nur Artefakten begegnen, die nützliche Informationen bereitstellen. Beispielsweise bieten Kommentare, die das Offensichtliche neu darstellen, keine zusätzlichen Informationen und erhöhen das Verhältnis von Rauschen zu Signalen.

Halten Sie codelogik einfach. Beachten Sie, dass es sich dabei nicht um eine Anweisung zur Verwendung der geringsten Anzahl von Zeilen handelt, um die Größe von Bezeichnernamen oder geschweiften Stilen zu minimieren, sondern um die Reduzierung der Anzahl von Konzepten und die Maximierung der Sichtbarkeit dieser durch vertraute Muster.

Erstellen von konsistentem, lesbaren Code

Die Lesbarkeit von Code wird mit niedrigen Fehlerraten korreliert. Versuchen Sie, einfach zu lesenden Code zu erstellen. Versuchen Sie, Code mit einfacher Logik zu erstellen und vorhandene Komponenten wiederzuverwenden, da dies auch zur Sicherstellung der Korrektheit beizutragen ist.

Alle Details des codes, den Sie erstellen, sind wichtig, von den grundlegendsten Details der Korrektheit bis hin zu konsistentem Stil und Formatierung. Halten Sie Ihren Codierungsstil mit dem, was bereits vorhanden ist, konsistent, auch wenn er nicht Ihren Vorlieben entspricht. Dadurch wird die Lesbarkeit der gesamten Codebasis erhöht.

Unterstützung der Konfiguration von Komponenten sowohl im Editor als auch zur Laufzeit

MRTK unterstützt eine Vielzahl von Benutzern – Personen, die Komponenten im Unity-Editor konfigurieren und Prefabs laden möchten, und Personen, die Objekte zur Laufzeit instanziieren und konfigurieren müssen.

Der gesamte Code sollte funktionieren, indem sowohl eine Komponente zu einem GameObject in einer gespeicherten Szene hinzugefügt wird, als auch diese Komponente im Code instanziiert wird. Tests sollten einen Testfall sowohl zum Instanziieren von Prefabs als auch zum Instanziieren enthalten, wobei die Komponente zur Laufzeit konfiguriert wird.

Play-in-Editor ist Ihre erste und primäre Zielplattform

Play-In-Editor ist die schnellste Möglichkeit, in Unity zu iterieren. Wenn unsere Kunden Schnell iterieren können, können sie lösungen schneller entwickeln und weitere Ideen ausprobieren. Anders ausgedrückt: Die Maximierung der Iterationsgeschwindigkeit ermöglicht es unseren Kunden, mehr zu erreichen.

Sorgen Sie dafür, dass alles im Editor funktioniert und dann auf jeder anderen Plattform funktioniert. Bleiben Sie im Editor funktionsfähig. Es ist einfach, Play-In-Editor eine neue Plattform hinzuzufügen. Es ist sehr schwierig, den Play-In-Editor zum Arbeiten zu bringen, wenn Ihre App nur auf einem Gerät funktioniert.

Hinzufügen neuer öffentlicher Felder, Eigenschaften, Methoden und serialisierter privater Felder mit Bedacht

Jedes Mal, wenn Sie eine öffentliche Methode, ein Feld oder eine Eigenschaft hinzufügen, wird sie Teil der öffentlichen API-Oberfläche von MRTK. Private Felder, die mit [SerializeField] gekennzeichnet sind, machen auch Felder für den Editor verfügbar und sind Teil der öffentlichen API-Oberfläche. Andere Personen können diese öffentliche Methode verwenden, benutzerdefinierte Prefabs mit Ihrem öffentlichen Feld konfigurieren und eine Abhängigkeit davon annehmen.

Neue öffentliche Mitglieder sollten sorgfältig geprüft werden. Alle öffentlichen Felder müssen in Zukunft beibehalten werden. Denken Sie daran: Wenn sich der Typ eines öffentlichen Felds (oder eines serialisierten privaten Felds) ändert oder aus einem MonoBehaviour entfernt wird, kann dies andere Personen beeinträchtigen. Das Feld muss zunächst für ein Release veraltet sein, und Code zum Migrieren von Änderungen für Personen, die Abhängigkeiten angenommen haben, muss bereitgestellt werden.

Priorisieren des Schreibens von Tests

MRTK ist ein Community-Projekt, das von einer Vielzahl von Mitwirkenden modifiziert wurde. Diese Mitwirkenden kennen möglicherweise nicht die Details Ihrer Fehlerbehebung bzw. Ihres Features und unterbrechen versehentlich Ihr Feature. MRTK führt Continuous Integration-Tests aus, bevor jeder Pull Request abgeschlossen wird. Änderungen, die Tests unterbrechen, können nicht eingecheckt werden. Daher sind Tests die beste Möglichkeit, um sicherzustellen, dass andere Personen Ihr Feature nicht unterbrechen.

Wenn Sie einen Fehler beheben, schreiben Sie einen Test, um sicherzustellen, dass er in Zukunft nicht zurückgestellt wird. Wenn Sie ein Feature hinzufügen, schreiben Sie Tests, die überprüfen, ob Ihr Feature funktioniert. Dies ist für alle UX-Features mit Ausnahme experimenteller Features erforderlich.

C#-Codierungskonventionen

Skriptheader mit Lizenzinformationen

Alle Microsoft-Mitarbeiter, die neue Dateien beitragen, sollten den folgenden Standard-Lizenzheader oben in allen neuen Dateien hinzufügen, genau wie unten gezeigt:

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

Funktions-/Methodenzusammenfassungsheader

Alle öffentlichen Klassen, Strukturen, Enumerationen, Funktionen, Eigenschaften und Felder, die im MRTK bereitgestellt werden, sollten genau wie unten dargestellt als zwecks Und Verwendung beschrieben werden:

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

Dadurch wird sichergestellt, dass die Dokumentation für alle Klassen, Methoden und Eigenschaften ordnungsgemäß generiert und verbreitet wird.

Alle ohne ordnungsgemäße Zusammenfassung übermittelten Skriptdateien werden abgelehnt.

MRTK-Namespaceregeln

Das Mixed Reality Toolkit verwendet ein featurebasiertes Namespacemodell, bei dem alle grundlegenden Namespaces mit "Microsoft.MixedReality.Toolkit" beginnen. Im Allgemeinen müssen Sie die Toolkitebene (z. B. Core, Anbieter, Dienste) in Ihren Namespaces nicht angeben.

Die derzeit definierten Namespaces sind:

  • Microsoft.MixedReality.Toolkit
  • Microsoft.MixedReality.Toolkit.Boundary
  • Microsoft.MixedReality.Toolkit.Diagnostics
  • Microsoft.MixedReality.Toolkit.Editor
  • Microsoft.MixedReality.Toolkit.Input
  • Microsoft.MixedReality.Toolkit.SpatialAwareness
  • Microsoft.MixedReality.Toolkit.Teleport
  • Microsoft.MixedReality.Toolkit.Utilities

Für Namespaces mit einer großen Anzahl von Typen ist es akzeptabel, eine begrenzte Anzahl von Unternamespaces zu erstellen, um die Bereichsverwendung zu unterstützen.

Wenn Sie den Namespace für eine Schnittstelle, eine Klasse oder einen Datentyp weglassen, wird Ihre Änderung blockiert.

Hinzufügen neuer MonoBehaviour-Skripts

Wenn Sie neue MonoBehaviour-Skripts mit einem Pull Request hinzufügen, stellen Sie sicher, dass das AddComponentMenu Attribut auf alle anwendbaren Dateien angewendet wird. Dadurch wird sichergestellt, dass die Komponente im Editor unter der Schaltfläche Komponente hinzufügen leicht erkennbar ist. Das Attributflag ist nicht erforderlich, wenn die Komponente nicht im Editor angezeigt werden kann, z. B. in einer abstrakten Klasse.

Im folgenden Beispiel sollte das Paket hier mit dem Paketspeicherort der Komponente gefüllt werden. Wenn Sie ein Element im ORDNER MRTK/SDK platzieren, ist das Paket SDK.

[AddComponentMenu("Scripts/MRTK/{Package here}/MyNewComponent")]
public class MyNewComponent : MonoBehaviour

Hinzufügen neuer Unity-Inspektorskripts

Versuchen Sie im Allgemeinen, benutzerdefinierte Inspektorskripts für MRTK-Komponenten zu vermeiden. Es fügt zusätzlichen Mehraufwand und die Verwaltung der Codebasis hinzu, die von der Unity-Engine verarbeitet werden kann.

Wenn eine Inspektorklasse erforderlich ist, versuchen Sie, die -Klasse von DrawDefaultInspector()Unity zu verwenden. Dies vereinfacht wiederum die Inspektorklasse und überlässt einen Großteil der Arbeit an Unity.

public override void OnInspectorGUI()
{
    // Do some custom calculations or checks
    // ....
    DrawDefaultInspector();
}

Wenn in der Inspektorklasse benutzerdefiniertes Rendering erforderlich ist, versuchen Sie, und EditorGUILayout.PropertyFieldzu verwendenSerializedProperty. Dadurch wird sichergestellt, dass Unity das Rendern geschachtelter Prefabs und geänderter Werte ordnungsgemäß verarbeitet.

Wenn EditorGUILayout.PropertyField aufgrund einer Anforderung in benutzerdefinierter Logik nicht verwendet werden kann, stellen Sie sicher, dass die gesamte Nutzung um einen EditorGUI.PropertyScopeumschlossen ist. Dadurch wird sichergestellt, dass Unity den Inspektor für geschachtelte Prefabs und geänderte Werte mit der angegebenen Eigenschaft ordnungsgemäß rendert.

Versuchen Sie außerdem, die benutzerdefinierte Inspektorklasse mit einem CanEditMultipleObjectszu versehen. Dieses Tag stellt sicher, dass mehrere Objekte mit dieser Komponente in der Szene gemeinsam ausgewählt und geändert werden können. Alle neuen Inspektorklassen sollten testen, dass ihr Code in dieser Situation in der Szene funktioniert.

    // Example inspector class demonstrating usage of SerializedProperty & EditorGUILayout.PropertyField
    // as well as use of EditorGUI.PropertyScope for custom property logic
    [CustomEditor(typeof(MyComponent))]
    public class MyComponentInspector : UnityEditor.Editor
    {
        private SerializedProperty myProperty;
        private SerializedProperty handedness;

        protected virtual void OnEnable()
        {
            myProperty = serializedObject.FindProperty("myProperty");
            handedness = serializedObject.FindProperty("handedness");
        }

        public override void OnInspectorGUI()
        {
            EditorGUILayout.PropertyField(destroyOnSourceLost);

            Rect position = EditorGUILayout.GetControlRect();
            var label = new GUIContent(handedness.displayName);
            using (new EditorGUI.PropertyScope(position, label, handedness))
            {
                var currentHandedness = (Handedness)handedness.enumValueIndex;

                handedness.enumValueIndex = (int)(Handedness)EditorGUI.EnumPopup(
                    position,
                    label,
                    currentHandedness,
                    (value) => {
                        // This function is executed by Unity to determine if a possible enum value
                        // is valid for selection in the editor view
                        // In this case, only Handedness.Left and Handedness.Right can be selected
                        return (Handedness)value == Handedness.Left
                        || (Handedness)value == Handedness.Right;
                    });
            }
        }
    }

Hinzufügen neuer ScriptableObjects

Stellen Sie beim Hinzufügen neuer ScriptableObject-Skripts sicher, dass das CreateAssetMenu Attribut auf alle anwendbaren Dateien angewendet wird. Dadurch wird sichergestellt, dass die Komponente im Editor über die Menüs zum Erstellen von Medienobjekten leicht auffindbar ist. Das Attributflag ist nicht erforderlich, wenn die Komponente nicht im Editor angezeigt werden kann, z. B. in einer abstrakten Klasse.

Im folgenden Beispiel sollte der Unterordner ggf. mit dem MRTK-Unterordner gefüllt werden. Wenn Sie ein Element im Ordner MRTK/Providers platzieren, lautet das Paket Anbieter. Wenn Sie ein Element im Ordner MRTK/Core platzieren, legen Sie dies auf "Profile" fest.

Im folgenden Beispiel wird myNewService | MyNewProvider sollte ggf. mit dem Namen Ihrer neuen Klasse gefüllt werden. Wenn Sie ein Element im Ordner MixedRealityToolkit platzieren, lassen Sie diese Zeichenfolge außen vor.

[CreateAssetMenu(fileName = "MyNewProfile", menuName = "Mixed Reality Toolkit/{Subfolder}/{MyNewService | MyNewProvider}/MyNewProfile")]
public class MyNewProfile : ScriptableObject

Protokollierung

Wenn Sie neue Features hinzufügen oder vorhandene Features aktualisieren, sollten Sie debugUtilities.LogVerbose-Protokolle zu interessantem Code hinzufügen, der für zukünftiges Debuggen nützlich sein kann. Es gibt hier einen Kompromiss zwischen dem Hinzufügen von Protokollierung und dem zusätzlichen Rauschen und nicht genügend Protokollierung (was die Diagnose erschwert).

Ein interessantes Beispiel, in dem die Protokollierung (zusammen mit interessanter Nutzlast) nützlich ist:

DebugUtilities.LogVerboseFormat("RaiseSourceDetected: Source ID: {0}, Source Type: {1}", source.SourceId, source.SourceType);

Diese Art der Protokollierung kann beim Abfangen von Problemen wie https://github.com/microsoft/MixedRealityToolkit-Unity/issues/8016helfen, die durch nicht übereinstimmende Quellereignisse und quellvergangene Ereignisse verursacht wurden.

Vermeiden Sie das Hinzufügen von Protokollen für Daten und Ereignisse, die in jedem Frame auftreten. Im Idealfall sollte die Protokollierung "interessante" Ereignisse abdecken, die durch unterschiedliche Benutzereingaben gesteuert werden (d. h. ein "Klick" eines Benutzers und die Gruppe von Änderungen und Ereignissen, die für die Protokollierung interessant sind). Der fortlaufende Status von "Benutzer hält immer noch eine Geste" protokolliert jeder Frame ist nicht interessant und überfordert die Protokolle.

Beachten Sie, dass diese ausführliche Protokollierung standardmäßig nicht aktiviert ist (sie muss in den Einstellungen des Diagnosesystems aktiviert sein).

Leerzeichen im Vergleich zu Registerkarten

Achten Sie darauf, vier Leerzeichen anstelle von Registerkarten zu verwenden, wenn Sie zu diesem Projekt beitragen.

Abstand

Fügen Sie keine zusätzlichen Leerzeichen zwischen eckigen Klammern und 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);
}

Benennungskonventionen

Verwenden Sie PascalCase immer für Eigenschaften. Verwenden Sie camelCase für die meisten Felder, mit Ausnahme von und PascalCasestatic readonlyconst Feldern. 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 lowercase letter
private string MyField; // <- Starts with an uppercase letter

Sie sollten

public string MyProperty;
protected string MyProperty;
private static readonly string MyField;
private string myField;

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 muss protected verwendet werden.

  • Felder sollten immer private privat sein, mit Eigenschaftenzugriff public oder protected.

  • Verwenden Von Ausdruckskörpermembern und automatischen Eigenschaften nach Möglichkeit

Sie sollten auf keinen Fall

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

// property should have protected setter
public int MyVariable => 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.

Nicht empfohlen

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 sollten alle in ihren eigenen Dateien gespeichert werden.

Wenn die Klasse, Struktur oder Enumeration als privat eingestuft werden kann, ist es in Ordnung, in derselben Datei enthalten zu sein. Dadurch werden Kompilierungsprobleme mit Unity vermieden, und es wird sichergestellt, dass eine ordnungsgemäße Codestraktion auftritt. 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;
}

Initialisieren von Enumerationen

Um sicherzustellen, dass alle Enumerationen ordnungsgemäß ab 0 initialisiert werden, bietet Ihnen .NET eine übersichtliche Verknüpfung, um die Enumeration automatisch zu initialisieren, indem Sie einfach den ersten (Starter)-Wert hinzufügen. (z. B. Wert 1 = 0 Verbleibende Werte sind nicht erforderlich)

Sie sollten auf keinen Fall

public enum Value
{
    Value1, <- no initializer
    Value2,
    Value3
}

Sie sollten

public enum ValueType
{
    Value1 = 0,
    Value2,
    Value3
}

Order enums for appropriate extension

Es ist wichtig, dass, wenn eine Enumeration wahrscheinlich in Der Zukunft erweitert wird, um Standardwerte am Anfang der Enumeration zu sortieren, sichergestellt wird, dass die Enum-Indizes nicht von neuen Ergänzungen betroffen sind.

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 SDKs that are supported by the MRTK
/// Initially, this lists proposed SDKs, 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
}

Überprüfen der Enumerationsverwendung für Bitfelder

Wenn die Möglichkeit besteht, dass eine Enumeration mehrere Zustände als Wert erfordert, z. B. Händigkeit = Links & Rechts. Anschließend muss die Enumeration ordnungsgemäß 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 Handedness
{
    None = 0 << 0,
    Left = 1 << 0,
    Right = 1 << 1,
    Both = Left | Right
}

Hartcodierte Dateipfade

Gehen Sie beim Generieren von Zeichenfolgendateipfaden und insbesondere beim Schreiben hartcodierter Zeichenfolgenpfade wie folgt vor:

  1. Verwenden Sie nach Möglichkeit die APIs vonPath C#, zPath.Combine. B. oder Path.GetFullPath.
  2. Verwenden Sie / oder Path.DirectorySeparatorChar anstelle von \ oder \\.

Mit diesen Schritten wird sichergestellt, dass MRTK sowohl auf Windows- als auch auf Unix-basierten Systemen funktioniert.

Sie sollten auf keinen Fall

private const string FilePath = "MyPath\\to\\a\\file.txt";
private const string OtherFilePath = "MyPath\to\a\file.txt";

string filePath = myVarRootPath + myRelativePath;

Sie sollten

private const string FilePath = "MyPath/to/a/file.txt";
private const string OtherFilePath = "folder{Path.DirectorySeparatorChar}file.txt";

string filePath = Path.Combine(myVarRootPath,myRelativePath);

// Path.GetFullPath() will return the full length path of provided with correct system directory separators
string cleanedFilePath = Path.GetFullPath(unknownSourceFilePath);

Bewährte Methoden, einschließlich Unity-Empfehlungen

Einige der Zielplattformen dieses Projekts erfordern eine Berücksichtigung der Leistung. Vor diesem Hintergrund sollten Sie immer vorsichtig sein, wenn Sie Arbeitsspeicher in häufig genanntem Code in engen Updateschleifen oder Algorithmen zuweisen.

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. Dies erleichtert es, auf einen Blick zu erkennen, was die Eigenschaft unterstützt und dass das Feld per Skript änderbar ist.

Hinweis

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

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 => myValue;
    set => myValue = value;
}

/// <summary>
/// Getter/Setters not wrapping a value directly should contain documentation comments just as public functions would
/// </summary>
public float AbsMyValue
{
    get
    {
        if (MyValue < 0)
        {
            return -MyValue;
        }

        return MyValue
    }
}

Zwischenspeichern und Serialisieren von Werten im Szenen-/Prefab

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 Verweisen auf Materialien, nicht jedes Mal ".material" aufrufen

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

Hinweis

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. Dadurch wird verhindert, dass sie versuchen, im Editor oder auf nicht unterstützten Plattformen auszuführen. Dies entspricht UNITY_WSA && !UNITY_EDITOR und sollte zugunsten von verwendet werden.
  • Verwenden Sie UNITY_WSA, um UWP-spezifische Unity-APIs zu nutzen, z. B. den UnityEngine.XR.WSA-Namespace. Dies wird im Editor ausgeführt, wenn die Plattform auf UWP festgelegt ist, sowie in integrierten 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.

Plattform 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

„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). Wenn Sie es mit relativen Zeiten zu tun haben (d. h. dem Delta zwischen einigen letzten Aktualisierungen und jetzt), empfiehlt es sich, DateTime.UtcNow zu verwenden, um den Mehraufwand für die Konvertierung von Zeitzonen zu vermeiden.

PowerShell-Codierungskonventionen

Eine Teilmenge der MRTK-Codebasis verwendet PowerShell für die Pipelineinfrastruktur und verschiedene Skripts und Hilfsprogramme. Neuer PowerShell-Code sollte dem PoshCode-Stil folgen.

Weitere Informationen

C#-Codierungskonventionen von MSDN