1. LoadScene();について
Unityでゲーム制作をするとき、いくつかのシーンを用意してそれぞれのヒエラルキーにオブジェクトなどを配置していくことが多いと思います。
例えばタイトルシーンにはスタートボタンのUIやタイトルのロゴなどを配置するし、ゲームシーンなんかだとプレイヤーのモデルやアイテムのオブジェクトなんかを配置しますよね。
今まではそうしたシーンをLoadScene("シーンの名前");って書いて毎回読み込んで切り替えていたんだけど、これだと画面が”ブツッ”って切り替わるし、何より何回も同じシーンを跨ぐのにわざわざ一回一回ロードしなおすのが馬鹿らしく感じてました。
加えてロードされたかと思うとすべてが初期化されてるしスクリプトもStart()から読み直してるしでいろいろと不便なんですよ……
「あぁ……なんかシーンを重ねて表示する方法ないのかなぁ」
そんなの夢物語だとわかっていながら、一抹の期待を込めてAI君に聞いてみました。
ありました、そんな方法。
それが今回の題材であるLoadSceneMode.Additive君ということです。
2. Additiveの挙動について
上の章でも触れていますが、
Additive = シーンを重ねる形でシーンを読み込む方法
です。
言葉だけで説明すると分かりづらいと思うので、実際にこれを使用したスクリプトを使って説明していきます。
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartButton : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnClick()
{
SceneManager.LoadScene("GameScene");
SceneManager.LoadScene("FieldScene", LoadSceneMode.Additive);
}
}
使ってもないのに取り残されているStart()とUpdate()はいったん無視してもらって、
一番下の方でLoadSceneを二回しているのが分かると思います。
片方はLoadScene("GameScene");で終わっているのに対して、
もう片方ではLoadScene("FieldScene", LoadSceneMode.Additive);と、後ろでモードの指定を行っているのが分かります。
これを実際にアタッチして動作を確認すると、以下のような挙動をします。
①タイトルシーンでボタンを押すと、ゲームシーンとフィールドシーンが読み込まれる。
②読み込みが完了するとタイトルシーンからゲームシーンに切り替わる。
③ゲームシーンの上にフィールドシーンが重なる形でシーンが二つ表示される。
そう、つまりAdditiveで追加するシーンはAdditiveする"前"に「存在する」or「切り替えた先」のシーンの上に表示するという形で処理されるのです。
Additiveを先に置くと、今回の場合はフィールドシーンがタイトルシーンの上に一瞬重ねられたうえで、ゲームシーン単体に切り替わるという形で処理されます。
「結局これで何ができるのかわからない」
確かに、これを知っただけではAditiveを正しく活用することなどできません。
そのため、次の章では「Additiveの有用性」を説明していきます。
3. Additiveが可能にすること
「シーンが重なって表示される」ってだけじゃイマイチその有用性が分かりづらいと思います。
ここで一度、シーンが持っている要素に目を向けてみましょう。
めちゃくちゃ大雑把に整理すると”ヒエラルキーがあって、そこには配置されたオブジェクトが並べられている”ということが分かると思います。
そして、オブジェクトにはアクティブ/非アクティブ状態なるものがあります。
察しの良い方は気づいたかと思いますが、Additiveが可能にすることの一つ目として、
3-1. シーンのオブジェクトのアクティブ状態を切り替えるだけでロードせずともシーン切り替えに近いことができる!!
というものがあります。
シーン切り替えとは異なり、非アクティブ化されたオブジェクトにアタッチされたスクリプトは初期化されず、Update関数などは一時停止している状態であるという点については注意が必要です。
例えばRPGゲームなどでフィールドシーンからバトルシーンに移行した後に、再度フィールドシーンに戻るなんて処理をしたいことがあると思います。
この時に通常のLoadSceneでシーンを切り替える形を採用してしまうと、バトルシーンからフィールドシーンに戻った際にフィールドシーンの状態(プレイヤーの位置・向き・敵の配置など)が初期化されてしまう問題が起きます。
しかし、Additiveを用いてバトルシーンをフィールドシーンの上面に表示する形を取れば、フィールドシーンのオブジェクトを非アクティブ化するだけで簡単にバトルシーンだけを映し出せますし、戻ってきた時にはアクティブ化されたオブジェクトは戦闘前の状態から再始動する形になり、非常に自然な処理を行うことができるのです!
「バトルシーンは毎回初期化して読み込みなおしたい」という場合は戦闘開始時にバトルシーンをAdditiveで追加表示し、戦闘終了時にUnLoadSceneをすることでフィールドの状態は保持したままバトルシーンを初期化することができます。
そして、これに近いですがAdditiveのもう一つの有用性について説明します。
3-2. UIやBGMなど、どのシーンでも使うような要素を一つのシーンで一元管理できる!!!
これ、地味にうれしいです。
それぞれのシーンにCanvasをおいて、MainCameraをおいて、それらを管理するスクリプトを書いて……なんてしなくてOK!
一番下地になるシーンにMainCamera、EventSystem、Canvasをすべて持たせ、UI・BGM・画像などを管理する空オブジェを作り、それぞれの管理オブジェにスクリプトを付ければ完了です!
とはいえこれだけだと分かりづらいので、ちょっとした例を挙げて話します。
例)フィールドシーンからバトルシーンへの移行に伴うUI表示の変更
「フィールドでは使うけどバトルシーンでは見せたくないUI」、「バトルシーンでは使うけどフィールドシーンでは見せたくないUI」、「どっちのシーンでも使いたいUI」と、シーンごとに表示したいUIはきっと異なるはずです。
今回の例であれば戦闘時のコマンドUIなどが当てはまるでしょう。
では実際にどうやってUIの表示を切り替えるのか、それは”バトルシーンの情報を受け取ってUI管理オブジェが表示を切り替える”という形になります。
バトルが開始した瞬間、バトルシーンの情報を管理するオブジェが”戦闘開始”という状態を保持したとします。(これはEnumなどを使って実装する形になるかな?)
そしてこのバトルシーンのオブジェがUI管理をするオブジェに自身の状態の変化を伝えることで、その状態に応じてUI管理オブジェがUIの表示と非表示を切り替える処理を行います。
つまり、
バトルシーンの状態を読み込む➡
それをUI管理オブジェに伝える➡
状態に応じてUIの表示を切り替える
という三段構えの構造を取る形になります。(もちろん他の方法もあると思うけど)
とまぁこんな感じであっちこっちにCanvasおいて~とかしなくても、一つの下地のシーンですべてのシーンのUIとかBGMとかを管理できるっていうことの説明でした。おしまい!
ただしこの形を採用する場合、他のシーンにEventSystemとAudioListenerを置くことは推奨されない(重複してしまうため)
まとめ
ここまでAdditiveについて説明してきましたが、結局やってることとしてはシーンを読み込み、重ねて表示させるという単純な処理だけなんですよね。それがいろいろと使いやすい形であったってだけで。
ただ1つ忘れないのでほしいのはAdditiveが向いてないシーンもあるよってことです。(タイトルシーンとか)
何でもかんでもAdditiveしてると痛い目見ると思うので、ちゃんとAdditiveを使うべきシーン/控えるべきシーンというのを考えて使用しましょう!
ということで今回はこれで説明おしまい!
間違いや誤植があったら教えていただけると幸いです。