Share
Embed
大規模Unityゲーム開発の設計事例 〜ドメイン駆動設計とDIコンテナを導入した一年を振り返る〜 / cedec2021-ddd
CEDEC2021 「大規模Unityゲーム開発の設計事例」の資料です。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
セッションの内容
現在開発中タイトルにおける、プログラム設計に関しての知識・経験を共有します。
ソーシャルゲーム開発では、作品を素早く完成させてリリースする事も大切ですが、同時にその後の継続的な運用開発についても考える必要があります。その際に重要になってくるのが設計です。
属人化を避けたり、メンテナンス性、制作スピードを維持するためにはメンバー間で統一された設計思想が必要だと考えました。
そのために私達は「ドメイン駆動設計(DDD)」と「DIコンテナ」という2つの手法を導入しました。
導入から一年が経ち、それらを振り返りつつ事例を紹介します。
https://cedec.cesa.or.jp/2021/session/detail/s60644099db9d6
CyberAgent SGE Engineer
November 17, 2021
Transcript
148 slides
-
2 自己紹介 【経歴】 広告系Flashコンテンツ開発 → 2011年 アメーバピグ Flash開発で(株)サイバーエージェント入社 → スマホブ
ラウザゲーム開発 → スマホUnityゲーム開発 → (株)アプリボット所属 【主な仕事】 Unity全般・設計・3D描画関係・昔はFlashでアニメーション作ったり演出・UXも考えたりしていました 畑山 政道 Unityエンジニア 2011年 中途入社 -
6 設計が重要 設計の綺麗さは金銭的な価値も生む • イテレーションが素早く回せる事で試行錯誤の回数が増え、品質が上がる • リリース後、運用に入っても機能追加がすぐに実装できる • バグが出にくい •
バグが出ても即座に対応できる といった事はビジネス上では重要 -
8 多人数での開発だと • 自己流の設計が至るところに散らばってしまう • 自己流がぶつかり合うと、コードレビューが難しくなる • 諦め (わからない/面倒だから良いや) •
対立 (自己流 vs 自己流) 設計が重要 -
11 大規模な開発にはsimpleが向いていると判断。 Easyはモック開発であったり、小規模な開発で は向いている。 EasyではなくSimpleな方針を取る。 その代わり手数は増える(コード量は増える)が 分かりやすい構造を目指す。 Simpleの実現にDDDが向いていると判断 EasyとSimpleの違い 設計が重要
- easyとsimple https://twitter.com/t_wada/status/1377147203077111814 和田 卓人さんのtweetから引用 -
12 ドメイン駆動設計とは 2003年、エリック・エヴァンスの書籍が発売 される。 Domain Driven Designの頭文字を取って DDDとも呼ばれる。 上手くシステム開発を進めるためのパターン・ ランゲージ。
良くある問題と、それに対するベストプラク ティスについて集めたパターン集。 -
29 改めてゲーム制作のドメインとは何か考える 「ソフトウェアが解決しようとしている問題の対象領域」 「ゲームが表現しようとしている対象領域」 ・自分たちが作っているゲームで表現しようとしているもの ・ゲームが提供する「遊び」 参考: ドメイン駆動設計 モデリング/実装ガイド -
little-hands - BOOTH https://booth.pm/ja/items/1835632 -
30 改めてゲーム制作のドメインとは何か考える 「ゲームが表現しようとしている対象領域」 データベース ファイルI/O 物理エンジン UI サウンド処理 描画エンジン ユーザー入力
世界観 キャラのスキル 成長ロジック ダメージ計算 キャラ性能 通信 行動エリア 地形 キャラ属性 -
31 データベース ファイルI/O 物理エンジン UI サウンド処理 描画エンジン ユーザー入力 世界観 キャラのスキル
成長ロジック ダメージ計算 キャラ性能 通信 行動エリア 地形 システムを構成する要素から、汎用的な部分を抜いて残る部分 キャラ属性 改めてゲーム制作のドメインとは何か考える 「ゲームが表現しようとしている対象領域」 -
32 • 自キャラの成長ロジックなど • どうやってレベルアップする? • 武器/防具を装備する? • スキルの存在 •
インゲーム • ゲームのルール • いつスキルが発動するのか? • ダメージの計算方法は? 改めてゲーム制作のドメインとは何か考える 「ゲームが表現しようとしている対象領域」→ ゲーム独自の概念・ルール・仕様 -
33 自分たちが作っているゲームで実現しようとしているもの • アクションゲームだと • ステージ情報 • 何がどこに配置される? • 壊せる・壊せない・移動する
etc.. • 自キャラ・敵キャラの配置 • 自分・敵キャラのステータス • HP、MP、攻撃方法 etc.. • ジャンプする・敵を踏みつける事で敵を撃退する、のようなゲーム固有のルール 改めてゲーム制作のドメインとは何か考える -
34 将棋ゲームのドメイン知識 の一例 • 登場キャラクター • 40枚の駒 • 駒の性能は8種類。それぞれ動ける範囲が違う •
成長ロジック • 成駒。動ける範囲が増える • mapの広さ・位置 • 9x9の81マスで戦う • 相手の駒を取り、保持する/使う 改めてゲーム制作のドメインとは何か考える -
35 • 9人 vs 9人 • 選手には打率・防御率・スタミナ、etc…などの能力パラメーターがある • ストライク3つでアウト •
バッターはボールを打ち返す • 攻撃側・防御側に分かれ、交互に入れ替わる • これらにゲーム特有のルールも加わる • 選手の成長要素、必殺技、アイテムetc.. 野球ゲームのドメイン知識 の一例 改めてゲーム制作のドメインとは何か考える -
プログラムに適用出来るパターンを一部紹介 値オブジェクト 1 エンティティ 2 3 リポジトリ 4 サービス 5
集約 6 境界づけられたコンテキスト 7 レイヤードアーキテクチャ -
58 関連するオブジェクトの集まり。データを変更するための単位として扱われる 4. 集約 OrderItem Create Destroy Price VatRate Value
to_i to_f Customer Order add_item remove_item total_price Entity Entity Entity Value object 窓口 = 集約Root 集約内部 この中はカプセル化されている Aggregate 窓口を通さないアクセスは認めない 引用 Facade Pattern as the way to implement Aggregate http://rubyblog.pro/2017/05/facade-pattern -
60 Facadeパターン Facade適用後 Subsystem内に、 外から自由にアクセスしている 窓口を通してしかアクセスできない 引用 Facade Pattern as
the way to implement Aggregate http://rubyblog.pro/2017/05/facade-pattern 窓口を設ける事で、複雑な構造をカプセル化する 4. 集約 -
61 データ(集約)の置き場所 データストアを操作する処理をカプセル化する。集約の保存・取得はここを通す。 他のクラスはデータストアを直接操作しない。 DBがある事を意識せず、メモリ内コレクションのように振る舞う。 5. リポジトリ 依頼者 集 約
集 約 write read 保存 問い合わせ 返却 リポジトリ -
68 ユーザーインターフェース層 (プレゼンテーション層) • UI表示 • アウトゲームのMVC/MVPで構成された各画面 現プロジェクトでは、 MonoBehaviour継承クラスだからUI層、とはしていない。 そのクラスが提供している機能で判断している。
各パターンがどの層にあてはまるか 7. レイヤードアーキテクチャ -
69 アプリケーション層 • ドメインオブジェクト・基盤機能を使って、アプリ ケーション全体の調整・進行役を担う • ドメインとUI層の架け橋 各パターンがどの層にあてはまるか 現プロジェクトでは、ユーザー情報、キャラクター情報の管理、取 得、変更などを担う機能実装がある。
機能毎にユースケース・クラスを作成し、そこに処理を記述してい る。 キャラクターをレベルUPさせる・装備を変更する・etc.. 7. レイヤードアーキテクチャ ゲームが持つ機能の実装 -
70 ドメイン層 • ドメインロジックの実装 • 値オブジェクト • エンティティ • ドメインサービス
各パターンがどの層にあてはまるか システムの心臓部 7. レイヤードアーキテクチャ -
71 ドメイン層が他のレイヤーと隔離されてい る事が重要。 他にもパターンは ・オニオンアーキテクチャ ・ヘキサゴナルアーキテクチャなどがあ る。 参考 : [DDD]ドメイン駆動設計で実装を始めるのに一番とっつきやすいアーキテクチャは何か
https://qiita.com/little_hand_s/items/ebb4284afeea0e8cc752 7. レイヤードアーキテクチャ -
74 依存関係逆転の原則 レイヤードアーキテクチャの実装 具象 ではなく、抽象 だけを参照するようにする 具象 → 実装クラス 抽象
→ interfaceやabstractなどの抽象宣言 <参考> Robert C.Martin,角 征典,高木 正弘. Clean Architecture 達人に学ぶソフトウェアの構造と設計 -
75 依存関係逆転の原則 レイヤードアーキテクチャの実装 Presenter Application 上層から下層への依存関係しか存在しなくなる ソースコードの依存方向 (※ 処理の流れではない) Assembly
Definitionsを設定 UIレイヤー (プレゼンテーション層) アプリケーション層 -
79 しかし、結局は上層のインスタンスを取得する必要がある。 では、どう取得するのか? Abstract Factory、Service Locatorなどの解決方法があるが、 現プロジェクトでは後述のDIコンテナを使用した。 レイヤードアーキテクチャの実装 依存関係逆転の原則 Presenter
Application IOutput 実体であるviewは、 どうやって取得するのか? -
92 • 各種サービスの連携 • レイヤーを跨いだ参照の受け渡し • 逆方向のレイヤーにある実装を使用する場合は、 interfaceを置く場所で制御 プロジェクトでの使い方 依存関係逆転の原則を活用
Interface 実装 ここにinterfaceを置く この層の実装を使いたい -
103 下記の3つの条件を満たすようにコードを記述する 契約による設計 事前条件 関数の開始時に、関数を呼ぶ側で保証すべき条件 事後条件 関数が終了時に保証すべき条件 不変条件 そのオブジェクトが常に満たすべき条件 コードにすると?
参考URL https://developer.hatenastaff.com/entry/2016/09/01/163542 -
120 値オブジェクト等のImmutableなクラスに変更。 ただ情報を他のレイヤーに運ぶだけのオブジェクトであれば、 DTO(Data Transfer Object)として表現。 現プロジェクトではDTOもImmutableとして作っている。 とりあえず色々Entityにされがち ドメイン駆動設計 -
つまづいた点 生成後に変化しない物もEntityとして作ってしまった。 対処 -
125 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 - つまづいた点 基本的にはデメテルの法則を適用する。 クラスCのメソッドfは、次のオブジェクトのメソッドのみを呼び出すべき • Cそのもの •
fで生成されたオブジェクト • fの引数で渡されたオブジェクト • Cのインスタンス変数に保持されたオブジェクト fは、上記の許されたメソッドから返されたオブジェクトのメソッドを呼び出し てはいけません。つまり友達とのみ会話し、知らない人とは会話してはいけない のです。 Robert C.Martin,花井 志生. Clean Code アジャイルソフトウェア達人の技 (Japanese Edition) (Kindle の位置No.2626-2631). Kindle 版. -
126 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 - つまづいた点 ただし、デメテルの法則を適用するのは振る舞いを持つオブジェクトに対してのみ。 振る舞いを持たないデータ構造は内部を公開しても構わない。 データ構造に過ぎず、何ら振る舞いを持たないのであれば、これらは内部構造をごく自然に公開しており、デメテルの法則は 適用されません。 Robert
C.Martin,花井 志生. Clean Code アジャイルソフトウェア達人の技 (Japanese Edition) (Kindle の位置No.2654-2655). Kindle 版. -
135 大きな集約をどこまで許容するか • 分割した場合、整合性・不変条件をどう保っていくか 参考 DDDで複数集約間の整合性を確保する方法(サンプルコードあり)[ドメイン駆動設計] https://little-hands.hatenablog.com/entry/2021/03/08/aggregation トランザクション単位で分割する手法があるが、 UnityのクライアントコードではDBが絡むトランザクションが関係ない事も多い。 その場合は大きいままでも良い?
モデリングが上手く行っていないのかも? まだ明確な答えは出ておらず、いったん大きいまま扱っている。 ドメイン駆動設計 - 悩み -
137 ユースケースの実現(アプリケーションが提供する機能)と捉え、現状アプリケーション層で 実装されている。 しかしドメインと関係の無い基盤機能と捉える方が適切なため、実装はインフラストラク チャ層、その機能を使う処理がアプリケーション層にある、という形の方が適切だった Unityでの実装がどのレイヤーになるのか ドメイン駆動設計 - 悩み 汎用機能
・アセットバンドルを読み込む ・ポップアップを表示 ・シーンの読み込み、画面遷移させる、など ・音を出す ? -
141 ドメイン駆動設計 良いところ • DDDを導入する事でチーム内で統一された設計思想を共有できるようになった • 悩んだ時 / 説明する際に参照できるリソースが豊富 •
データと振る舞いがセットで記述される事でコードの保守性・可読性が向上する • ドメインへの理解が推奨される • 一度習得すれば所属プロジェクトが変わっても様々な面で力になる • UnityでなくてもOK。特定の技術にあまり左右されない -
142 ドメイン駆動設計 • 導入のハードルはなかなか高い • 継続的な学習は必要 • チーム内に浸透するまで時間がかかる • まずはプログラムの実装パターン適用からでも効果はある
• それでも各実装パターンへの正確な理解は必要 • ドメイン駆動の名の通り、ドメインへの理解を深める事が本質 • しかし形から入る事で分かる事も多い 大変なところ・注意したいところ -
143 DIコンテナ 良いところ • singletonの代わりとなる • 多態性を使ったモック作成がやりやすい • 依存関係を管理しやすい 大変なところ
• 最初の学習コスト • 結局やってる事は単純ながら、概念を掴むのが難しい -
146 エリック・エヴァンスのドメイン駆動設計 https://www.amazon.co.jp/dp/B00GRKD6XU ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 https://www.amazon.co.jp/dp/B082WXZVPC 実践ドメイン駆動設計 https://www.amazon.co.jp/dp/B00UX9VJGW ドメイン駆動設計 モデリング/実装ガイド
https://booth.pm/ja/items/1835632 Clean Code アジャイルソフトウェア達人の技 https://www.amazon.co.jp/dp/B078HYWY5X Clean Architecture 達人に学ぶソフトウェアの構造と設計 https://www.amazon.co.jp/dp/B07FSBHS2V オブジェクト指向入門 第2版 原則・コンセプト https://www.amazon.co.jp/dp/4798111112 参考書籍 -
147 DDDスコアカード https://www.informit.com/articles/article.aspx?p=1944876&seqNum=2 Domain Driven Design(ドメイン駆動設計) Quickly 日本語版 https://www.infoq.com/jp/minibooks/domain-driven-design-quickly/ DDD
Reference - Domain Language https://www.domainlanguage.com/ddd/reference/ DDDリファレンス 定義とパターン概要 (鋭意修正中, CC-BY) https://zenn.dev/takahashim/books/fb4cdc32f8e95c ドメイン駆動設計をゲーム開発に活かす https://www.slideshare.net/masuda220/ss-111011089 ゲームで学ぶ「役に立つ」ドメインモデルの考え方 - Qiita https://qiita.com/MinoDriven/items/7b4609d0bc6717769060 DDD難民に捧げる Domain-Driven Designのエッセンス 第1回 ドメイン駆動設計とは https://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap1.html より引用 Facade Pattern as the way to implement Aggregate http://rubyblog.pro/2017/05/facade-pattern 参考資料URL -
148 [DDD]ドメイン駆動設計で実装を始めるのに一番とっつきやすいアーキテクチャは何か https://qiita.com/little_hand_s/items/ebb4284afeea0e8cc752 DIコンテナの本当の使いどころ https://www.ulsystems.co.jp/topics/025 48. GoFデザインパターンとDI (前編) w/ twada
https://fukabori.fm/episode/48 契約による設計の紹介 - Hatena Developer Blog https://developer.hatenastaff.com/entry/2016/09/01/163542 契約による設計、例外、表明の関係について個人的なまとめ https://qiita.com/hiko1129/items/f312212070716f672ff6 値オブジェクトの実装 | Microsoft Docs https://docs.microsoft.com/ja-jp/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects DDDで複数集約間の整合性を確保する方法(サンプルコードあり)[ドメイン駆動設計] https://little-hands.hatenablog.com/entry/2021/03/08/aggregation 参考資料URL