Классы Unity singleton manager

в Unity, каков хороший способ создать одноэлементный игровой менеджер, к которому можно получить доступ везде как к глобальному классу со статическими переменными, которые будут выплевывать одинаковые постоянные значения для каждого класса, который тянет эти значения? И как бы это реализовать в единстве? Должен ли я прикрепить его к GameObject? Может ли он просто быть в папке, не находясь в сцене визуально?

6 ответов


как всегда: это зависит. Я использую синглеты обоих видов, компоненты, прикрепленные к GameObject и автономные классы, не производные от MonoBehaviour. ИМО общий вопрос-как случаи, привязанные к lifcycle сцен, игровых объектов, ... И не забывать иногда удобнее иметь компонент, особенно ссылающийся на другие MonoBehaviour объекты проще и безопаснее.

  1. есть классы, которые просто должны предоставить некоторые значения, такие как, например, config класс, который должен загружать параметры из слоя сохраняемости при вызове. Я проектирую эти классы как простые синглеты.
  2. С другой стороны, некоторые объекты должны знать, когда начинается сцена, т. е. Start вызывается или должны выполнять действия в Update или другими методами. Затем я реализую их как компонент и прикрепляю их к игровому объекту, который выживает при загрузке новых сцен.

Я разработал синглеты на основе компонентов (тип 2) с двумя частями: постоянный GameObject называется Main, который содержит все компоненты и плоский синглтон (тип 1) под названием MainComponentManager для управления ею. Некоторые демо-код:

public class MainComponentManger {
    private static MainComponentManger instance;
    public static void CreateInstance () {
        if (instance == null) {
            instance = new MainComponentManger ();
            GameObject go = GameObject.Find ("Main");
            if (go == null) {
                go = new GameObject ("Main");
                instance.main = go;
                // important: make game object persistent:
                Object.DontDestroyOnLoad (go);
            }
            // trigger instantiation of other singletons
            Component c = MenuManager.SharedInstance;
            // ...
        }
    }

    GameObject main;

    public static MainComponentManger SharedInstance {
        get {
            if (instance == null) {
                CreateInstance ();
            }
            return instance;
        }
    }

    public static T AddMainComponent <T> () where T : UnityEngine.Component {
        T t = SharedInstance.main.GetComponent<T> ();
        if (t != null) {
            return t;
        }
        return SharedInstance.main.AddComponent <T> ();
    }

теперь другие синглеты, которые хотят зарегистрироваться как Main компонент просто выглядит так:

public class AudioManager : MonoBehaviour {
    private static AudioManager instance = null;
    public static AudioManager SharedInstance {
        get {
            if (instance == null) {
                instance = MainComponentManger.AddMainComponent<AudioManager> ();
            }
            return instance;
        }
    }

инженеры, которые новички в Unity, часто не замечают этого

вы не можете иметь "синглтон" в системе ECS.

Это бессмысленно.

все, что у вас есть в Unity, это GameObjects, at, XYZ позиции. Они могут иметь прикрепленные компоненты.

Это было бы похоже на попытку иметь "синглтон" или "наследование".... Photoshop или Microsoft Word.

Photoshop файл-пиксели в XY позиции
текст редактор файл-Буквы в X позициях
единство файл-GameObjects в позиции XYZ

Это "просто".

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

невероятно простой способ:https://stackoverflow.com/a/35891919/294884

Это так просто, это не проблема.

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

(Примечание A -некоторые языки, используемые для компиляции компонентов

(Примечание B-в первые дни Unity вы увидите попытки создания кода, который "создает игровой объект на лету-и сохраняет его уникальным - и присоединяется к нему". Помимо того, что странно, просто fwiw теоретически невозможно обеспечить уникальность (на самом деле даже не в рамках кадра). Опять же, это просто полностью спорно, потому что это тривиальная не проблема, в Unity общие модели поведения просто идут на сцену предварительной загрузки.)


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

просто создайте класс с общедоступными статическими членами.

public class Globals
{
    public static int mStatic1 = 0;
    public static float mStatic2 = 0.0f;
    // ....etc
}

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


Я написал одноэлементный класс, который упрощает создание одноэлементных объектов. Это сценарий MonoBehaviour, поэтому вы можете использовать Coroutines. Его на основе этого Unity Wiki статья, и я добавлю опцию, чтобы создать его из Prefab позже.

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

public class MySingleton : Singleton<MySingleton> {
  protected MySingleton () {} // Protect the constructor!

  public string globalVar;

  void Awake () {
      Debug.Log("Awoke Singleton Instance: " + gameObject.GetInstanceID());
  }
}

теперь ваш класс MySingleton является Синглтон, и Вы можете назвать его экземпляром:

MySingleton.Instance.globalVar = "A";
Debug.Log ("globalVar: " + MySingleton.Instance.globalVar);

вот полный учебник:http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/


это настройка, которую я создал.

сначала создайте этот скрипт:

MonoBehaviourUtility.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

static public class MonoBehaviourUtility 
{

    static public T GetManager<T>( ref T manager ) where T : MonoBehaviour
    {
        if (manager == null)
        {
            manager = (T)GameObject.FindObjectOfType( typeof( T ) );
            if (manager == null)
            {
                GameObject gameObject = new GameObject( typeof( T ).ToString() );
                manager = (T)gameObject.AddComponent( typeof( T ) );
            }
        }
        return manager;
    }

}

тогда в любом классе, который вы хотите быть синглтоном, сделайте следующее:

public class ExampleManager : MonoBehaviour 
{   
    static public ExampleManager sharedManager 
    {
        get 
        {
            return MonoBehaviourUtility.GetManager<ExampleManager>( ref _sharedManager );
        }
    }   
    static private ExampleManager _sharedManager;       
}

один из способов сделать это-сделать сцену, чтобы инициализировать ваш игровой менеджер следующим образом:

public class GameManager : MonoBehaviour {
    GameManager static instance;

    //other codes

    void Awake() {
        DontDestroyOnLoad(transform.gameObject);
        instance = this;
    }

    //other codes
}

Это все, это все, что вам нужно сделать. А затем сразу после инициализации game manager загрузите следующую сцену и больше никогда не возвращайтесь к этой сцене.

взгляните на этот учебник: https://youtu.be/64uOVmQ5R1k?list=WL