Я думаю, что главная причина, по которой это не работает, в том, что у вас, вероятно, также есть еще 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
всегда в 0
;))
Теперь я могу переключаться между test
и test2
с помощью клавиши Пробел .
Если я перетаскиваю один из объектов, я могу перенести его в следующую сцену (но только по одному за раз). Я даже могу снова вернуться к первой сцене, чтобы, например, два сферных объекта, с которыми я могу играть;)