Unityに関連する記事です

一定の時間が経過するとイベント準備が整うケース


 前回の続きになります。

 UniTaskとUniRxを使用して Final FantasyのATBゲージのような、一定時間が経過するとイベントの準備が整うシステムを実装する例です。
ここではボタンが押せるようにしています。


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


設計と概要


 今回は前回作成した1つのクラスを MVP パターンを利用して、3つのクラスを分割します。

 リファクタリングの学習にもなります。
 

元となるクラス


 前回の手順で作成しているクラスです。


ATBController.cs

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



MVP パターンでの実装


 ATBControllerクラスはMVP(MV(R)P)パターン3つのクラスに引き継ぐことができます。
MVPパターンでは、Model、View、Presenterの3つの役割に引き継ぐことで、クラスの責任を明確にし、それぞれの責任を分離しています。

 ・ATBModel:ATB値や回復速度などのデータを保持し、加工するクラス

 ・ATBView:ATBゲージや攻撃ボタンなどのUI表示、ユーザーからの入力を処理するクラス

 ・ATBPresenter:ATBModelとATBViewの仲介役として、データの取得や加工、UIの更新を行うクラス

 ATBModelはATBのデータとロジックを持ち、ATBPresenterはATBModelとATBViewの両方と通信し、ビューの更新を行います。
ATBViewはUIコンポーネントを持ち、ATBの状態をビューに表示します。



 具体的な役割分担は以下の通りです。

 ・ATBModel:ゲームシステムの進行に関する処理を担当し、ATBゲージの現在の値を更新したり、攻撃ボタンの状態を更新したりする

 ・ATBView:UIの表示・操作に関する処理を担当し、ATBゲージの表示を更新したり、攻撃ボタンが押された時の処理を実行する

 ・ATBPresenter:ModelとViewの橋渡しを担当し、ModelからViewにデータを渡したり、ViewからModelにイベントを伝えたりすることで、ModelとViewを疎結合に保つ



 上記で提示したように、ATBControllerクラスをMVPパターンで3つのクラスに分けることで、各クラスの役割を明確に分けて実装することができます。
また、MVPパターンを採用することで、各クラスを疎結合に保ち、柔軟な拡張性を持たせることができます。
その際には UniRxを使用してデータの流れを明確にし、コードの読みやすさや保守性を向上させています。

 これにより、ゲームシステムの進行とUIの表示・操作を分離し、柔軟に拡張できるようになります。

 それでは各クラスの実装例です。


ATBModel


 MonoBehaviour を継承した形での作成を行っています。
次の手順では MonoBehaviour を継承しない形での作成を行うため、ここでは分かりやすくクラス名を分けています。

 今回は接尾に Mono をつけることで、MonoBehaviour を継承している Model クラスであることを明示的にしています。


ATBModel_Mono.cs

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




 Model クラスで管理する変数の情報は、Presenter より受け取って利用する形式を用います。
つまり、初期値の設定が必要になる訳ですが、このときに Start メソッドを使ってしまうと引数がないため、この外部からもらった値を設定するという設計が実現できません。

 そこで Start メソッドの代わりに SetTriggerATB メソッドを作成し、こちらを Presenter 側から実行してもらうことで
Presenter から各変数の初期値を受け取って、その情報を活用するようにしています。
 
 初期値がなければゲームの進行が滞ってしまうため、SetTriggerATB メソッドの実行タイミングは
Presenter クラス内の Start メソッドか、それに準じる、ゲーム開始時に処理を行う部分で実行することになります。



 UpdateAsObservable メソッドを利用するには、using UniRx.Triggers; の宣言が必要です。

 UpdateAsObservable メソッドは MonoBehaviourに紐づいているため、AddToメソッドを使用する必要はありません

 MonoBehaviourがアクティブな間、Observableもアクティブであり、MonoBehaviourが終了した(ゲームオブジェクトが破棄された)ときには、
Observableも自動的に破棄されるためです。

 一方、Observable.EveryUpdateやObservable.Intervalなどの静的メソッドを使用する場合は、AddToメソッドを使用する必要があります。
