PONOS Advent Calendar 2022 13日目です。
昨日は、@nissy_gpさんでした。
はじめに
Unityの開発を始める際に、最初の方で決めなければならないのが、画面管理をどのように行うべきかだと思います。
これまで多くのプロジェクトに参加してきまして、SceneかPrefabでの管理の2通りに分かれておりました。
それぞれのメリット・デメリットを上げてどちらを選択したほうがいいか決める際の参考になればと思います。
Unityの画面管理
Unityの画面管理は、大きく分けて複数のシーンを都度読み込みシーンを組み合わせて画面を形成していくやり方と、
1つのシーン内でPrefabの生成し画面を形成していくやり方が主にあるかと思います。
両方を組み合わせて実装しているプロジェクトもあるかと思いますが、基本的な考え方は前の2つを場合によって使い分けていると思います。
シーンで画面管理を行うメリット・デメリット
メリット
- 画面管理が簡単に実装できる。
シーンの読み込み自体は1コードでできるため、単純な画面遷移や切り替えは、簡単に実装できます。
戻るボタンや遷移履歴などが必要な場合は少し複雑になりますが、それでもそこまで苦労なく実装できるかと思います。
SceneManager.LoadScene("Test");
-
カメラをシーンごとに作成するため影響範囲が狭い
カメラをシーンごとに用意して調整できるため、特殊な演出や表現などを実装しても他のシーンオブジェクトに影響を与えず、好みの実装を行いやすい。 -
ヒエラルキーの構造を作成者の好きな形に実装できる
これに関しては、自由すぎることで問題になりやすいところでもありますが、ルートのオブジェクトやクラスだけ決めてあとは自由に実装などを行うことも可能となります。
親子関係や参照などが自由に実装でき、また他のシーンへの影響が少ないため固有画面だけの実装などが行いやすいかと思います。 -
シーンで別れているため競合しづらい
シーンで作業が別れているため、複数の画面を同時に作成を進めても影響範囲が限られているため、作業の競合が起こりづらくなります。
共通部分であるヘッダー・フッターも別シーンで用意して重ねるように作成すれば競合なく実装できるかと思います。 -
単体実行が行いやすい
シーンでの実行ができるため、画面単体でテストや確認が行いやすいです。
デメリット
-
シーン間でのデータの渡し方が難しい
シーン間でのデータを渡す方法はいくつかありますが、どれも処理が重いかメモリとして無駄になる場合があります。
ExecuteEvents.Execute
とIEventSystemHandler
を使って別のシーンにあるGameObjectの関数を呼び出してデータを渡す方法がありますが、インターフェイスの検索などを呼び出しのたびに発生してしまうため、処理としては重くなってしまいます。
また、対象のGameObjectを取得する必要があり、別のシーンにあるGameObjectの場合はルートのGameObjectが一番取得しやすくなるため、ルートに追加しているコンポーネントのスクリプトが複雑になりやすくもあります。
データの受け渡しとして、static
の変数やプロパティを用いて対象のクラスに直接値を入れたり、取得したりするやり方もありますが、この場合に問題となるのがメモリの破棄やそのデータの信憑性になります。
static
の場合いつ用意されたのか、いつ破棄されるのかが外部からはわからず、また値を自由に変更できる状態の場合は、他のシーンで変更された場合にそのデータが想定されたものかが判断できないかと思います。 -
共通化が難しい
画面ごとにシーンで管理している場合に似たような画面だけど、シーンを分けてしまったので別で作らなければならないなどが発生しやすくなります。
シーン内のオブジェクトをPrefabで共通化などできていれば問題ないですが、そこがうまく行かないと表示の調整をシーンごとに行っていく必要が出てきてしまいます。 -
実行中のカメラが多くなりやすい
シーンごとにカメラを作成していくため、ちょっとした画面であれば1つのカメラで済みますが、バトルやパズルなどゲーム画面では複数のカメラが必要になる場合があり、その上に別の画面やダイアログを出した場合に多くのカメラが必要となり、結果的に重くなってしまう場合があります。 -
シーンの破棄やそれに紐づくGameObjectの破棄がUnity任せ
シーンの破棄時に紐づくGameObjectの破棄なども行ってくれますが、参照が残っていたりする場合にうまく破棄できずメモリリークが起こってしまう場合があります。
1つのシーン内のPrefabで画面管理を行うメリット・デメリット
メリット
-
Prefab管理のため、データの受け渡しが行いやすい。
Prefabでの管理になるため、生成時にクラスの取得を行っておくことや、生成のコールバックとしてデータを渡すことが実装しやすく、都度検索などもなくコストが安く済みます。 -
生成破棄が管理クラスで行うため明確になる
Prefabの生成破棄が管理クラスを通してのみ行うため、メモリリークが起こりづらい構造にしやすくなります。
メモリリークが起こった場合でも明示的に破棄処理を記載しなければならないため、破棄ができていないところを探すなどある程度対処がわかりやすくなります。 -
共通化が行いやすい
Prefabで作っていくため、基本的にPrefabの継承やPrefabInPrefabを使用しての共通化を行っていく形になるかと思います。 -
カメラの数を抑えられる
1つのシーン内で完結するため、カメラもある程度抑えることができます。
必要に応じて増やす場合はありますが、描画レイヤーが同じであれば1つのカメラで済みますし、使いまわして描画するという事もできます。 -
static
を減らすことができる
1つのシーンで管理している場合は、シーンが切り替わってデータが破棄されるというタイミングが無いため、GameObjectやパラメータをstatic
で保持する必要はなくなります。
デメリット
-
画面管理クラスの実装が必要
画面の表示、切り替えなどを行う管理クラスを実装しないと画面遷移ができないため、その実装と設計が必要になります。
長期的なプロジェクトな場合は、画面遷移を管理してくれるシステムが必要になる場合が多々あるため、実装が必要ですが、短期的なプロジェクトではコストが重くなってしまいます。 -
単体実行を行う機能の追加
Prefabで作成の場合、単体で実行するための機能が必要となってしまいます。
Prefabだけではゲームが処理できないため、単体実行用のシーン生成や必要な設定を行うためシステムを作成する必要が出てきます。 -
競合しやすい
シーンのように別れていないため、Prefabの競合や共通部分の競合などが起こりやすくなります。
また、一部の変更が全体に与える影響も大きくなるため、管理をきちんと行っていく必要があります。 -
ヒエラルキーの構造に制限が生まれる
1つのシーンで表現するため、他の画面に影響が出るようなヒエラルキーの構造にはできませんし、生成破棄を管理する上でもシーンごとに分かれている場合ほどの自由度はありません。 -
カメラの設定変更影響が大きい
カメラの設定を変更しての表現調整を行った場合に、きちんと初期化やリセットを行わないと他の画面へ遷移した場合にも残ってしまい不具合へと繋がる場合があります。
両方を組み合わせた作り
これまでは複数シーンでの管理と1つのシーンでPrefabを生成破棄する管理を記載してきましたが、両方を使うというやり方もあります。
ゲームのバトルやパズルなど遊びの部分だけをシーンで分けて、ホームや育成画面などは、1つのシーン上で行うなどがあります。
遊びの部分は、何度もテストが必要だったりバランス調整で単体実行が行えたほうがいいため、シーン分けるようにしてホームや育成など他の画面との連携が強いところでは、データの受け渡しを行いやすいPrefabでの管理を行う設計もあります。
両方のメリット・デメリットを取って画面や機能、用途によって分けていきます。
ただ、シーンで分ける部分が増えてきすぎてしまうと、Prefabで管理する部分が不要となり無駄なシステムになってしまう場合があるため、両方を組み合わせる場合でもバランスやプロジェクトの状況を見て進めたほうがいいかと思います。
まとめ
複数シーン管理、1つのシーンでのPrefab管理、その両方を入れた管理を紹介しました。
どの実装にもメリット・デメリットがあり、プロジェクトの規模や方針、開発期間によって分けていく形がいいと思います。
皆様の画面管理システムを作成する際の参考になれば幸いです。
明日は @honeniqさんです。