次の方法で共有


コーディングのガイドライン

このドキュメントでは、World Locking Tools for 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;
    }

このルールにより、ドキュメントが適切に生成され、すべてのクラス、メソッド、およびプロパティに対して配布されます。

適切な summary タグなしで送信されたスクリプト ファイルはすべて拒否されます。

名前空間の規則

すべてのクラスと拡張機能は、次の名前空間から適切に選択された名前空間によって範囲を選択する必要があります。

Microsoft.MixedReality.WorldLocking.Core - World Locking Tools の基本サービスを満たす基盤コード。

Microsoft.MixedReality.WorldLocking.Tools - World LockingTools に加えて開発を補足するオプション機能。 例としては、診断の視覚化や、アプリケーション イベント ハンドラーのベースライン実装などがあります。

Microsoft.MixedReality.WorldLocking.Examples - World Locking Tools 機能の使用方法と、得られるメリットを示す特定の実装。

上記にある名前空間のいずれかに含まれる関連機能は、新しいサブ名前空間に拡張することでグループ化できます。

実行

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

インターフェイス、クラス、データ型の名前空間を省略すると、変更がブロックされる原因となります。

スペースとタブ

このプロジェクトの共同作成者としてコードを提供する際は、タブではなく 4 つのスペースを使用してください。

また、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 とし、プロパティ アクセサーは public または protected とする必要があります。

次のことは行わないでください

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

将来の拡張に備えて列挙値の順序を考慮する。

列挙型が将来拡張される可能性がある場合は、既定値を列挙型の先頭に置くことが極めて重要です。 そうすれば、新たに値が追加されても、列挙型のインデックスが影響を受けることはありません。

次のことは行わないでください

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
}

ビットフィールドの列挙型の使用を確認する

列挙型が値として複数の状態を必要とする可能性がある場合 (たとえば、Handedness = Left & Right)。 次に、列挙型を正しく使用できるようビットフラグで正しく修飾する必要があります。

その具体的な実装を含んだ 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 }
}

可能な場合は foreach の代わりに for を使用します

場合によっては、たとえば、IEnumerable をループするときに foreach が必要になります。 ただし、パフォーマンスを向上させるために、可能な場合は 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" プロパティを使用してください。このプロパティであれば、参照するたびに新しい素材が作成されることはありません。

他のプラットフォームでのビルドに支障が生じないよう、プラットフォーム依存コンパイルを使用する

  • Unity のものではない UWP 固有の API を使用するには、WINDOWS_UWP を使用します。 この定義により、Editor やサポート対象外のプラットフォームでの実行を未然に防ぐことができます。 この定義は UNITY_WSA && !UNITY_EDITOR に相当し、優先的に使用する必要があります。
  • UWP 固有の Unity API を使用するには、UNITY_WSA を使用します (UnityEngine.XR.WSA 名前空間など)。 これは Editor (プラットフォームが UWP に設定されている場合) およびビルドされた UWP アプリで動作します。

この表を参考に、実際のユース ケースと想定するビルド設定に応じて、使用すべき #if を判断してください。

定義 UWP IL2CPP UWP .NET [エディター]
UNITY_EDITOR False
UNITY_WSA True True True
WINDOWS_UWP True True
UNITY_WSA && !UNITY_EDITOR True
ENABLE_WINMD_SUPPORT True False
NETFX_CORE True False

DateTime.Now よりも DateTime.UtcNow を優先する

DateTime.UtcNow の方が DateTime.Now よりも高速です。 以前実施したパフォーマンス調査の結果、DateTime.Now を使用すると、特に Update() ループで使用した場合に、著しいオーバーヘッドが生じることがわかりました。 同じ問題は、他でも報告されています

ローカライズされた時刻が実際に必要な場合 (現在時刻をユーザーのタイム ゾーンで表示したい場合など) 以外は、DateTime.UtcNow の方を使用してください。 相対的な時間 (最終更新時刻と現在時刻の差分) を扱う場合には、タイムゾーン変換のオーバーヘッドを回避するために、DateTime.UtcNow を使用するのが最良の選択肢です。