Open6

すいかゲームのクローンを設計する

0y00y0

趣旨

某ゲームのクローンを作ります。

0y00y0

Scriptsディレクトリの構成


オニオンアーキテクチャ+MVPパターンで設計しました

  • 各層でAssembly Definitionを定義しています
  • 下位層から上位層を参照することはできません
  • DIのためにZenjectを利用しています
  • MVPパターンのためにUniRxを利用しています
  • 非同期処理のためにUniTaskを利用しています

Domain層

どこにも依存しない(UniRxやUniTaskへの依存は許可)

  • Interfaces:Domain ServiceおよびInfrastructure層のインタフェースを定義
  • Services:Domain Serviceを実装
  • ValueObject:Domain Modelを実装
  • Entity:Domain Modelを実装(現状は無し)

UseCase層

Domain層を参照(インタフェース経由でInfrastructure層の実装を利用可能)

  • Interfaces:UseCaseのインタフェースを定義
  • UseCases:Domain Serviceやリポジトリを利用してUseCaseを実装

Infrastructure層

Domain層を参照(直接参照されることはない)

  • Repositories:Domain ModelやDomain Serviceを利用してリポジトリを実装
  • Services:外部APIなどを利用してServiceを実装(現状は無し)
  • SODefinitions:スクリプタブルオブジェクトを定義(後述)

Presentation層

Domain層およびUseCase層を参照

  • Interfaces:Viewのインタフェースを定義
  • Presenter:Presenterを実装(UseCase層とViewのバインド)
  • View:Viewを実装(MonoBehaviourの継承許可)
  • SODefinitions:スクリプタブルオブジェクトを定義(後述)

Installer層

DIのためすべての層を参照

  • DI設定を記述
0y00y0

シーン

各シーンは以下のような要素を持っています
※DIするMonoBehaviourなスクリプトおよびPrefabは実行するまでヒエラルキーに表示されません
※DIする非MonoBehaviourなスクリプトは実行してもヒエラルキーに表示されません

各シーン共通

プロジェクトコンテクスト(レイヤー”Default”)

実行時にDIされDontDestroyOnLoadになる

  • インストーラ
  • オーディオBGN
    • 常に再生
  • オーディオSE
    • ユーザインタラクションに応じて再生
  • インプットイベントハンドラ
    • キーボード入力を検知してストリームソースを生成

タイトルシーン

メインカメラ(レイヤー”Default”)

レイヤー”Default”のオブジェクトを映す

UIカメラ(レイヤー”Default”)

レイヤー”UI”のオブジェクトを映す

バックグラウンドページ(レイヤー”Default”)
  • 背景イメージ
タイトルページ(レイヤー”UI”)
  • ボタン×3
    • メインシーンに遷移
    • 詳細なスコアランキング表示
    • 音量設定パネル表示
  • 詳細スコアランキングパネル
    • ボタン×1
      • 前の画面に戻る
  • 設定パネル
    • スライダー×2
      • BGM音量
      • SE音量
    • ボタン×1
      • 前の画面に戻る
ローディングページ(レイヤー”UI”)

シーン遷移の際に表示される

  • ロード画面イメージ
インストーラ(レイヤー”Default”)
  • タイトルシーンインストーラ
シーンコンテクスト(レイヤー”Default”)
イベントシステム(レイヤー”Default”)

メインシーン

メインカメラ(レイヤー”Default”)

レイヤー”Default”のオブジェクトを映す

UIカメラ(レイヤー”Default”)

レイヤー”UI”のオブジェクトを映す

ステージ(レイヤー”Default”)
    • マージアイテムとの接触判定を持ち、あふれるとゲームオーバー条件を満たす
  • マージアイテム
    • マウスを動かしたときに移動
    • クリックしたときにマージアイテムを落下、SEを再生
    • 同じ種類のマージアイテムが接触するとスコア加算、SEを再生
バックグラウンドページ(レイヤー”Default”)
  • 背景イメージ
メインページ(レイヤー”UI”)
  • マージアイテムパネル表示(次回に選ばれるアイテム)
  • スコアパネル
  • スコアランキングパネル
    • 左右キーでカテゴリをスクロール
  • ポーズパネル(Escキーを押したとき)
    • ボタン×3
      • メインシーンに遷移
      • リスタート
      • 現在のゲームに戻る
  • ゲームオーバーパネル(ゲームオーバー条件を満たしたとき)
    • ゲームオーバー時のスクリーンショット表示
    • スコアランキング表示
    • ボタン×3
      • リスタート
      • 詳細なスコアランキングパネル表示
      • タイトルシーンに遷移
  • 詳細スコアランキングパネル
    • ボタン×1
      • 前の画面に戻る
ローディングページ(レイヤー”UI”)

シーン遷移の際に表示される

  • ロード画面イメージ
インストーラ(レイヤー”Default”)
  • メインシーンインストーラ
シーンコンテクスト(レイヤー”Default”)
イベントシステム(レイヤー”Default”)
0y00y0

ModelとViewのバインド

UniRxを用いたMV(R)Pパターンにより、PresenterがModelとViewをバインドします。
UseCase層に実装したクラスをModel、Presentation層のViewに実装したクラスをViewとしました。

  • UseCase層
    • IReadOnlyReactivePropertyを公開
  • Presentation層(View)
    • IObservableを公開
  • Presentation層(Presenter)
    • IReadOnlyReactivePropertyとIObservableを購読
0y00y0

スクリプタブルオブジェクトの扱い

スクリプタブルオブジェクトはどの層からもアクセスできますが、オニオンアーキテクチャに従うのであればインフラ層からアクセスするべきだと思います。
現状は、SODefinitionsというスクリプタブルオブジェクトを定義するディレクトリがインフラ層とプレゼンテーション層に存在します。後者はViewで表示したい文字情報を定義するために使用されており、インフラ層を介さずプレゼンテーション層から直接アクセスしています。インフラ層→ユースケース層→プレゼンテーション層という流れが冗長に感じたためこのような仕様になっています。

このあたりは改善の余地があります。