i-school - 2D放置ゲーム 発展1
 ここからは中級者向けの教材になります。

 放置ゲームの教材をベースに処理の発展・応用を行っていきます。
難しいと感じたら一旦終了して、復習をしっかりと行ってから進めるようにしましょう。

 教材を進めることが目的ではありません。教材を通じて学習し、理解を深め、ロジックを考えられるようにすることが目的です
エラーを出ないように進めれば教材を終了することはできますが、それでは知識や技術は備わりません
わかったつもりでは先々に自分ひとりで処理を考える際に詰まってしまいますので、是非、自分の力を養うためのツールとして上手に活用をしてください

 以下の内容で順番に実装を進めていきます。

発展1 −UniRX (ReactivePropery)を利用した UI 表示更新機能の実装−
1.事前準備と UniRX のインポート
2.UniRX とは?
3.UI 表示の更新処理を UniRX の機能である ReactivePropery を利用した処理に変更する



 新しい学習内容は、以下の通りです。

 ・ReactivePropery<T>



1.事前準備と UniRX のインポート

1.事前準備


 まずはゲーム画面に獲得しているポイント(GameData クラスの totalRewardPoint 変数)を表示させる UI 機能と、
ポイントを獲得するごとに画面のポイント表示が更新される処理を作成してください。

 スクリプトなどの設計は任意です。

 そちらを改修して学習を進めていくことになりますので、まずはこちらの製作をしてください。


<実装動画>
動画ファイルへのリンク



2.UniRX をインポートする


 今回はポイント用の UI 表示の更新に UniRX(ユニアールエックス)という無償で提供されているライブラリを利用します。

 Unity のアセットストアの検索にて UniRX と入力すると検索結果として表示されますので、そちらをダウンロードして Unity にインポートします。
あるいは下記のリンクから直接参照も出来ます。
UniRX
https://assetstore.unity.com/packages/tools/integr...


アセットストア




2.UniRX とは?

1.UniRX


 UniRX とはリアクティブプログラミングという考え方を Unity で利用できるように設計されているライブラリです。
非同期処理イベント処理に対して効率的な実装方法を提供する構造になっています。

 便利かつ、高度な処理を実現することができますが、学習コストも非常に高いものになっています。
また学習したからといって、なんでもこの構造にすればいい、というものでもありません

 今回はその中でも比較的扱いやすく、利用する機会の多い ReactivePropery(リアクティブプロパティ)という機能について、
数回に分けて実装を行いながら学習を行っていきます。

 C# の基礎だけではなく、Linq、ラムダ式(および、メソッドチェーン)といった機能も理解していないと扱うことが難しいため、
これらの機能についてまだ理解が不十分である場合には、そちらを学習してから進めるようにしてください。

 すでに実装されている機能を ReactivePropery を利用した設計内容にリファクタリングを行う内容になっていますので、
現在の放置ゲームのスクリプトの処理の内容をしっかり把握できていないと、どこをどのように修正しているのかも難しい内容です。

 UniRX および ReactivePropery については記事も多いですし、参考となる書籍もありますので、そちらも合わせて学習を行うようにしてください。


2.オブザーバーデザインパターンを利用した設計


 UniRX はデザインパターンの1つである、オブザーバーデザインパターンというもので設計されています。

 具体的には、イベント(ボタンなど)を実行した際の処理を、通知する側通知を受け取る側、という概念を利用して実装を行う設計になります。

 以下の記事は非常にわかりやすくまとめられています。こちらを読み込んで、理解を深めてください。
