i-school - Channel の活用例
 抽象化設計を推し進めるため、UniTask に用意されている Channel 機能を活用する事例です。

 アイテムなどの情報が一覧表示されているポップアップがあり、
その中にあるアイテムを選択すると確認ポップアップや、詳細説明のポップアップが開くような場合の実装例です。









設計とメリット


 一覧表示用のポップアップ内で、任意のボタンをタップした際、その上に確認用のポップアップが開く構造です。
このとき、確認用のポップアップには、選択したボタンが有している情報(アイテムの名前、価格、性能など)が表示されます。

 そのため挙動としては、一覧表示用のポップアップで選んだ内容が、確認用のポップアップに反映され、
「キャンセル」か「決定」のいずれかを押した場合に確認用のポップアップが閉じます。
「決定」を選択した場合に限り、一覧表示用のポップアップ内でロジックが動く作りです。

 そのため、確認用ポップアップ内の決定ボタン自体は、それ自身が何を決定しているかは認知していません
一覧表示用のポップアップから指示されてその都度確認ポップアップは開きますが、その結果(キャンセルか、決定かのみ)をフィードバックするという構造です。
つまり、ボタン本来が持っている抽象的な構造を有している形のポップアップになります。

 この抽象的な構造を Channel の機能を応用することで表現できます。


サンプルコード


 サンプルコードは外部ライブラリとして UniRx、UniTask、DOTween を利用しています。
また処理内部ではデリゲートの機能を利用しています。

 またアイテムのデータ管理クラスなどは用意していませんので、適宜、スクリプタブル・オブジェクトなどを活用してデータを用意してください。


生成されるアイテムなどのボタンにアタッチされているクラス


 アイテム用のボタンにアタッチされて、アイテムの画像、データなどを管理します。
他にも任意の情報を追加してください。

 Setup メソッドの Action のメソッドを外部クラスで設定することにより、
アイテムの挙動を外部クラスで指定できる。例えば、アイテムを使うメソッドを紐づければアイテムを利用するボタンになり、
アイテムを捨てるメソッドを紐づければ、アイテムを捨てるボタンになります。

 また今回はアイテムの名称と Index を Action で渡していますが、
アイテムのクラス全体を渡すようにすれば、引数1つで渡すことも可能になります。

 自分のプロジェクトに合わせて調整してください。




ポップアップのアニメ設定用クラス


 作成後、スクリプタブル・オブジェクトを作成して必要な設定を行ってください。




 作成したスクリプタブル・オブジェクトの参考画像です。





ポップアップ用の親クラス


 popupAnimSettings 変数には先ほど作成した PopupAnimSettings スクリプタブル・オブジェクトをアサインします。

 参考までにコルーチンの処理もコメントアウトして残してあります。




確認用のポップアップ


 選択したオブジェクトの用途に合わせた確認用の処理を行います。
例えばアイテムを選択したのであれば、それを利用する確認時、廃棄の確認時、など確認が必要な場面に利用します。

 ポイントは、このポップアップ内のボタンは、「可否」を通知する機能のみを有している状態にとどめていることです。
これにより、その先の処理である「アイテムを使ったときの処理」や「破棄する処理」など、ボタンがつながる処理は知らない状態です。

 Channel を使うことで多様な入力方式に備えつつ、どのような方式でも、どのような結果でも一括して await confirmationChannel.Reader.ReadAsync(); で待機できるようにしています。



一覧表示用のポップアップ


 アイテムなどのインベントリや、ショップなどのオブジェクトを一覧表示用のポップアップの作成例です。

 ポップアップ内において、それらのボタンオブジェクトを生成し、デリゲートを利用して実行したいメソッドをボタン側に提供しています。
この設計により、ボタン側はこの一覧表示用のポップアップを知る必要はなく、押下した際に一覧表示用ポップアップ内に用意したメソッドを実行できます。

 UniTask を活用することで、確認用ポップアップ内のボタンの押下処理の結果を取得できるまで待機し、
その結果を受けて処理の分岐や、選択したボタンに応じた処理につなげていく処理を書きやすくしています。




処理の説明


 アイテムなどの選択用のボタンを4つ(実際には複数個)生成し、それにデリゲートとして SelectItem メソッドを渡すことで、ボタン側と一覧表示用ポップアップ側の直接の依存関係を断っています。
その後、選択のボタンを押すと SelectItem メソッドが動き、併せて、選択されたボタンの情報が渡ってきます。

 ここで確認用ポップアップを開き、ポップアップ内にある「決定」「キャンセル」のいずれかのボタンが押されるまで待機します。
ほかの処理が止まるので、ゲーム内の処理を止め、ボタンの選択を待てる状況で使う前提です。

 SetupConfirmPopup メソッドでボタン自体の役割を設定しつつ、その結果が何に使われるかは認知せずに一覧表示用ポップアップ側に押したボタンの状態(決定かキャンセルか)フィードバックします。

ConfirmPopup.cs 64 行目
  // ボタンの押下結果を待機(Channel のキューに値が入っていたら1つ取り出すまで待機) 
  bool result = await confirmationChannel.Reader.ReadAsync();

 この ReadAsync を使って Channel に書き込むタイミングを非同期で待っているのですが、
今回の場合、ボタン押下時に書きこみをリンクさせているので、ボタンを押すまで待機する、という挙動になっています。