これらのメソッドは、MonoBehaviourに紐づいていないため、Observableの寿命を管理する必要があります(自動的には破棄されない)
AddToメソッドを使用することで、Observableが終了したときにDisposeすることができ、メモリリークを防止することができます。


ATBView



ATBView.cs

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



 
 attackButton.OnClickAsObservable() は、ボタンがクリックされたときに実行される IObservable<Unit> イベントを作成します。

 その後、 public IObservable<Unit> OnAttackButtonClick => attackButton.OnClickAsObservable(); は、 OnAttackButtonClick プロパティを公開します。
このプロパティは、外部から attackButton のクリックイベントにサブスクライブするための IObservable<Unit> インターフェースを提供します。

 つまり、 OnAttackButtonClick プロパティを Subscribe することで、ボタンがクリックされたときに実行される処理を登録できますので、
ボタンのクリックによって実行される処理を、ATBView クラスではなく、外部クラスに書くことができます。


ATBPresenter



ATBPresenter.cs

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


 Subscribeメソッドを使用して、model.CurrentATBの値が変更されたときに呼び出されるコールバックを定義しています。コールバックは、atbという変数に現在のATB値が渡されます。そして、この値を使って、view.AtbGauge.fillAmountプロパティを更新しています。

view.AtbGauge.fillAmountは、0から1の範囲で値が期待されているため、atbをmodel.MaxATBで割ることで、0から1の範囲での値に変換しています。AddToメソッドは、ストリームを管理するためのもので、ここではviewにストリームを紐付けるために使用されています。

 画面上の UI の制御ですので、DOTween を活用してもよいでしょう。


model.CurrentATB
    .Subscribe(atb =>
    {
        var fillAmount = atb / model.MaxATB;
        view.AtbGauge.DOFillAmount(fillAmount, 0.01f).SetEase(Ease.Linear);
    })
    .AddTo(this);

 こちらの処理にすることでも、ゲージをアニメーションさせることが出来ます。


ATBController ゲームオブジェクトに各クラスをアタッチし、アサイン設定を行う


 アタッチする順番は任意です。

 View クラスにはゲージ用の Image と Button をアサインしてください。

 Presenter クラスの各変数には初期値が代入されるはずですので合わせて確認してください。
デバッグしやすいように値を調整してもよいでしょう。


インスペクター画像




実行して挙動を確認する


 Console ビューで Presenter の各メソッドに用意した Debug.Log メソッドが実行されるかを確認します。





 また、リファクタリングですので、ゲーム上の挙動は今までと同じ通りに動く前提です。
そちらも確認してください。

 ゲームを実行するときには、ATBController ゲームオブジェクトを選択しておき、インスペクターも確認してください。
ゲームの実行に合わせて、新しく2つのコンポーネントが自動的に追加されます。

 ObservableUpdateTrigger と ObservableDestroyTrigger の2つです。

 
インスペクター画像




 ObservableUpdateTrigger は Model クラス内に記述されている UpdateAsObservable メソッドによって自動的に追加されるコンポーネントです。
こちらにより、UpdateAsObservable が機能していると考えてください。


参考サイト
Qiita @toRisouP(株式会社 バーチャルキャスト) 様
UniRx入門 その4 -Updateをストリームに変換する方法とメリット-



 ObservableDestroyTrigger は Presenter クラス内で記述している各ストリームの最後にある AddTo メソッドと関連しています。
AddTo メソッドにより自動的に追加されるコンポーネントです。複数の AddTo がある場合でも1つだけ追加されます。

 この ObservableDestroyTrigger 内には OnDestroy() が実装されています。
ゲームオブジェクトの破棄に紐づいて OnDestroy() が実行され、その中で Dispose()が呼ばれることでストリームの破棄を行っています。


参考サイト
Qiita @su10 様
【Unity】破棄を忘れずに行うためにIDisposableをうまく利用する方法【UniRx】



 以上になります。

 最後は、各クラス内の処理における重点ポイントやリファクタリング手法です。

 次は イベント駆動型処理の作成例 です。

コメントをかく


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

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

Menu



技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

3D脱出ゲーム(抜粋)

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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