Qiita @toRisouP 様
UniRx入門 その1
https://qiita.com/toRisouP/items/2f1643e344c741dd9...



 では今回の実装方法は、どのように利用していくのか、どんな部分に利点があるのかを説明します。

 現在、獲得した褒賞のポイントをゲーム画面に配置した UI 表示を更新する場合には、以下のような手順の処理で実装していると思います。

 1.GameManager クラスがお使い達成時の褒賞ポイントを GameData クラスの totalRewardPoint 変数に加算する処理を実行する
       ↓
 2.totalRewardPoint 変数をゲーム画面に表示している Text クラスに対して命令を出して、ゲーム画面の表示を更新する

 このうち、2の処理をクラス間での処理内容に置き換えましょう。

 2−1.GameManager クラス (totalRewardPoint 変数をゲーム画面に表示している Text クラスに対して命令を出して)
       ↓
 2ー2.Text クラス、あるいは、Text クラスを管理している UIManger のような管理クラス  (ゲーム画面の表示を更新する)

 以上のことより、GameManager クラスが UI 管理用のクラスと依存関係を結んで、その情報を利用してメソッドを実行している流れです。

 オブザーバーパターンを利用した場合、処理は次のようになります。

 1.GameManager クラスがお使い達成時の褒賞ポイントを GameData クラスの totalRewardPoint 変数の代わりに ReactivePropery に値を加算する処理を実行する(処理の流れは同じ)
       ↓
 2.ReactivePropery の値を UI 管理用のクラスが監視していて、ReactivePropery の値が更新されるたびに、Text クラスに対して自動的に命令を出して、ゲーム画面の表示を更新する(違う部分)

 大きく異なるのは、2の部分です。

 ReactivePropery を利用することにより、この値を外部クラス(UI 管理クラス)から監視する処理を実装することができます。
この処理により、以前のように、値が更新されるたびに、GameManager クラスから表示更新の命令を受けることなく、自動的に UI の更新処理を行うようにする仕組みです。


3.UI 表示の更新処理を UniRX の機能である ReactivePropery を利用した処理に変更する

1.GameData スクリプトを修正する


 ここまでの説明をふまえて、GameData クラスにある totalRewardPoint 変数を、ReactivePropery の情報に変更します。

 UniRX をスクリプトで利用する場合には、他のライブラリと同じように using に宣言を追加します。

 totalRewardPoint 変数の代わりに ReactivePropery の宣言を追加して利用できるようにします。
totalRewardPoint 変数を利用している部分が3箇所ありますので、そちらを ReactivePropery に変更します。

 ReactivePropery はジェネリック型であるため、<T> の部分には任意の型を指定できます。
今回は totalRewardPoint 変数の代わりとなるため同じ int 型ですが、自作したクラス(例えば、EarnedReward クラス)などを指定することもできます。

 ReactivePropery はプロパティの一種ですので、Value プロパティを利用することで値を参照したり、更新することが出来ます。
 
 そして、ReactivePropery には「値が更新されると通知を行う」という機能が備わっています
そのため、ReactivePropery の値が 0 => 10 というように変化をすると、ReactivePropery から通知を受け取る(監視する)処理を施すことで
この処理に付随して、自動的にイベント処理を発生させることが出来ます。

 今回はこの GameData クラスに用意する ReactivePropery の値を、外部のクラスである UI 管理クラス側にて監視してもらうようにします。

 ReactivePropery 側が「通知を行う側 = 発行」といい、ReactivePropery の値の監視を行う側を「通知を受け取る側 = 購読」といいます。


GameData.cs

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


 スクリプトを修正したらセーブします。


2.UI 管理用のスクリプトを修正するか、UIManager スクリプトを作成する


 現在ポイントの画面更新を管理しているスクリプトを修正してください。
あるいは、新しく UIManager スクリプトを作成してください。

 Text クラスに褒賞ポイントの表示を行う処理について、GameData スクリプトに追加した ReactivePropery の値が変化するたびに
自動的に画面の表示を行うように ReactivePropery を監視する処理を追加します。そのため、UIManager クラスは監視をする側であり、通知を「購読」する側になります。

 監視(購読)の処理は、一度手続きすれば、あとは処理を実行しなくても、ReactivePropery の値が変化したタイミングに合わせて自動的に実行されるようになります。
そのため、Start メソッドにて1回だけ処理を行っています。

 自動的に実行される処理の内容は、Subscribe メソッドの引数内に指定を行います。この引数にはラムダ式で処理を登録します。
今回は、Text クラスの text プロパティの値を ReactivePropery の値に更新する処理を行うことにより、画面に表示されるポイントの値を更新しています。
この引数にはメソッドも登録できますし、このように処理を登録することもできます。


UIManager .cs

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


 スクリプトを作成したらセーブします。


3.<ReactivePropery<T> >


 ReactivePropery(リアクティブプロパティ)は、UniRX の持つ機能の1つです。
