テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】シーン間でスコアを共有 まとめ

Unityの特性として「シーン遷移(LoadLevel)時にオブジェクトを破棄」します。このため、オブジェクトにデータを持たせている場合は破棄されてしまします。

 

しかしシーン内で解決する問題だけでなく、ゲームの進行では必ずといってよいほどシーン間のデータのやり取りが必要になります。
このデータの扱いは、先にやり方を決めておかないと後で色々と調整が発生して面倒くさい事になります

目次

static(静的な)オブジェクトとして保持する

一番手っ取り早い方法は、staticな変数に格納することです。
staticで格納した値はインスタンス(オブジェクト)単位ではなくゲーム全体で保持されるため、シーンが変わりオブジェクトが破棄された後でも保持され続けます

gist.github.com

注意すべきは、この保持され続ける効力が自身でリセットする処理を挟まなければずっと持続する点です。
例えばスコアをstaticに格納しスコアを加算し続けた場合、2週目から前回のスコアを引きついた強くてニューゲームになってしまいます。
このリセットのタイミングがstaticな変数を使用する上でのキモとなるので、リセットを行うタイミングはよく考える必要があります。

またUnityはstaticな変数はシリアライズする事が出来ません

オブジェクトを破棄されないようにする

シーン遷移時に破棄されないのであれば、DontDestroyOnLoadを使用することが出来ます。この設定を付与したGameObject、および付与されたオブジェクトの子オブジェクトはLoadLevel時に破棄されなくなります

つまりDontDestroyOnLoadを使用することでオブジェクトが破棄されなくなり、遷移先のシーンからFind等を使用して参照関係を構築すれば問題無くデータを引き渡し出来る訳です

gist.github.com

ですが問題があります。DontDestroyOnLoadを設定したオブジェクトは明示的にDestroyしないと破棄されない点です。

例えば最初のシーンでDontDestroyOnLoadを設定するコードがある場合、2週目になってもオブジェクトが破棄されず、2つ目のDontDestroyOnLoadが設定されたオブジェクトが生成される事になります。またDestroyOnLoadを解除する事は出来ません。

また事前にインスタンスのあるシーンからロードする必要があるため、自然とゲームを実行する順番が強制される事になり、ゲームの動作確認が非常に面倒になります。

 

なお「DontDestroyOnLoadが付与されたオブジェクトは子オブジェクトを破棄しない」特性を利用することで、一時的にLoadLevel時に破棄されないオブジェクトとする事が可能です。
これはオブジェクトごと使いまわしたい時(破棄と生成コストが高いオブジェクト)等に使用すると若干良いです。

tsubakit1.hateblo.jp

オブジェクトは破棄されないし重複もしない

Singletonは「オブジェクトのインスタンスが一つである事」を保障するパターンです。これと上記のDontDestroyOnLoadを組み合わせる事で、2週目にインスタンス複数出来てしまう問題を回避することが出来ます。

また全てのシーンにインスタンスを配置する事で、DontDestroyOnLoadの問題であったシーンの呼び出し順番による制約を回避することが出来る点や、
staticを介してアクセス出来るためFindといった不確定要素を使用しなくても済むポイントがあります。

破棄も簡単なので、良く使われる手法ではあります。

gist.github.com

シーンを加算ロード・アンロードで
オブジェクト破棄を行わない

Unityのオブジェクトを破棄するタイミングはLoadLevelのタイミングです。なので、LoadLevel(遷移)ではなくLoadLevelAditive【加算)し、加算後に使用していないシーンをアンロードする事でオブジェクトの掃除を行います。

つまりこのアプローチは「オブジェクトを破棄しないシーン読込」で問題を回避します。

gist.github.com

このアプローチは、シーンの呼び出し順番がよりシビアです。データの共有というよりは、UIの追加・削除といったアプローチに向いているかもしれません。

ちなみにUnity 5.3よりSceneManagerが追加されます

 

なお以前はこのアプローチはシーンではなくResourcesで行っているケースが多かったです。これは以前のUnity(Unity 3.x)がLoadLevelが遅かった要因もあります。

アセットのインスタンスにデータを格納

若干異端ですが、ScriptableObjectにデータを格納するアプローチです。

このアプローチはアセットとして登録したScriptableObjectにデータを格納するアプローチで、シリアライズしたデータを使用できる点や、参照をスクリプトではなくメタデータで行える点、あとはDontDestroyOnLoadSingletonのようにシーン遷移時にオブジェクトの生成と破棄が行われない点がポイントです。

tsubakit1.hateblo.jp

なおAPIで設定したい場合は、Resourcesからインスタンスを呼び出します。
また、ScriptableObjectを実行後シングルトンのような形でstaticに登録してしまう事もアリです。

データを外部に保存する

データの設計が面倒くさい場合は、PlayerPrefsやIOにデータを格納してしまうアプローチがあります。

このアプローチの最大の利点はゲームを終了しても破棄されない事です。このため、例えばゲームを終了した際のシーンや最後のステータスを格納しておけば、シーン遷移で進行するゲームならば最後のシーンから再開する事が出来ます

tsubakit1.hateblo.jp

ただ、IOに書きだしたデータは簡単に改ざんできるので、その辺りは注意が必要です。