Unity
HoloLens
VisualStudio2017
WinMR

Mixed Reality Toolkit - Unityが提供するSingtonパターンに使える機能について

インスタンスを1つにしたいときにMRTKは2種類ある。

SingletonパターンはGoFデザインパターンの1つで、何かとよく使います。特に共通系の機能やパラメータを扱う場合によく使うテクニックの1つですが、Mixed Reality Toolkit - Unityの中にもいくつか便利なクラスが提供されています。今回はたまたまよく似た2つの部品を見つけたのでそれぞれの違いについて整理してみました。

今回の開発環境

  • Unity 2017.2.1p2
  • Windows 10 Fall Creators Update
  • Mixed Reality Toolkit - unity 2017.2.1.1
  • Visual Studio 2017

MRTKで提供されるSingletonパターンに当てはまるもの

MTRKでは2つの機能が提供されています。

  • Singleton
  • SingleInstance

それぞれオブジェクトを一意に扱うための機能ですが範囲が違います。

Singleton

あるコンポーネントを一意で扱うときに継承します。このクラスを継承されたオブジェクトはInstanceプロパティを使ってどこからでもクラスにアクセスすることができます。なお、Singletonクラスを使用するとDontDestroyOnLoad
でインスタンス化される形になります。

実装例と使い方

実装方法はいたって簡単です。Unityの[Project]タブ内で[Create]-[C# script]を選択して新しいスクリプトを追加します。
次のMonoBehaviorの代わりにSingleton<T>を継承します。Tには今回作成したクラスを設定します。

public class Foo : Singleton<Foo>
{
  public string Bar()
  {
     return "Samples";
  }
}

Fooクラスを使う場合、Foo.Instanceでインスタンスにアクセスします。

 var data = Foo.Instance.Bar();

使用時の注意事項

この機能を利用する際には実装時にいくつか注意が必要です。

1. 継承クラスでAwake/OnDestroyを利用する場合はOverrideで実装

SingletonクラスはAwakeメソッドの中でオブジェクトをインスタンス化します。その後Instanceプロパティでアクセスできます。また、インスタンスが破棄されるとOnDestroyメソッドで後処理を実施します。
このため、Awake/OnDestroyを使う場合はオーバーライドで上位クラスのAwake/OnDestroyを呼び出すように実装してください。

public class Foo : Singleton<Foo>
{

 public override void Awake()
 {

  base.Awake();
  //....
 }

 public override void OnDestroy()
 {

  base.OnDestroy();
  //....
 }


  public string Bar()
  {
     return "Samples";
  }
}
2.このクラスを使用する場合はStartメソッド以降で

SingletonクラスはAwakeで初期化を行ってます。このため、Unityでのオブジェクトのライフサイクルを考慮すると他のコンポーネントのAwakeで呼び出した場合、Singletonクラスの初期化が完了していない場合がありえます。このため、インスタンスのアクセスはStartメソッドからにした方がよいと思います。

SingleInstance

あるコンポーネントを一意で扱うときに継承します。このクラスを継承されたオブジェクトはInstanceプロパティを使ってどこからでもクラスにアクセスすることができます。Singletonクラスとは異なり、こちらはDontDestroyOnLoadを使っていません。

実装例と使い方

実装方法はいたって簡単です。Unityの[Project]タブ内で[Create]-[C# script]を選択して新しいスクリプトを追加します。
次のMonoBehaviorの代わりにSingleInstance<T>を継承します。Tには今回作成したクラスを設定します。

public class Foo : SingleInstance<Foo>
{
  public string Bar()
  {
     return "Samples";
  }
}

Fooクラスを使う場合、Foo.Instanceでインスタンスにアクセスします。

 var data = Foo.Instance.Bar();

使用時の注意事項

この機能を利用する際には実装時にいくつか注意が必要です。

1. 継承クラスでOnDestroyを利用する場合はOverrideで実装

SingleInstanceクラスはインスタンスが破棄されるとOnDestroyメソッドで後処理を実施します。このため、OnDestroyを使う場合はオーバーライドで上位クラスのOnDestroyを呼び出すように実装してください。

public class Foo : SingleInstance<Foo>
{
 public override void OnDestroy()
 {

  base.OnDestroy();
  //....
 }

  public string Bar()
  {
     return "Samples";
  }
}
2.このクラスを使用する場合はシーン内にこのコンポーネントを複数設置してはいけない。

SingleInstanceクラスから一意のインスタンスを取得するにはInstanceプロパティを利用します。このInstanceプロパティは、FindObjectsOfTypeメソッドを用いてインスタンスを検索します。そして、この検索結果が1インスタンスの場合のみ値を返します。例えばFooコンポーネントを複数のGameObjectに登録し、Instanceプロパティを使用すると、正しいインスタンスが取得できないかnullとなります。
このため、SingleInstanceクラスはシーンの中に1つだけ設定するようにしてください。

両方の使い分けについて

Singletonパターンでオブジェクトを作る場合1つだけ用意するという点では同じですが、スコープの範囲がことなります。
Singleton<T>クラスは、シーンを横断して一意にする場合に利用します。そこまで広いスコープが必要なくシーン内で一意であればよい場合はSingleInstanceクラスで対応します。