利用するには using UniRx; の宣言が必要になります。

  using UniRx;

  public ReactiveProperty<int> PointReactiveProperty = new ReactiveProperty<int>(0);



 ReactivePropery はジェネリック型であるため、<T> の部分には任意の型を指定できます。
今回は totalRewardPoint 変数の代わりとなるため同じ int 型ですが、自作したクラス(例えば、EarnedReward クラス)などを指定することもできます。

 ReactivePropery は Value プロパティを持っており、この中に ReactivePropery の情報が代入されています。
そのため、 Value プロパティを利用することで値を参照したり、代入処理をして更新することが出来ます。
 
 今回であれば、int 型の整数の情報、すなわち、totalRewardPoint 変数の代わりとなる値が代入されている部分が Value になります。

<Value プロパティ>
  PointReactiveProperty.Value += amount;


 
 もっとも大きな特徴として、ReactivePropery には「値が更新されると通知を行う」という機能が備わっています
そのため、ReactivePropery の値が 0 => 10 というように変化をすると、ReactivePropery から通知を受け取る(監視する)処理を施すことで
この処理に付随して、自動的にイベント処理を発生させることが出来ます。

 今回は GameData クラスに用意した ReactivePropery の値を、外部のクラスである UI 管理クラス側にて監視してもらうように設計しています。

 ReactivePropery 側が「通知を行う側 = 発行」といい、ReactivePropery の値の監視を行う側を「通知を受け取る側 = 購読」といいます。

<UIManager クラスが ReactivePropery の通知を受け取る(購読する)側>
 // ReactivePropery から通知を受け取る(購読)側の設定をする。ReactivePropery の値が更新されると、通知が届き、この Subscribe メソッドの内容が実行される
  GameData.instance.PointReactiveProperty.Subscribe(x => txtPoint.text = GameData.instance.PointReactiveProperty.Value.ToString());

 ReactivePropery からの通知を受け取るためには、通知を受け取りたい ReactivePropery に対して、Subscribe メソッドを実装します。
Subscribe メソッドは引数内に、処理の実装、あるいはメソッドの呼び出し処理を実装することにより、
ReactivePropery の値が更新されたら、Subscribe メソッドの引数に記述してある処理を自動的に毎回実行する」という処理が動きます。

 この機能によって、「ある状態になったときに、指定してある処理を実行する」というイベント処理を実装することが出来るようになっています。

 Subscribe メソッドはラムダ式で処理を記述します。引数を利用しない場合には、(_ => ) とアンダーバーを指定できます。
今回の Subscribe メソッドの処理は (x => txtPoint.text = GameData.instance.PointReactiveProperty.Value.ToString()) ですので、
ReactivePropery の値(現在の獲得ポイント)が変更になったら、その都度、txtPoint.text に GameData.instance.PointReactiveProperty.Value.ToString() の情報を代入するという処理が動いています。

 そのため今回の実装では、獲得ポイントを更新したあとに、画面の表示を更新するという処理がなくなっていますが、
この ReactivePropery の機能によって、いままでと同じ処理を自動的に動作するように機能をしています。ここが大きな違いになります。

 実装したプログラムを読み直してみてください。
画面の表示を更新する処理は、UIManager クラスの Start メソッドで1回実行されているだけであることが分かると思います。


4.ゲームを実行して動作を確認する


 今回行っている実装はリファクタリングですので、ゲームの挙動が変わってしまうことはありません。(変わってしまっていたら、それはリファクタリングになりません)
ゲームを実行し、いままで動いていたのと同じように、ポイントの加算に応じて画面の表示も更新されれば制御成功です。


<実装動画>
動画ファイルへのリンク
 

 ゲーム画面上はいままでと同じ挙動であるのに対し、プログラムの実装内容がいままでと違うことを意識して、処理を確認してみてください。

 Text クラスに対して、ポイント更新の都度、更新の命令を実行していた処理と異なり
この実装では ReactivePropery の値の更新に合わせて、ReactivePropery を監視している UIManager クラスの Text クラスが一緒に更新されていることが分かります。

 このように「値の更新に合わせてイベント処理を発生させる」という場合に、ReactivePropery は非常に便利な機能と言えます。