見出し画像

UE4を使ったプロジェクトで、いかに効率的で協業しやすい設計をするか

こんにちは、PARTYのテクニカルディレクター、梶原です。

今回は「経験上こういうノリの設計にしておくと効率的かつ初心者も含めた共同開発で事故やストレスが少ない」という完全主観なオレオレ設計をご紹介しておきます。
(PARTYにもこれからUE4を触っていくというエンジニアがいるので、社内共有の意味も込めて。)
ゲーム業界発の情報ではないので決して王道とか定石というわけではないと思いますが、スピーディーに開発を進める上で結構ワークしてきた方法ですので、参考にしていただければ幸いです。
*公開後も突然加筆修正していく可能性高いのでご了承ください。

どういう人に効果的か

年単位で開発をするゲーム業界とは違い、展示やイベントでの制作は使える開発期間が2ヶ月とか3ヶ月しかないことがほとんどです。
そして機能が少ないかというとそういうわけではなく、「ただ制作期間が短い」だけなので、いかに速く効率よく、そしてUE4習熟度の差がある協業環境でバグなく開発していくかが肝になります。
ここで紹介する考え方は、主にこういった状況にある(デジタル広告業界とかアート・エンタメ業界)プロジェクト向けのものとなります。

過去のUE4での展示事例

UE4を使った展示・イベントの事例をいくつか貼っておきます。

設計概要

さて、そもそもUE4ではエンジンの設計思想に沿って機能設計していけば自然ととても綺麗な設計になります。ここはUnityなんかと大きく違うところです。
ただ、小中規模のプロジェクト設計についてはあまり情報がない印象なので、僕がやる場合の設計を少し体系立てて紹介しておきます。

まずフォルダ単位で切り分ける

UE4では、主に”Content”というフォルダにプロジェクトで使うアセットたちを入れていきます。そして、そこから先のフォルダの切り方は自由です。
僕がまずプロジェクトを立ち上げる際、まずやるのがフォルダ階層を作ってしまう作業です。

なぜかというと、多くのエンジニアで作業する場合に、「誰それはこのフォルダ」という感じで作業を切り分けやすいからですね。UE4の習熟度が統一されていて、みんなが共通の勘所がある場合は必要ないかもしれませんが、初心者などが含まれている場合こういったわかりやすい線引きの仕方をしておくと後々プロジェクトが肥大化していった際に便利です。

ざっくりフォルダ構成例

まず大きくこんな感じでフォルダ構成を組みます。

画像1

Main:単体機能部分以外の主なアセットは全てここに入ります
Features:単体機能のものをフォルダに分けていきます
StoreAssets:購入したアセットなどはここにまとめます

それぞれの中身ですが、

画像2

Baseという親レベルに、様々なサブレベルを紐付けていきます。このBaseレベルにはActorなどは置かず、あくまでサブレベルの制御用の親レベルです。
Commonというフォルダには、全体で共通で使うものを入れていきます。例えばこんな感じです。

画像3

GameInstanceやGameMode、SaveGameや共通で使うStructやEnumなどもここで管理します。
一方、Mapフォルダにはマップを構成するサブレベルたちをフォルダに分けて格納していきます。

画像4

Featuresフォルダには、各単体機能をフォルダ単位で格納します。

画像5

設計思想

なんだよ、フォルダわけだけかよ。というところではありますが、ここからが大事なところです。

機能の分離のための設計

Featuresフォルダ内では、全ての機能を単体テストできる様に切り分けて考えます。つまり、各機能の要素はすべてフォルダ内に閉じ込め、単体のテスト用レベルの中で機能を実装していきます。
また、プロジェクト本体にマージする際は、マージ用のサブレベルとしてPersistantレベルに追加していきます。
大きく“Features”というフォルダの中に各機能それぞれのフォルダを作り、例えばチャット機能などは“Chat”というフォルダを作り、その中で機能が完結するようにテストレベルを用意してそこに機能を実装していきます。

機能単位でサブレベルに分解する

