Unityに関連する記事です

 コルーチンのメソッドと同じ非同期処理である UniTask を利用した非同期処理の実装例です。



設計


 前回までの複数の非同期処理によるイベント処理について、インターフェースと UniTask を利用し、リファクタリングした場合の処理です。

 処理の内容は同じ、待機型で実行される非同期処理になりますが、UniTask を利用した方が処理をすっきりと記述できます。

 そのため、今までのコルーチンのメソッドの処理を残しつつ、UniTask の非同期処理に置き換えて学習します。

 合わせて抽象化のポイントの1つになるインターフェースについても学習します。


<UniTask をインポートする>

1.<UniTask とは?>


 C# において提供されている Task 機能を、より Unity に最適化し、より扱いやすくしたものが、UniTask になります。
UniTask は非同期処理に優れており、コルーチンによる処理の多くを async / await キーワードを使って非同期処理として記述できるようになります。

Chsharp 様
https://github.com/Cysharp/UniTask


 スクリプト内で UniTask の機能を利用する場合には、using Cysharp.Threading.Tasks; の宣言が必要になります。

 大きな違いとして、コルーチンや Invoke メソッドは MonoBehavior クラス内のメンバであるため、MonoBehavior を継承しないと実装が出来ませんが、UniTask と async / await にはその制約はありません。つまり、MonoBehavior を継承していない static クラスなどでも利用可能です。
 
 また、コルーチンの処理はゲームオブジェクトが破棄された段階で自動的に処理が破棄(キャンセル)されますが、
async / await で実装した非同期処理は自動的には停止しませんので、キャンセル処理を考えて実装する必要があります。


<参考サイト>
Qiita @toRisouP 様
UniTask機能紹介
https://qiita.com/toRisouP/items/4445b6b9bf00e49eb...
ソフトメディア研究会 すいま 様
UniTaskでレッツ非同期!
http://softmedia.sakura.ne.jp/advent-calendar/2019...



 UniTask はアセットストアにはありません。
Github サイトから UniTask 用の Unitypackage をダウンロードしてインポートを行うか、PackageManager 経由でインポートを行います。
 
 次は順番にそれらの手順を紹介します。
どちらの方法でインポートしても問題ありません。


2.Github サイトから Unitypackage をダウンロードしてインポートする場合

https://github.com/Cysharp/UniTask/releases

<Unitypackage を選択する>



 Unity を起動し、ダウンロードした Unitypackage を Unity 内の Assets フォルダにドラッグアンドドロップしてください。
インポート用のポップアップウインドウが開きますので、Import を押してください。自動的にインポートが始まります。


3.PackageManager 経由でインポートする場合


 Unity 内で PackageManager ウインドウを開き、左上の + アイコンを押して、Add package from git URL... を選択します。





 Github の URL を入力するポップアップが出ますので、そちらに、下記の URL をコピーして貼り付け、Add ボタンを押します。

<UniTask の GitURL>
 https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask


<上記の URL をコピーして貼り付けてから Add ボタンを押す>



 以上の手順を行うと、自動的に UniTask のインポートが始まります。








4.<async キーワードと await キーワードを利用した非同期処理>


 このキーワード群は C# の Task クラス、および UniTask クラスに紐づいた構文です。
よって Task クラスか UniTask クラスと一緒に宣言を行う必要があります。

 async(エイシンク) は Asynchoronous(エイシンクロナス) という単語の略語で「非同期」という意味です。
これは await を利用するメソッドを記述するために、メソッドの宣言時につける必要があるキーワードです。
その際、戻り値には UniTask を利用します。

 なお、メソッド名は、非同期処理が可能であることを示すように、メソッド名の最後に 〜 Async とつけると分かりやすくなります。

<async キーワード>
private async UniTask HogeAsync() {

}



 await(アウェイト) キーワードは「待ち受ける」という意味です。
このキーワードは、非同期処理として実行するメソッドの先頭に記述します。

<await キーワード>
private async UniTask HogeAsync() {
    await HogeHogeAsync();
}



 この2つのキーワードを利用し、戻り値の型を UniTask にすることにより、コルーチン処理と同じように非同期処理を実行し、その処理の解決を待ち受ける機能(メインスレッドに処理が戻ってくるのを待つ)を実装することが出来ます。
またコルーチンメソッド内で記述が可能であった yield return null や WaitUntil なども処理も UniTask には static メソッドとして用意されていますので、async / await キーワードを使うことで、コルーチンメソッドを置き換えることが出来ます。

 またコルーチンでは直接指定できなかった戻り値も UniTask<T> として記述できるようになっています。
タプル型を利用することで複数の戻り値を持たせることも可能です。

 なお UniTask には void に相当する UniTaskVoid 型があります。こちらの型にすることで処理を待ち受けない(await できない)メソッドとして機能させることも出来ます。
処理の完了を通知する必要がないメソッドで利用するため、この機能は UniTask を使った非同期処理の最後に利用されることになります。(非同期処理を最初に処理し始める場所、とも言えます)

<参考サイト>
MicroSoft
Async
Qiita @4_mio_11 様
非同期理解のためにasync/awaitとTaskの基礎を学んだ話


