tsg4gf
@tsg4gf (あやね ばば)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Unityでシーンの状態を保存したまま非同期で次のシーンへ移りたい

解決したいこと

ステージセレクトシーンから、ゲームシーンへの遷移で悩んでいます。
ステージセレクトシーンの状態を保存したまま、非同期でゲームシーンに移りたいのですが、どうやったらいいんでしょうか?
unityの機能には入ってませんが、下記のようなことです。

SceneManager.DontDestroyOnLoadAsync("GameScene");
1

2Answer

こんな感じで行き来できました。

方法A

各シーンが自身で終了する方法です。
セレクター側は単に選ばれたシーンをロードするだけです。

セレクター側

SceneSelector.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>シーンセレクタ</summary>
public class SceneSelector : MonoBehaviour {

	/// <summary>シーンが選ばれた (ボタンなど)</summary>
	public void OnPushSelect (string sceneName) => StartCoroutine (loadScene (sceneName));

	/// <summary>ロード処理</summary>
	private IEnumerator loadScene (string sceneName) {
		yield return SceneManager.LoadSceneAsync (sceneName, LoadSceneMode.Additive);
	}

}

選択対象シーン側

SelectedScene.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>選ばれたシーン</summary>
public class SelectedScene : MonoBehaviour {

	/// <summary>戻るボタンが押された</summary>
	public void OnPushUnload () => StartCoroutine (unloadScene ());

	/// <summary>アンロード処理</summary>
	private IEnumerator unloadScene () {
		yield return SceneManager.UnloadSceneAsync (gameObject.scene.name);
	}

}

方法B

各シーンはセレクターに終了要求を伝え、セレクターが実際に終了させます。
セレクターに制御を集中させた方法です。

セレクター側

SceneSelector.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>シーンセレクタ</summary>
public class SceneSelector : MonoBehaviour {

	#region Static
	/// <summary>シーンセレクタ (シングルトン)</summary>
	private static SceneSelector sceneSelector = null;

	/// <summary>シーンの終了要求</summary>
	public static void RequestUnloadScene (Scene scene) {
		sceneSelector.UnloadScene (scene.name);
	}
	#endregion

	/// <summary>起動時初期化</summary>
	private void Awake () {
		if (!sceneSelector) { // 排他制御
			sceneSelector = this; // セレクタを登録
		}
	}

	/// <summary>シーンが選ばれた</summary>
	public void OnPushSelect (string sceneName) => StartCoroutine (loadScene (sceneName));

	/// <summary>ロード処理</summary>
	private IEnumerator loadScene (string sceneName) {
		yield return SceneManager.LoadSceneAsync (sceneName, LoadSceneMode.Additive);
	}

	/// <summary>シーンを終了</summary>
	public void UnloadScene (string sceneName) => StartCoroutine (unloadScene (sceneName));

	/// <summary>アンロード処理</summary>
	private IEnumerator unloadScene (string sceneName) {
		yield return SceneManager.UnloadSceneAsync (sceneName);
	}

}

選択対象シーン側

SelectedScene.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>選ばれたシーン</summary>
public class SelectedScene : MonoBehaviour {

	/// <summary>戻るボタンが押された</summary>
	public void OnPushUnload () => SceneSelector.RequestUnloadScene (gameObject.scene);

}
3Like

こんな感じでどうでしょうか。
(シーン名はテストに使用しただけなので気にしないでください。)

最初のシーンを残す方法

SceneManagerTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneManagerTest : MonoBehaviour {

	/// <summary>シーンを順繰りにロード</summary>
	private IEnumerator Start () {
		yield return null;
		foreach (var name in new [] { "FlickOrPropagation", "IndexOfTimes", "PointerEvents", "RectContains", "RectStatus", }) {
			yield return SceneManager.LoadSceneAsync (name, LoadSceneMode.Additive);
			yield return new WaitForSeconds (3);
			yield return SceneManager.UnloadSceneAsync (name);
		}
	}

}

ローダーだけを残す方法

この方法だと、オブジェクトが残っているだけで、最初のシーンそのものは残っていません。

SceneManagerTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneManagerTest : MonoBehaviour {

	/// <summary>このオブジェクトが削除されないようにする</summary>
	private void Awake () => DontDestroyOnLoad (gameObject);

	/// <summary>シーンを順繰りにロード</summary>
	private IEnumerator Start () {
		foreach (var name in new [] { "FlickOrPropagation", "IndexOfTimes", "PointerEvents", "RectContains", "RectStatus", }) {
			yield return new WaitForSeconds (3);
			yield return SceneManager.LoadSceneAsync (name);
		}
	}

}
1Like

Comments

  1. @tsg4gf

    Questioner

    ご丁寧にありがとうございます。
    今のシーン(①)を保存して、次のシーン(②)に移るのは分かったのですが、
    ②から①に戻る時に①が初期化されてしまいます。

    これはどうやったらいいのでしょうか?
  2. 以下、「最初のシーンを残す方法」を前提にお話しします。

    > ②から①に戻る時に①が初期化されてしまいます。

    「②から①に戻る時に」、②で①を`LoadSceneAsync`していないでしょうか?
    ②をロードする際に①を破棄しなかったので、②が処理を続けている間も、②が終了したいと思ったときにも、①は背後で動き続けています。
    そこへ②が①を再ロードすると、元の①とは別に、二つ目の①が初期状態で実行されますよね。

    ②が終了するときに、待機中の①に対して、「次は○○をロードしてね」と伝えるか、
    ②が終了したことを①が検知して、「②がやられたか、次は○○だな…」と処理を進めるか、そういった制御になると思います。
  3. @tsg4gf

    Questioner

    ご返信ありがとうございます。
    初期化されてしまう原因は`LoadSceneAsync`でした。
    ②が終了して、待機中の①のシーンに戻りたいのですが、②のシーンを削除しても①に戻りません・・・。
    どうしたら状態が保存されている待機中の①に戻ることができるのでしょうか?
    質問ばかりですみません。
  4. `activeSceneChanged`を使えば、②が自身をアンロードすることで、①に制御が戻った際に検知できそうです。
    あるいは、終える前に`SetActiveScene`を呼ぶことで、①が②をアンロードすることもできそうです。
    はたまた、①の`Update`で常に状況を監視する方法も考えられます。

    SceneManager (スクリプトリファレンス)
    https://docs.unity3d.com/ja/2019.4/ScriptReference/SceneManagement.SceneManager.html

    Qiitaで「unity scenemanager」を検索
    https://qiita.com/search?q=unity+scenemanager
  5. 具体的な解決を別記させていただきました。

    以下蛇足です。

    > `activeSceneChanged`を使えば、②が自身をアンロードすることで、①に制御が戻った際に検知できそうです。
    > あるいは、終える前に`SetActiveScene`を呼ぶことで、①が②をアンロードすることもできそうです。

    `LoadSceneMode.Additive`モードで次のシーンをロードしても、`SetActiveScene`を使わない限り、自動的にアクティブシーンが移動するわけではないようです。
    そのため、別記した回答では、別のアプローチになっています。

    実は、今回初めてSceneManagerを使いました。
    シーンを切り替える必要がある時には便利ですね。
    勉強になりました。
  6. @tsg4gf

    Questioner

    ご返信ありがとうございます!!
    別記までして頂いて、感謝の限りです……!
    無事解決しました!ありがとうございます!!!
  7. 僅かでもお役に立てたなら何よりでした。

Your answer might help someone💌