Blueprintクラスで機能を作り、それをプロジェクトにマージしていったりすると思うのですが、可能であればサブレベルにそれを配置し、プロジェクトにはサブレベルを追加していく様な形で進めていきます。そして、そのサブレベル単体でも問題なく機能が動作するのを確認しておきます。
UE4ではサブレベル間でのデータのやり取りや参照をとるのが実質できない様になっていて、これはある意味不便ではあるのですが、この設計に従うことで各機能の依存性を最小限に抑えることが可能です。
サブレベル間でのデータをやり取りするのは、大元のPersistantレベル、もしくはGameInstanceにその仲介役を任せます(もちろんそれ以外の手法もたくさんありますが、主にこれらを使う。という意味です)。ただし基本的にPersistantレベルはActorのない空のレベルにしておき(普段は“Base”という名前を付けています)、レベルブループリント内部には、各レベルの呼び出しなど、プロジェクト全体の制御に関わる基本処理を書く程度に留めるようにしておきます。
極力単体で機能する様に各機能の設計をし、連携が必要なものはそれ用の機能を使う。という考え方ですね。
こうすることで、数多くの利点が生まれます。

1.  多人数が同時並行でマップ・機能の開発ができる
2. 不要な機能をサブレベル単位で切り離すことができる
3. バグがあった際に、原因を切り分けて考えられるため解決ステップを最小限に抑えられる
4. Persistantレベルをいじる際、協業者間でコンフリクトが起きる(めちゃ起きやすい)のを軽減できる

サブレベルを跨ぐ機能

ちなみに、機能を分離したサブレベルを跨いだ処理が必要なものは何か?というと、代表的なものとしてネットワーク系の機能です。
APIやマルチプレイヤー機能を実装する際は、それぞれのAPIの単体テストも必要ですが、そこから返ってきた結果をマップやキャラクターなどの状態に反映する必要が出てきます。
マップおよびキャラクターもまた、別のサブレベルに設置しているため、ここでサブレベルを跨ぐ処理が必要になってくるわけです。

機能同士の連携

基本的にはイベント駆動で様々な機能が連携するように設計します。
Aというイベントに対し、それをListenしている機能たちがトリガーで発動するって感じですね。毎フレ状態をチェックして動作するようなことはあまり起きないようにします。なので、「Tickを切っていても動作するように作れたらいいなぁ」と思いつつ設計して、「うーん、、いや、Tickいるか。」ってときだけTickをオンにしておく。というノリで作っていきます。テストレベルでは、このイベント発火をデバッグに使ってテストをできるようにしておけばテストの際もマージの際も簡単です。

マップの作成

こちらも同じくサブレベルに分割して制作していきます。レベルデザインとライティングのデザイン、細かいVFXや天候などの実装担当が分かれることも多いので、それぞれ別のサブレベルに分割するなどして同時に作業を進めていきます。
大元のガイドとなる地形は、別のサブレベルとしてプロジェクト最初期に作ってしまいその他の作り込み時は、それぞれのレベルデザイン時にサブレベルとして地形をガイドで読み込んで作業を進めます。

まとめ

ということで、最も重要な点は、「機能を分離すること」にあります。

 まずはフォルダで各自の役割を明示的に分割し、タスクを振り分けていく。
そして、それぞれの中で開発を進めていき、適宜メインのPersistantレベルにマージしていく。その際、必ず単体テストができていること。そしてそれをほとんどそのまま(インターフェースをつける程度の追加で)マージできていることに留意しながら進めていきます。

ちなみに開発者の人数が少ない場合どうしても担当する機能の粒度が大きくなりがちですが、「バグが起きた際にいかに問題を切り分けて対処できるか?」を常に意識した粒度で機能を分離していくのが重要となります。分離する粒度の選定を慎重に行いましょう
この辺りはゲームエンジンだとかは関係なく、いわゆるプログラマの腕と勘の魅せどころではあるので、色々やりやすい方法を試してください。

最後に

設計手法は様々な方法があると思いますが、今回はプロジェクトの全体的な設計の話をしました。これが最適だぜ!ってことでは全くもってないので、あくまでご参考程度に。
ただ、1つ「型」みたいなのを持っていると改善もしやすいので、何でもいいですがスタイルを作っておくことはお勧めです。
内部の細かい設計に関しては、また別の記事で紹介していこうと思います。

この記事が気に入ったらサポートをしてみませんか?