i-school - インターフェースを利用したエントリーポイントの実装例

エントリーポイント


 Start メソッドや Awake メソッドが複数のクラスに含まれている場合、それらは Unity を実行した際に順番に処理されていきますが、
どのクラスを優先して順番で処理していくかは Unity 次第であり、こちらの意図している処理の順番にはなりません。
そのため、処理して欲しい順番が前後してしまった際に、不具合が生じることがあります。
(A のクラスの Start メソッドの処理が先に解決される場合、B のクラスの Start メソッドにある処理が正常に動作しない、というケースが発生します。)

 また、処理の順番が順不同であるため、複数のクラス内に Start メソッドが存在する場合、どのクラスの Start が成功し、どのクラスの Start が動作していないのか、
それを確かめる方法が困難です。そのため、すべての Start メソッド内に Debug.Log メソッドを配置するといった対応が必要にもなります。



 こういったケースを回避し、かつ、プログラムを記述するエンジニアの意図(処理の順番)を反映した処理の流れを作り出す方法の1つとして、
エントリーポイントという考え方と、それを利用した設計方法があります。

 エントリーポイントでは、Start メソッドや Awake メソッドを1つのクラスのみに限定し、
その Start メソッドや Awake メソッドから処理を行いたい順番に各クラスのメソッドを実行していくという手法が実装出来ます。

 意図している順番通りに処理が動くことが保証されることにより、複数のクラスが動き出す順番をエンジニア側で制御することが可能になりますので、
動いている処理と動いていない処理が明確になります。処理を追いかけやすいということは、エラーの特定もしやすい、ということにつながります。

 こういったときに役立つのがインターフェースの機能です。


インターフェース


 インターフェースについては、色々なサイトに解説がありますので、そちらも参考にしてください。

 プログラム内に「共通のルールを作り、同じ動作(メソッド)を実行し、内部の振る舞いを変える」という考えです。
そのため、インターフェースではメソッド(およびプロパティ)の定義のみが出来、実際のメソッドの内容は実装したクラスに任されます。

 現実世界にも多くの分野でインターフェースの概念が利用されています。
コンセントの差し込み口と電化製品のコンセント、Nintendo Switch 本体とソフトなどの関係性もインターフェースの考え方です。

MicroSoft
interface (C# リファレンス)
未確認飛行 C様
インターフェース
サムライエンジニアブログ様
【C#】インターフェースって何?基礎からしっかり解説してみた!


実装例


IEntryRun インターフェースの作成


 IEnrtyRun はインターフェースですので、他のクラスに実装(一緒に利用)するための機能です。

IEntryRun.cs

<= クリックすると開きます。


 インターフェースは抽象的な概念であるため、それ単体では利用することは出来ません
必ず他のクラスに実装する形で利用します。これは抽象クラスと同様のルールです。

 また MonoBehaviour クラスを継承していませんので、このインターフェース単体をゲームオブジェクトにアタッチすることは出来ません
インターフェースを実装したクラスが MonoBehaviour を継承している場合には、そのクラスをゲームオブジェクトにアタッチすることが出来ます。

 その場合、インスペクターから直接確認は出来ませんが、インターフェースの機能も合わせて実装されます。
これはクラス継承と同じ考え方です。


SerializableInterface の導入


 今回の実装例では、インターフェースの実装しているクラスがアタッチされているゲームオブジェクトを、インスペクターからアサインして利用します。
インターフェースは Serialize を行ってもインスペクターには表示されません。
こちらの無料のアセットを導入してください。Serialize したインターフェースがインスペクターに表示されるようになります。

 あるいはアセットストアにて有料のアセットである Odin Inspector and Serializer をご利用ください。


<SerializableInterface>
https://github.com/Thundernerd/Unity3D-Serializabl...


Odin Inspector and Serializer








EntryPoint の作成


 上記の SerializableInterface を利用して、EntryPoint クラスを作成します。



EntryPoint.cs

<= クリックすると開きます。


 SerializableInterface によって定義されたインターフェースは、インターフェース名.Value と記述することで利用できます。
加えて今回は、Value の値が null ではないかを判定し、null ではない場合のみ EntryRun メソッドを実行するようにしています。


  entry.Value?.EntryRun();


IEnrtyRun インターフェースを実装したクラスを作成


 すでにクラスがある場合には、そちらに IEnrtyRun インターフェースを実装してください。

 今回は例として2つのクラスを用意していますので、こちらも活用ください。
指定した順番通りに処理が動いているかどうかを確認するため、複数のクラスに IEnrtyRun インターフェースを実装してデバッグする必要があるためです。


SoundManager.cs

<= クリックすると開きます。





UserData.cs

<= クリックすると開きます。

各ゲームオブジェクトを作成して、それぞれのクラスをアタッチする

EntryPoint ゲームオブジェクト


 EntryPoint クラスをアタッチするためのゲームオブジェクトを作成して、EntryPoint クラスをアタッチします。


ヒエラルキー画像



インスペクター画像



 EntryList の Size はまだ 0 で構いません。


IEntryRun インターフェースが実装されているクラスをアタッチしたゲームオブジェクトを複数作成する


 ここでは2つのゲームオブジェクトを作成し、それぞれに SoundManager クラスと UserData クラスをアタッチしています。
すでに自作しているクラスに IEntryRun インターフェースを実装している場合には、ここで新しく作成しなくても問題ありません。


ヒエラルキー画像



インスペクター画像



インスペクター画像


EntryPoint ゲームオブジェクトの EntryPoint クラスにある EntryList に、IEntryRun インターフェースを実装しているクラスがアタッチされているゲームオブジェクトをアサインする


 EntryPoint ゲームオブジェクトの EntryPoint クラスにある EntryList の Size を 0 から 2 に変更します。
この数は IEntryRun インターフェースを実装しているクラスの数に応じて変化させてください。(今回は SoundManager と UserData の2つだけなので 2 です。)
そちらに作成しておいた IEntryRun インターフェースを実装しているクラスがアタッチされているゲームオブジェクトをアサインします。

 このとき、アサインした順番に IEntryRun が実行されます。


インスペクター画像



ゲームを実行して、処理の順番を確認する


 Console ビューに、意図している処理の順番通りに Debug.Log メソッドの実行結果が表示されるかを確認します。



 以上になります。

 正常に動作していることがわかったら、EntryPoint クラスの EntryList の順番を変えたり、List 内の数を増やして挙動の変化を確認してみてください。