Классы Unity singleton manager
в Unity, каков хороший способ создать одноэлементный игровой менеджер, к которому можно получить доступ везде как к глобальному классу со статическими переменными, которые будут выплевывать одинаковые постоянные значения для каждого класса, который тянет эти значения? И как бы это реализовать в единстве? Должен ли я прикрепить его к GameObject? Может ли он просто быть в папке, не находясь в сцене визуально?
6 ответов
как всегда: это зависит. Я использую синглеты обоих видов, компоненты, прикрепленные к GameObject
и автономные классы, не производные от MonoBehaviour
. ИМО общий вопрос-как случаи, привязанные к lifcycle сцен, игровых объектов, ... И не забывать иногда удобнее иметь компонент, особенно ссылающийся на другие MonoBehaviour
объекты проще и безопаснее.
- есть классы, которые просто должны предоставить некоторые значения, такие как, например, config класс, который должен загружать параметры из слоя сохраняемости при вызове. Я проектирую эти классы как простые синглеты.
- С другой стороны, некоторые объекты должны знать, когда начинается сцена, т. е.
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