2021/09/02

Reactive ProgramingをUnityに活用した話

WRITER: q.q.vuong

Reactive ProgramingをUnityに活用した話


この記事のメイン対象:

  • UnityでObservable Patternを活用することに興味持っている人
  • 何らかのパターンを使って設計・実装してみたい人
  • クラスの責務や依存関係を整理したい人

Reactive Programmingとは

リアクティブプログラミング(Reactive Programming)はエンジニアにとって疎いことではありません。

簡単に解説すると、「非同期データストリームを使用したプログラミング」です。

 

Reactive Programingの主たることは:「Observable(観察可能な)シーケンスを使用して、非同期のイベントベースのプログラムを作成することです」

上記に記載したReactive Programmingの定義には、重要な言葉が1つあります – 「非同期」(Asynchronous)です。

データがストリームで非同期に発行されると通知されるため、メインプログラムフローとは独立しています。

データストリームを中心にプログラムを構成することで、非同期コードを記述します。

つまり、ストリームが新しいアイテムを発行したときに呼び出されるコードを記述します。

 

注意:

  • Reactive ProgrammingReactive Systemではありません。(Reactive Systemはこの記事で説明しないです)
  • Reactive Programingのコード書き方が明確で読みやすいですが、諸刃の剣です。例えば、色々ロジックを1つ関数に結合してSubscribeやZipやSelectなどをすると読解性がなくす可能性もあります。バグ発生した場合、Debug作業が非常にやり辛いです。なので、自分勝手に実装しないでください!Reactive Programingを利用する時、コメントを書いたり、説明したり、ダイアグラムを描いたりください。
  • Reactive Programing、基本はThread管理などが必要です、その管理ができないと不利なサイドエフェクト(side Effect)の痛みは測り知れないです。例えばdeadlock hunt, blockingなどの問題。

AsynchronousはMultithreadingではないです!!!!

多くの人は、マルチスレッドと非同期は同じものであると教えられていますが、そうではありません。

マルチスレッドは非同期の一種にすぎません

AsyncとMultithreadingの分別はサンプルで解説したほうが理解しやすいと思います。
例えば、あるレストランで「親子丼と味噌汁セット」の注文をするときでは:

  • Synchronous: 「親子丼」を料理してから、「味噌汁」を料理します。
  • Asynchronous – Single Thread (シェフが一人しかいない): 「親子丼」を料理して、完了するまでを待たずにタイマーをセットして、「味噌汁」を料理して、タイマーもセットする。タイマーが鳴るまで別の仕事をやります。上記の両方タスクが完了しましたら、「親子丼」と「味噌汁」を準備して、提供する。
  • Asynchronous – Multithread (シェフが二人以上): 一人が「親子丼」を準備する、もう一人が「味噌汁」を準備する。全部の料理が上がるまで待ちます。

下記の図を見ればもっと分かると思います。

引用:https://www.baeldung.com/cs/async-vs-multi-threading

結論:

  • マルチスレッドは非同期の一種にすぎません
  • マルチスレッドはワーカーに関するものであり、非同期はタスクに関するものです。

UniRXとMVPパターン

MVPとは

MVPパターンはAndroidアプリやWeb開発者にとって、とてもおなじみものですね。

MVPはModel-View-Presenterの頭字語です。

このパターンの紹介はネットに記事がいっぱいありまして、更に詳細な解説もあるので、本記事では紹介しません。

参照URLだけを記載しております。

MVPの基本解説を上記の記事から持っております。

M、V、Pの説明
MVPのMはModel、VはView、PはPresenterをそれぞれ表します。
各々、下記の図のような役割を持ちます。

引用:https://developers.cyberagent.co.jp/blog/archives/4262/

この設計では、PresenterがModelとViewを所持しています。
そのため、PresenterはModelとViewを知っているのですが、相互参照を避けるためにModelとViewはPresenterを知りません。

 

UniRXとは

UniRx (Reactive Extensions for Unity) は.NETの ReactiveExtensionsの再実装です

UniRxを使用すると、UnityでReactive Programmingを書くのがもっと簡単になります。

UniRxでReactive Programingを書くと、ロジックを分離できて依存関係が少なくなります。

こうすると、チーム内でテストや実装のタスクの分担を柔軟にできます。

では柔軟に分担するためのWorkflowを説明します。

今までは機能単位で分けて、エンジニア毎に実装タスクを持っています。

UniRXを使用した場合タスクをもっと分解することができます。

上記の図を見ると、2人で1つの機能を分担できることが分かると思います。

UIの反映はすべてUniRXを使って、UIの変更はデータの状態により反映される形で実装する。
Presenterは全然Viewの処理が知らなくても良い、Viewの関数だけよばれたら良い感じです。

例:(解説はコードに記載します)

 

結論:

  • システム開発では、依存関係をなくすことが非常に大事です。依存関係をなくすことができたら、メンテナンスコストやタスクアサインすることも楽になります。
  • Reactive ProgramingはUnityの新しいDOTS技術に活用ことができると思います。これから、ゲーム開発をOOP(オブジェクト指向プログラミング)以外、DOP(データ指向プログラミング)も身についたほうが良いと思います。

 

アバター

q.q.vuong

開発Gクライアントエンジニア
メイン:Unity
興味はゲーム内システム仕組みを設計する。
最近はNFT、Unity(VR) x Blockchainに興味があります。