インターフェースの機能と定義


 インターフェースは、クラスに対して特定のメソッドやプロパティを実装することを要求することができるものです。
これにより、複数のクラスで同じ動作をすることを強制することができます。

参考サイト
++C++; // 未確認飛行 C 様
インターフェース



 実装の方法は、インターフェースを定義するための「interface」キーワードを使用します。例えば、次のようなインターフェースを定義できます。


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




 このインターフェースを実装するクラスは、「IMoveable」を実装して、「Move」メソッドを実装することが必要になります。


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




 インターフェースの利点としては、次のようなものがあります。

 ・複数のクラスで同じ動作を強制することができる。
 ・インターフェースを継承したクラスに対して同じように操作することができる。
 ・コードの保守性を高めることができる。
 ・仮想メソッドよりも軽量である。

 これらの利点により、よりスマートなコードを書くことができます。


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


 インターフェースは通常の C# スクリプトから作成できます。
インターフェースの名前には命名規則があり、先頭に「I(大文字のアイ。インターフェースの頭文字)」をつけて作成します。

 いつもは public class と書いてある部分の class を interface に変えます。
これでこのスクリプトファイルは、クラスではなく、インターフェースとして機能するようになります。



 UniTask を利用する場合、非同期処理用のメソッドをインターフェース内に定義しておくことが可能です。
コルーチンのメソッドは MonoBehaviour クラスを継承していないと定義できないため、インターフェース内には定義出来ません。

 非同期処理を行うメソッドは、非同期であることを示すため、メソッド名の最後には 〜 Async と記述するようにすることを推奨します。


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


 メソッドの書式が見慣れないかもしれませんが、通常のメソッドと同様です。
アクセス修飾子(public) 戻り値の型(UniTask<bool>) メソッド名(ExecuteEventAsync) 引数(CancellationToken token) という並びです。


親クラス


 作成済の親クラスである GameEventBase クラスに IGameEvent インターフェースを実装し、
IGameEvent インターフェース内で定義している ExecuteEventAsync メソッドを実装します。

 インターフェースを実装した場合、そのインターフェース内に定義しているメソッドを実装していないとエラーが出ます
なぜならばインターフェースにおけるメソッドはいわば仮想状態であり、インターフェース内ではメソッドの内容を記述することが出来ません
あくまでもメソッドの定義を作っているだけであり、具体的に記述されている処理がありません。

 そのため、クラスの継承で利用できる仮想メソッドと異なり、インターフェースではメソッドの実装(中身を書く)を強制する力があります。
この強制力がインターフェースの特徴です。

 またメソッドの実装にあたっては、virtaul キーワードを任意で設定できます。つまり、再度、仮想メソッドとして処理の抽象化をすることが出来ます。
今回は virtual キーワードを設定していますので、この親クラスを継承した子クラス内でオーバーライドして利用できます。


GameEventBase.cs

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



<async / await と UniTask を利用した非同期処理のポイント>


 非同期処理用のメソッドであることを示すため、async キーワードを追加しています。
またメソッド名の最後に命名規則に則って 〜 Async と記述しています。

 戻り値の型は UniTask 型です。値を返さない場合には UniTask のみを記述し、メソッド内の値を戻したい場合には UniTask<T> と記述します。
T の部分はジェネリックですので、任意の型を記述できます。今回は UniTask<bool> ですので、戻り値の型は bool 型です。
メソッド内に return の記述をすることで bool 型の値を命令元に戻すことが出来ます。



 コルーチンの非同期処理と異なり、async / await による非同期処理はゲームオブジェクトが破棄されても処理が停止しません
そのためゲームオブジェクトの破棄のタイミングに合わせてキャンセルする処理を自前で実装する必要があります。

 キャンセル機能を利用するためには CancellationToken 型の値が必要になりますので、
引数を利用して命令元から CancellationToken 型の値を取得し、このメソッド内で実行される await 処理に対してキャンセル処理を記述できるようにしています。
 
 現時点では await UniTask.Yield(token); の部分で利用されています。


インターフェースをインスペクターに表示させるため Serializable Interface アセットをインポートする


 インターフェースをデータ型として変数宣言した場合、 SerializeField 属性を付与していてもインスペクターには表示されません
これはインターフェースそのものの仕様です。
 
 次回の手順ではインターフェースをインスペクターに登録したりしたいので、インスペクターに表示させる機能を追加します。



 無料アセットである Serializable Interface アセットを Unity にインポートし、そちらの機能を利用することにより
インターフェースをデータ型として作成した変数をインスペクターに表示させることが出来ます。

 下記の Github 内のページからダウンロードして Unity にインポートしてください。

Github
Thundernerd/Unity3D-SerializableInterface

https://github.com/Thundernerd/Unity3D-Serializabl...


 インポートされるとフォルダが追加されます。


Propject フォルダ内



参考サイト
コガネブログ 様
【Unity】MonoBehaviour の参照を Interface で Inspector に設定できる「Serializable Interface」紹介



 以上でこの手順は終了です。
引き続き次の手順にて処理を完成させます。

 次の手順は 処理の抽象化による実装例 です。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

3Dトップビューアクション(白猫風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます