共用方式為


程式代碼撰寫指導方針

本檔概述適用於 Unity 的世界鎖定工具的建議程式代碼撰寫指導方針。 這些建議大多遵循 MSDN 的建議標準。


腳本授權信息標頭

張貼至 World Locking Tools for Unity 的所有腳本都應該附加標準授權標頭,如下所示:

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

任何未提交授權標頭的腳本檔案都會遭到拒絕。

函式 / 方法摘要標頭

所有公用類別、結構、列舉、函式、屬性、張貼的欄位都應該描述為其用途和使用方式,如下所示:

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

此規則可確保針對所有類別、方法和屬性正確產生並傳播檔。

任何未提交適當摘要標記的腳本檔案都會遭到拒絕。

命名空間規則

所有類別和延伸模組都應該依命名空間範圍,從下列命名空間中適當選擇。

Microsoft.MixedReality.WorldLocking.Core - 基礎程式代碼滿足世界鎖定工具的基本服務。

Microsoft.MixedReality.WorldLocking.Tools - 選擇性功能可補充世界鎖定工具的開發。 範例包括診斷視覺效果,以及應用程式事件處理程式的基準實作。

Microsoft.MixedReality.WorldLocking.Examples - 示範如何使用世界鎖定工具功能的特定實作,以及獲得的好處。

上述其中一個命名空間內的相關功能可藉由擴充至新的子命名空間來分組。

可行事項

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

省略介面、類別或數據類型的命名空間會導致您的變更遭到封鎖。

空格與索引標籤

參與此專案時,請務必使用四個空格,而不是索引標籤。

此外,請確定已針對條件式/迴圈函式新增空格,例如 if / while / for

禁止事項

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

可行事項

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

間隔

請勿在方括弧和括弧之間新增額外的空格:

禁止事項

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

可行事項

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

命名規範

一律用於 PascalCase 公用/受保護的/虛擬屬性,以及 camelCase 用於私人屬性和字段。

唯一的例外狀況是,需要由 JsonUtility串行化欄位的數據結構。

禁止事項

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

可行事項

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

存取修飾詞

一律宣告所有欄位、屬性和方法的存取修飾詞。

除非您需要在衍生類別中覆寫它們,否則所有 Unity API 方法都應該 private 預設為 。 在這裡情況下 protected ,應該使用 。

欄位應一律為 private,具有 publicprotected 屬性存取子。

禁止事項

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

可行事項

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

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

使用大括弧

請一律在每個語句區塊後面使用大括弧,並將其放在下一行。

禁止事項

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

禁止事項

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

可行事項

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

公用類別、結構及列舉應該全都位於自己的檔案中。

如果類別、結構或列舉可以設為私用,則可以包含在相同的檔案中。 此包含可避免 Unity 的編譯問題,並確保會發生適當的程式代碼抽象概念。 當程式代碼需要變更時,它也會減少衝突和中斷性變更。

禁止事項

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

可行事項

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

可行事項

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

為適當的擴充功能排序列舉。

如果 Enum 未來可能會擴充,則為排序列舉頂端的預設值非常重要。 此順序可確保列舉索引不會受到新增項目的影響。

禁止事項

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

可行事項

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

以 「Type」 結尾列舉名稱

列舉名稱應該使用 Type 後綴清楚指出其本質。

禁止事項

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

可行事項

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

檢閱 Bitfields 的列舉使用

如果列舉有可能要求多個狀態做為值,例如 Handedness = Left 和 Right。 然後,必須以 BitFlags 裝飾列舉,才能正確使用列舉

Handedness.cs檔案具有此實作的具體實作

禁止事項

public enum Handedness
{
    None,
    Left,
    Right
}

可行事項

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

最佳做法,包括 Unity 建議

此專案的某些目標平臺需要將效能納入考慮。 考慮到這一點,在經常呼叫的程式代碼中配置記憶體時,請務必小心,在緊密更新迴圈或演算法中配置記憶體。

封裝

如果需要從類別或結構外部存取字段,請一律使用私人欄位和公用屬性。 請務必共置私人欄位和公用屬性。 這個位置可讓您更輕鬆地查看屬性,以及腳本可修改欄位的內容。

如果您需要能夠在偵測器中編輯欄位,最佳做法是遵循封裝的規則,並將支援欄位串行化。

唯一的例外狀況是需要 串行 JsonUtility化 欄位的數據結構,其中數據類別必須具有所有公用欄位,才能讓串行化正常運作。

禁止事項

public float MyValue;

可行事項

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

可行事項

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

禁止事項

private float myValue1;
private float myValue2;

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

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

可行事項

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

盡可能使用 for 而非foreach

在某些情況下,需要 foreach,例如,在迴圈處理 IEnumerable 時。 但為了獲得效能優勢,請避免在您可以的時候使用 foreach。

禁止事項

foreach(var item in items)

可行事項

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

請快取值,並盡可能在場景/預製專案中加以串行化。

考慮到 HoloLens,最好將場景或預製中的效能和快取參考優化,以限制運行時間記憶體配置。

禁止事項

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

可行事項

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

快取對材質的參考,每次不要呼叫 “.material”。

Unity 會在每次使用 「.material」 時建立新的材質,如果未正確清除,則會導致記憶體流失。

禁止事項

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

可行事項

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

或者,使用 Unity 的 「SharedMaterial」 屬性,該屬性不會在每次參考時建立新的材質。

使用 平臺相依編譯 來確保工具組不會在另一個平臺上中斷組建

  • 使用 WINDOWS_UWP 來使用UWP特定的非 Unity API。 此定義可防止它們嘗試在編輯器中或在不支援的平台上執行。 此定義相當於 UNITY_WSA && !UNITY_EDITOR ,而且應該用於支援。
  • 使用 UNITY_WSA 來使用 UWP 特定的 Unity API,例如 UnityEngine.XR.WSA 命名空間。 當平台設定為UWP和內建UWP app時,這會在編輯器中執行。

此圖表可協助您根據使用案例和預期的組建設定,決定 #if 要使用的專案。

定義 UWP IL2CPP UWP .NET 編輯器
UNITY_EDITOR False False True
UNITY_WSA True True True
WINDOWS_UWP True True
UNITY_WSA && !UNITY_EDITOR True True
ENABLE_WINMD_SUPPORT True True False
NETFX_CORE False True False

偏好使用 DateTime.UtcNow over DateTime.Now

DateTime.UtcNow 比 DateTime.Now 更快。 在先前的效能調查中,我們發現使用 DateTime.Now 會增加顯著的額外負荷,尤其是在 Update() 迴圈中使用時。 其他人也遇到同樣的問題

除非您實際需要當地語系化的時間,否則偏好使用 DateTime.UtcNow (可能是您想要在使用者的時區中顯示目前時間的合法原因)。 如果您正在處理相對時間(也就是,某些上次更新與現在之間的差異),最好使用 DateTime.UtcNow 以避免執行時區轉換的額外負荷。