Я делаю простой игровой менеджер. У меня есть script, который будет доступен из всех сцен в игре. И мне нужно проверить значения его переменных после загрузки новой сцены. Но мой код запускается только один раз после запуска моделирования, тогда как объект с этим script существует во всех сценах. Что не так? Почему он не работает после загрузки новой сцены?
Unity игровой менеджер. Script работает только один раз
Ответ 1
В каждом проекте Unity у вас должна быть ПРЕДВАРИТЕЛЬНАЯ СЦЕНА.
Весьма странно, что Unity не имеет встроенной сцены предзагрузки.
Они добавят эту концепцию в будущем.
Повторим, у вас буквально должна быть сцена предварительной загрузки.
Это так просто.
Это ЕДИНСТВЕННАЯ БОЛЬШАЯ ОШИБКА для новых программистов, пробующих Unity!
К счастью, предзагрузить сцену очень легко.
Шаг 1.
Создайте сцену с именем "preload". Это должна быть сцена 0 в Build Manager.
Шаг 2.
В сцене "предварительной загрузки" создайте пустой GameObject с именем, скажем, "__app".
Просто поместите DontDestroyOnLoad
в "__app".
Замечания:
Это единственное место во всем проекте, где вы используете DontDestroyOnLoad
.
Это так просто.
В примере: разработчики сделали однострочный DDOL-скрипт.
Поместите этот скрипт в объект "__app".
Вам больше никогда не придется думать о DDOL.
Шаг 3
Ваше приложение будет иметь (много) "общих поведений". Таким образом, такие вещи, как подключение к базе данных, звуковые эффекты, оценка и так далее.
Вы должны и только можете поместить свое общее поведение в "_app".
Это действительно так просто.
Общее поведение тогда - конечно - доступно везде в проекте, всегда и во всех сценах.
Как еще ты мог это сделать?
В приведенном выше примере изображения обратите внимание на "Iap" ("покупка в приложении") и другие.
Все ваши "общепринятые поведения" - звуковые эффекты, оценки и т.д. - прямо на этом объекте.
Важный...
Это означает, что - конечно, естественно -
... у вашего общего поведения, конечно, будут обычные инспекторы, как и все в Unity.
Вы можете использовать все обычные функции Unity, которые вы используете на любом другом игровом объекте.
Конечно, вы можете использовать обычные переменные инспектора (перетащите для подключения), настройки и так далее.
(Действительно: допустим, вы были наняты для работы над существующим проектом. Первое, что вы сделаете, это взгляните на сцену с предварительной загрузкой. Вы увидите все "общие характеристики" проекта в одном месте. Звуковые эффекты, оценка, AI и т.д. Вы сразу увидите все настройки для этих вещей как переменные инспектора... громкость речи, идентификатор игрового магазина и т.д.)
Вот пример "Звуковые эффекты" общего поведения:
Похоже, что здесь также есть "голос за кадром" общего поведения и "музыка" общего поведения ".
Повторить. Относительно вашего "общего" поведения. (Звуковые эффекты, озвучивание, общение и т.д.). Они МОГУТ ТОЛЬКО ИДЕТ на игровой объект в предзагрузочной сцене.
Это не обязательно: другой альтернативы нет!
Вы сделали
Это честно так просто. Там нет буквально ничего!
Многим инженерам из других жанров всегда нужно понимать, что это так просто, потому что это так просто и легко, что "не может быть правдой".
Просто повторюсь: вся проблема в том, что (причудливо) Unity просто забыла "встроить" предзагрузочную сцену. По этой причине вам просто нужно щелкнуть, чтобы добавить один (не забудьте добавить DDOL на сцену с предварительной загрузкой).
Сейчас в процессе разработки:
Всегда начинайте свою игру со сцены Preload.
Это так просто.
(Один важный момент: у вашего приложения наверняка будут "ранние" сцены. Например, "заставка" или "меню". Обратите внимание, что вы не можете использовать "заставку" или "меню" в качестве сцены предварительной загрузки. Вы должны буквально есть сцена предварительной загрузки. Затем сцена предварительной загрузки загрузит вашу сцену-заставку, сцену меню или все, что вы пожелаете.)
Тогда у вас возникнет проблема с простым поиском, скажем, "SoundEffects" или "GameManager" из любого сценария в любой из ваших сцен.
К счастью, это очень просто, это одна строка кода.
Это не проблема:
Sound sound = Object.FindObjectOfType<Sound>();
Game game = Object.FindObjectOfType<Game>();
Сделайте это в Awake
для любого скрипта, который в этом нуждается.
Это честно так просто. Это все, что нужно сделать.
Sound sound = Object.FindObjectOfType<Sound>();
Огромная путаница возникает из-за сотен абсолютно неправильных примеров кода, которые можно увидеть в Интернете. Кроме того, новые инженеры Unity "не могут поверить", это так просто!
Это действительно так просто - честно!
Совершенно странно, что Unity забыла добавить встроенную "сцену предварительной загрузки" - где-то для подключения ваших систем, таких как SoundEffects, GameManager и т.д. Это всего лишь одна из тех странных вещей в Unity. Итак, первое, что вы делаете в любом проекте Unity, это просто один раз щелкните, чтобы создать сцену с предварительной загрузкой.
Это!
Деталь...
Обратите внимание, что если вы действительно хотите набирать еще меньше (!) Строк кода, это удивительно просто - вы можете просто использовать глобальные для каждой из этих вещей!
Это подробно объясняется здесь (вы можете использовать мой популярный скрипт Grid.cs) и в ответе @Frits ниже.
(Независимо от того, используете ли вы глобальную переменную или просто используете локальную переменную в каждом компоненте, который нуждается в этом конкретном случае, это просто вопрос стиля кода. В очень необычных ситуациях "глобальный" подход может быть эффективным (как и в случае любой глобальной).)
DylanB спрашивает: " Во время разработки довольно досадно, что вам приходится каждый раз нажимать на сцену предварительной загрузки, прежде чем нажимать" Play ". Это можно автоматизировать?"
Конечно, у каждой команды есть свой способ сделать это. Вот тривиальный пример:
// this should run absolutely first; use script-execution-order to do so.
// (of course, normally never use the script-execution-order feature,
// this is an unusual case, just for development.)
...
public class DevPreload:MonoBehaviour
{
void Awake()
{
GameObject check = GameObject.Find("__app");
if (check==null)
{ UnityEngine.SceneManagement.SceneManager.LoadScene("_preload"); }
}
}
Но не забывайте: "что еще вы можете сделать?" Игры должны начинаться с предзагрузочной сцены. Что еще вы можете сделать, кроме как пойти на сцену с предзагрузкой, чтобы начать игру? Вы также можете спросить: "Это раздражает запуск Unity для запуска Unity - как избежать запуска Unity?" Игры просто, конечно, должны начинаться с предзагрузочной сцены - как еще это может быть? Так что при работе в Unity вы должны "щелкнуть по сцене предварительной загрузки перед тем, как щелкнуть по Play" - как еще это может быть?
Ответ 2
@Fattie: Спасибо за разработку всего этого, это здорово! Хотя есть момент, что люди пытаются дозвониться до вас, и я просто попробую:
Мы не хотим, чтобы каждый экземпляр всего в наших мобильных играх делал "FindObjectOfType" для каждого и каждого "глобального класса"!
Вместо этого вы можете просто использовать его сразу же, не ища!
И это так просто: напишите это в каком классе вы хотите получить доступ из любого места, где XXXXX - это имя класса, например "Звук"
public static XXXXX Instance { get; private set; }
void Awake()
{
if (Instance == null) { Instance = this; } else { Debug.Log("Warning: multiple " + this + " in scene!"); }
}
Теперь вместо вашего примера
Sound sound = Object.FindObjectOfType<Sound>();
Просто используйте его, не глядя, и никаких дополнительных переменных, просто так, прямо из любого места:
Sound.Instance.someWickedFunction();
Альтернативно (технически идентично), просто используйте один глобальный класс, обычно называемый Grid, для "удержания" каждого из них. Как. Так,
Grid.sound.someWickedFunction();
Grid.networking.blah();
Grid.ai.blah();
Ответ 3
Вот как вы можете запустить любую понравившуюся вам сцену и обязательно реинтегрировать свою _preload-сцену каждый раз, когда вы нажимаете кнопку воспроизведения в редакторе Unity. С момента RuntimeInitializeOnLoadMethod
Unity 2017 RuntimeInitializeOnLoadMethod
новый атрибут, подробнее об этом здесь.
По сути, у вас есть простой плоский класс С# и статический метод с RuntimeInitializeOnLoadMethod
. Теперь каждый раз, когда вы запускаете игру, этот метод будет загружать сцену предварительной загрузки для вас.
using UnityEngine;
using UnityEngine.SceneManagement;
public class LoadingSceneIntegration {
#if UNITY_EDITOR
public static int otherScene = -2;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void InitLoadingScene()
{
Debug.Log("InitLoadingScene()");
int sceneIndex = SceneManager.GetActiveScene().buildIndex;
if (sceneIndex == 0) return;
Debug.Log("Loading _preload scene");
otherScene = sceneIndex;
//make sure your _preload scene is the first in scene build list
SceneManager.LoadScene(0);
}
#endif
}
Затем в вашей _preload-сцене у вас есть другой скрипт, который загрузит нужную сцену (с которой вы начали):
...
#if UNITY_EDITOR
private void Awake()
{
if (LoadingSceneIntegration.otherScene > 0)
{
Debug.Log("Returning again to the scene: " + LoadingSceneIntegration.otherScene);
SceneManager.LoadScene(LoadingSceneIntegration.otherScene);
}
}
#endif
...