В Unity, что является хорошим способом создания однопользовательского игрового менеджера, к которому можно получить доступ во всем мире как к глобальному классу со статическими переменными, которые будут выплевывать одни и те же константные значения для каждого класса, который извлекает эти значения? И каков был бы способ реализации в Unity? Нужно ли прикреплять его к GameObject? Может ли он быть в папке без визуального отображения?
Классы менеджеров Unity singleton
Ответ 1
Как всегда: это зависит. Я использую одноэлементы обоих видов, компоненты, прикрепленные к GameObject
, и автономные классы, не полученные из MonoBehaviour
. IMO - общий вопрос: каким образом экземпляры привязаны к жизненному циклу сцен, игровых объектов... И не забывайте, что иногда удобнее иметь компонент, особенно ссылающийся на другие объекты MonoBehaviour
, проще и безопаснее.
- Существуют классы, которые просто должны предоставлять некоторые значения, например, например, класс конфигурации, который должен загружать настройки из уровня персистентности при вызове. Я разрабатываю эти классы как простые синглтоны.
- С другой стороны, некоторые объекты должны знать, когда сцена запущена, т.е.
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;
}
}
Ответ 2
Инженеры, которые новичок в Unity, часто не замечают, что
у вас не может быть "singleton" в системе ECS.
Это бессмысленно.
Все, что у вас есть в Unity, - это GameObjects, at, XYZ. Они могут иметь компоненты.
Это будет похоже на попытку "одиночного" или "наследования" в... Photoshop или Microsoft Word.
Photoshop - пиксели в положениях XY
Текстовый редактор - буквы в положениях X
Unity - GameObjects на позициях XYZ
Это просто так просто.
Конечно, в любом проекте Unity вам обязательно нужно иметь предварительную сцену.
Невероятно простой способ: fooobar.com/questions/182020/...
Это так просто, это не проблема.
Когда Unity включает в себя "встроенную сцену предварительной загрузки" (то есть, чтобы сохранить один щелчок при создании одного), это, наконец, никогда не будет обсуждаться снова.
(Примечание. A - некоторые языки, которые вы используете для компиляции компонентов для Unity, конечно, имеют концепции OO; сам Unity не имеет никакого отношения к OO вообще.)
(Примечание B - в первые дни Unity вы увидите попытки создать код, который "создает игровой объект" на лету ", и сохраняет его уникальным - и присоединяется к нему". Помимо причудливого, просто FWIW теоретически не представляется возможным обеспечить уникальность (на самом деле даже не в пределах кадра). Опять же, это просто совершенно спорно потому что это тривиальное не проблема, в единстве общего поведения просто пойти на преднагрузки сцене.)
Ответ 3
Если этот класс предназначен только для доступа к глобальным переменным, вам не нужен шаблон singleton для этого или использовать GameObject.
Просто создайте класс с общедоступными статическими членами.
public class Globals
{
public static int mStatic1 = 0;
public static float mStatic2 = 0.0f;
// ....etc
}
Другие решения прекрасны, но излишнее, если вам нужен глобальный доступ к переменным.
Ответ 4
Я написал одноэлементный класс, который упрощает создание одноэлементных объектов. Это MonoBehaviour script, поэтому вы можете использовать Coroutines. Он основан на этой статье Wiki Unity, и я добавлю вариант для ее создания из Prefab позже.
Поэтому вам не нужно писать коды Singleton. Просто скачайте этот базовый класс Singleton.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/
Ответ 5
Это настройка, которую я создал.
Сначала создайте этот script:
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;
}
Ответ 6
Один из способов сделать это - создать сцену просто для инициализации игрового менеджера, например так:
public class GameManager : MonoBehaviour {
static GameManager instance;
//other codes
void Awake() {
DontDestroyOnLoad(transform.gameObject);
instance = this;
}
//other codes
}
Вот и все, что вам нужно сделать. А затем сразу же после инициализации игрового менеджера загрузите следующую сцену и больше никогда не возвращайтесь на эту сцену.
Посмотрите этот учебник: https://youtu.be/64uOVmQ5R1k?list=WL
Изменить: Изменен GameManager static instance;
в static GameManager instance;