Как сохранить полное состояние объектов между сценами? - PullRequest
2 голосов
/ 04 июля 2019

При загрузке новой сцены я сталкиваюсь с проблемой, при которой мое перетаскивание мыши не переносится на следующую сцену, и приходится повторно щелкать при загрузке новой сцены.

Мне бы хотелось, чтобы щелчок мыши плавно переходил на следующую сцену, не обращая на это внимания игрока, и в целом я хотел бы знать, как лучше сохранить некоторые игровые объекты и заставить их перенести на следующую сцену.

По сути, я пытаюсь сделать так, чтобы вся игра была похожа на одну большую сцену, в которую игрок может играть, но все же будет разбита на более мелкие сцены, к которым можно будет получить доступ или преобразовать в уровни на более позднем этапе.этап.

Заранее спасибо.

Это код, который я сейчас использую

using UnityEngine;
using System.Collections;
using UnityEngine.Profiling;

public class MoveBall : MonoBehaviour
{
    public static Vector2 mousePos = new Vector2();

    private void OnMouseDrag()
    {    
        mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        transform.position = mousePos;   

        DontDestroyOnLoad(this.gameObject);     
    }
} 

Ниже приведен скрипт, отвечающий за загрузку сцены:

public class StarCollision : MonoBehaviour
{

    private bool alreadyScored = false;

    private void OnEnable()
    {
        alreadyScored = false;
    }


    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.CompareTag("White Ball"))
        {
            if (!alreadyScored)
            {
                ScoreScript.scoreValue += 1;
                StartCoroutine(ChangeColor());

                alreadyScored = true;
            }

        }

        if (ScoreScript.scoreValue > 4)
        {
            SceneManager.LoadScene(1);
        }

    }


    private IEnumerator ChangeColor()
    {

        ScoreScript.score.color = Color.yellow;
        yield return new WaitForSeconds(0.1f);
        ScoreScript.score.color = Color.white;
        gameObject.SetActive(false);

    }
}

1 Ответ

3 голосов
/ 04 июля 2019

Я думаю, что главная причина, по которой это не работает, в том, что у вас, вероятно, также есть еще Camera в новой сцене.

OnMouseDrag полагается на физическую систему внутри, используя объекты Collider и лучевые трансляции из Camera. Теперь, если вы переключите Сцену, я думаю, что одна Камера будет отключена, поэтому ваше перетаскивание будет прервано.

Также использование LoadScene вместо LoadSceneAsync вызывает видимое отставание и также может быть связано с проблемой.


Возможно, у меня есть более сложное решение, но я обычно так делаю:

1. Есть одна глобальная сцена "MainScene"

Эта сцена содержит такие вещи, как, например, MainCamera, глобальные компоненты глобального менеджера, которые никогда не должны быть уничтожены.

2. Используйте добавку асинхронной загрузки сцены

Вы сказали, что не хотите, чтобы пользователь не замечал, когда сцена переключается, поэтому я все равно рекомендовал бы использовать SceneManager.LoadSceneAsync.

Затем, чтобы не выгрузить ранее упомянутый MainScene, вы передаете необязательный параметр LoadSceneMode.Additive. Это делает новую сцену загруженной дополнительно к уже существующей. Затем вам нужно будет только обменять их, выгружая ранее добавленную загруженную сцену.

Я создал очень простой static менеджер для этого:

public static class MySceneManager
{
    // store build index of last loaded scene
    // in order to unload it later
    private static int lastLoadedScene = -1;

    public static void LoadScene(int index, MonoBehaviour caller)
    {
        caller.StartCoroutine(loadNextScene(index));
    }

    // we need this to be a Coroutine (see link below)
    // in order to correctly set the SceneManager.SetActiveScene(newScene);
    // after the scene has finished loading. So the Coroutine is required 
    // in order to wait with it until the reight moment
    private static IEnumerator loadNextScene(int index)
    {
        // start loading the new scene async and additive
        var _async = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);

        // optionally prevent the scene from being loaded instantly but e.g.
        // display a loading progress
        // (in your case not but for general purpose I added it)
        _async.allowSceneActivation = false;
        while (_async.progress < 0.9f)
        {
            // e.g. show progress of loading

            // yield in a Coroutine means
            // "pause" the execution here, render this frame
            // and continue from here in the next frame
            yield return null;
        }

        _async.allowSceneActivation = true;
        // loads the remaining 10% 
        // (meaning it runs all the Awake and OnEnable etc methods)
        while (!_async.isDone)
        {
            yield return null;
        }

        // at this moment the new Scene is supposed to be fully loaded

        // Get the new scene
        var newScene = SceneManager.GetSceneByBuildIndex(index);

        // would return false if something went wrong during loading the scene
        if (!newScene.IsValid()) yield break;

