Подтвердить что ты не робот

Unity игровой менеджер. Script работает только один раз

Я делаю простой игровой менеджер. У меня есть script, который будет доступен из всех сцен в игре. И мне нужно проверить значения его переменных после загрузки новой сцены. Но мой код запускается только один раз после запуска моделирования, тогда как объект с этим script существует во всех сценах. Что не так? Почему он не работает после загрузки новой сцены?

4b9b3361

Ответ 1

В каждом проекте Unity у вас должна быть ПРЕДВАРИТЕЛЬНАЯ СЦЕНА.

Весьма странно, что Unity не имеет встроенной сцены предзагрузки.

Они добавят эту концепцию в будущем.

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

Это так просто.

Это ЕДИНСТВЕННАЯ БОЛЬШАЯ ОШИБКА для новых программистов, пробующих Unity!


К счастью, предзагрузить сцену очень легко.

Шаг 1.

Создайте сцену с именем "preload". Это должна быть сцена 0 в Build Manager.

enter image description here

Шаг 2.

В сцене "предварительной загрузки" создайте пустой GameObject с именем, скажем, "__app".

Просто поместите DontDestroyOnLoad в "__app".

Замечания:

Это единственное место во всем проекте, где вы используете DontDestroyOnLoad.

Это так просто.

enter image description here

В примере: разработчики сделали однострочный DDOL-скрипт.

Поместите этот скрипт в объект "__app".

Вам больше никогда не придется думать о DDOL.

Шаг 3

Ваше приложение будет иметь (много) "общих поведений". Таким образом, такие вещи, как подключение к базе данных, звуковые эффекты, оценка и так далее.

Вы должны и только можете поместить свое общее поведение в "_app".

Это действительно так просто.

Общее поведение тогда - конечно - доступно везде в проекте, всегда и во всех сценах.

Как еще ты мог это сделать?

В приведенном выше примере изображения обратите внимание на "Iap" ("покупка в приложении") и другие.

Все ваши "общепринятые поведения" - звуковые эффекты, оценки и т.д. - прямо на этом объекте.

Важный...

Это означает, что - конечно, естественно -

... у вашего общего поведения, конечно, будут обычные инспекторы, как и все в Unity.

Вы можете использовать все обычные функции Unity, которые вы используете на любом другом игровом объекте.

Конечно, вы можете использовать обычные переменные инспектора (перетащите для подключения), настройки и так далее.

(Действительно: допустим, вы были наняты для работы над существующим проектом. Первое, что вы сделаете, это взгляните на сцену с предварительной загрузкой. Вы увидите все "общие характеристики" проекта в одном месте. Звуковые эффекты, оценка, AI и т.д. Вы сразу увидите все настройки для этих вещей как переменные инспектора... громкость речи, идентификатор игрового магазина и т.д.)

Вот пример "Звуковые эффекты" общего поведения:

enter image description here

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

Повторить. Относительно вашего "общего" поведения. (Звуковые эффекты, озвучивание, общение и т.д.). Они МОГУТ ТОЛЬКО ИДЕТ на игровой объект в предзагрузочной сцене.

Это не обязательно: другой альтернативы нет!

Вы сделали

Это честно так просто. Там нет буквально ничего!

Многим инженерам из других жанров всегда нужно понимать, что это так просто, потому что это так просто и легко, что "не может быть правдой".

Просто повторюсь: вся проблема в том, что (причудливо) 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
...