        // Set the new scene active
        // we need this later in order to place objects back into the correct scene
        // if we do not want them to be DontDestroyOnLoad anymore
        // (see explanation in SetDontDestroyOnLoad)
        SceneManager.SetActiveScene(newScene);

        // Unload the last loaded scene
        if (lastLoadedScene >= 0) SceneManager.UnloadSceneAsync(lastLoadedScene);

        // update the stored index
        lastLoadedScene = index;
    }
}

Этот MySceneManager является static class, поэтому он не привязан к какому-либо GameObject или Scene, а просто «живет» в Assets. Теперь вы можете позвонить из любого места, используя

 MySceneManager.LoadScene(someIndex, theMonoBehaviourCallingIt);

Второй параметр типа MonoBehaviour (то есть, в основном, ваши скрипты) необходим, потому что кто-то должен нести ответственность за запуск IEnumerator Coroutine , который не может быть выполнен самим static class .

3. DontDestroyOnLoad

В настоящее время вы добавляете любой перетаскиваемый объект GameObject в DontDestroyOnLoad. Но вы никогда не отменяете это, поэтому все, к чему вы прикасались, будет продолжаться с этого момента ... навсегда.

Я бы лучше использовал, например, что-то вроде

public static class GameObjectExtensions
{
    public static void SetDontDestroyOnLoad(this GameObject gameObject, bool value)
    {
        if (value)
        {
            // Note in general if DontDestroyOnLoad is called on a child object
            // the call basically bubbles up until the root object in the Scene
            // and makes this entire root tree DontDestroyOnLoad
            // so you might consider if you call this on a child object to first do
            //gameObject.transform.SetParent(null);
            UnityEngine.Object.DontDestroyOnLoad(gameObject);
        }
        else
        {
            // add a new temporal GameObject to the active scene
            // therefore we needed to make sure before to set the
            // SceneManager.activeScene correctly
            var newGO = new GameObject();
            // This moves the gameObject out of the DontdestroyOnLoad Scene
            // back into the currently active scene
            gameObject.transform.SetParent(newGO.transform, true);
            // remove its parent and set it back to the root in the 
            // scene hierachy
            gameObject.transform.SetParent(null, true);
            // remove the temporal newGO GameObject
            UnityEngine.Object.Destroy(newGO);
        }
    }
}

Это метод расширения , который позволяет просто позвонить

someGameObject.SetDontDestroyOnLoad(boolvalue);

для любой ссылки на GameObject.

Затем я изменил ваш скрипт на

public class MoveBall : MonoBehaviour
{
    public static Vector2 mousePos = new Vector2();

    // On mouse down enable DontDestroyOnLoad
    private void OnMouseDown()
    {
        gameObject.SetDontDestroyOnLoad(true);
    }

    // Do your dragging part here
    private void OnMouseDrag()
    {
        // NOTE: Your script didn't work for me
        // in ScreenToWorldPoint you have to pass in a Vector3
        // where the Z value equals the distance to the 
        // camera/display plane
        mousePos = Camera.main.ScreenToWorldPoint(new Vector3(
            Input.mousePosition.x,
            Input.mousePosition.y,
            transform.position.z)));
        transform.position = mousePos;
    }

    // On mouse up disable DontDestroyOnLoad
    private void OnMouseUp()
    {
        gameObject.SetDontDestroyOnLoad(false);
    }
}

А в вашем StarCollision скрипте вам нужно только обменять

SceneManager.LoadScene(1);

с

MySceneManager.LoadScene(2, this);

Демо

Для небольшой демонстрации я "подделал" его с помощью двух простых скриптов

Этот на главной сцене

public class LoadFirstscene : MonoBehaviour
{
    // Start is called before the first frame update
    private void Start()
    {
        MySceneManager.LoadScene(1, this);
    }
}

И этот в других сценах

public class LoadNextScene : MonoBehaviour
{
    [SerializeField] private int nexSceneIndex;

    private void Update()
    {
        if (!Input.GetKeyDown(KeyCode.Space)) return;

        MySceneManager.LoadScene(nexSceneIndex, this);
    }
}

И есть 3 сцены:

  • Main: Как уже упоминалось, содержит

    • Основная камера
    • Направленный свет
    • LoadFirstScene

    enter image description here

  • тест: содержит

    • a MoveBall "Сфера"
    • LoadNextScene

    enter image description here

  • test2: содержит

    • MoveBall "Куб"
    • LoadNextScene

    enter image description here

С индексами, соответствующими настройкам сборки (убедитесь, что Main всегда в 0;))

enter image description here

Теперь я могу переключаться между test и test2 с помощью клавиши Пробел .

Если я перетаскиваю один из объектов, я могу перенести его в следующую сцену (но только по одному за раз). Я даже могу снова вернуться к первой сцене, чтобы, например, два сферных объекта, с которыми я могу играть;)

enter image description here

...