Widziałem kilka filmów i samouczków dotyczących tworzenia obiektów singletonów w Unity, głównie dla takich GameManager
, które wydają się wykorzystywać różne podejścia do tworzenia instancji i sprawdzania singletona.
Czy istnieje właściwe, a raczej preferowane podejście do tego?
Dwa główne przykłady, które napotkałem, to:
Pierwszy
public class GameManager
{
private static GameManager _instance;
public static GameManager Instance
{
get
{
if(_instance == null)
{
_instance = GameObject.FindObjectOfType<GameManager>();
}
return _instance;
}
}
void Awake()
{
DontDestroyOnLoad(gameObject);
}
}
druga
public class GameManager
{
private static GameManager _instance;
public static GameManager Instance
{
get
{
if(_instance == null)
{
instance = new GameObject("Game Manager");
instance.AddComponent<GameManager>();
}
return _instance;
}
}
void Awake()
{
_instance = this;
}
}
Główną różnicą między nimi jest:
Pierwsze podejście będzie polegało na przejściu przez stos obiektów gry w celu znalezienia instancji, GameManager
która choć zdarza się to (lub powinna się zdarzyć) tylko raz, wydaje się, że może być bardzo niezoptymalizowana, ponieważ sceny powiększają się podczas tworzenia.
Pierwsze podejście oznacza również, że obiekt nie będzie usuwany, gdy aplikacja zmieni scenę, co zapewnia zachowanie obiektu między scenami. Wydaje się, że drugie podejście się do tego nie stosuje.
Drugie podejście wydaje się dziwne, ponieważ w przypadku, gdy instancja jest pusta w module pobierającym, utworzy nowy GameObject i przypisze do niego komponent GameManger. Jednak nie można tego uruchomić bez uprzedniego podłączenia tego komponentu GameManager do obiektu w scenie, co powoduje pewne zamieszanie.
Czy są jakieś inne podejścia, które byłyby zalecane, lub hybryda dwóch powyższych? Istnieje wiele filmów i samouczków dotyczących singletonów, ale wszystkie różnią się tak bardzo, że trudno jest dokonać porównania między nimi, a tym samym stwierdzić, który z nich jest najlepszym / preferowanym podejściem.
źródło
GameManager
należy zrobić, a raczej jak zapewnić, że istnieje tylko jedna instancja obiektu i najlepszy sposób, aby to wymusić.Odpowiedzi:
To zależy, ale zwykle używam trzeciej metody. Problem z zastosowanymi metodami polega na tym, że jeśli obiekt zostanie dołączony na początku, nie usunie go z drzewa, a nadal można je utworzyć, tworząc instancję zbyt wielu wywołań, co może sprawić, że wszystko będzie mylące.
Problemem obu twoich implementacji jest to, że nie niszczą one obiektu, który jest tworzony później. To może działać, ale można wrzucić klucz małpi do prac, co może spowodować bardzo trudny do debugowania błąd w linii. Pamiętaj, aby zaznaczyć opcję Przebudź, jeśli istnieje już instancja, a jeśli tak, zniszcz nową instancję.
źródło
OnDestroy() { if (this == _instance) { _instance = null; } }
, jeśli chcesz mieć inną instancję w każdej scenie.Instance
właściwość static zostaje wyczyszczona. Przykład takiego, którego nie można znaleźć w jednej z poniższych odpowiedzi , lub wiki.unity3d.com/index.php/Singleton (co może być przestarzałe, ale wydaje się, że działa z moich eksperymentów z nim)Oto krótkie podsumowanie:
Jeśli więc zależy Ci tylko na globalnym dostępie, wszystkie trzy dostaną to, czego potrzebujesz. Użycie wzorca Singleton może być nieco dwuznaczne, czy chcemy leniwej instancji, wymuszonej wyjątkowości czy globalnego dostępu, więc dobrze się zastanów, dlaczego sięgasz po singleton, i wybierz implementację, która odpowiednio dostosowuje te funkcje. niż stosowanie standardu dla wszystkich trzech, gdy potrzebujesz tylko jednego.
(np. jeśli moja gra zawsze będzie miała GameManagera, może nie dbam o leniwe tworzenie instancji - może to tylko globalny dostęp z gwarantowanym istnieniem i wyjątkowością, na której mi zależy - w takim przypadku klasa statyczna uzyskuje dokładnie te cechy bardzo zwięźle, bez uwag na temat ładowania sceny)
... ale zdecydowanie nie używaj Metody 1 jak napisano. Znalezienie można łatwiej pominąć dzięki metodzie Awake () metody Method2 / 3, a jeśli utrzymujemy menedżera między scenami, bardzo prawdopodobne jest, że chcemy zabić duplikaty, na wypadek, gdybyśmy kiedykolwiek załadowali się między dwiema scenami z menedżerem już w nich zawartym.
źródło
Najlepsze wdrożenie ogólnego
Singleton
wzorca dla Unity, o którym wiem, to (oczywiście) moje własne.Potrafi zrobić wszystko i robi to schludnie i skutecznie :
Inne zalety:
OnApplicationQuit()
. (I robi to z pojedynczą flagą globalną, zamiast każdego typu singletonu posiadającego własne)A ponieważ dzielenie się jest ważne, oto:
źródło
MonoBehaviour
i uważam, że każda klasa używana przez Unity ogólnie.SampleSingletonClass : Singleton
,SampleSingletonClass.Instance
wraca zSampleSingletonClass does not contain a definition for Instance
.Singleton<>
klasy ogólnej . Dlatego generic jest dzieckiemSingleton
klasy podstawowej .Chciałbym tylko dodać, że przydatne może być zadzwonienie,
DontDestroyOnLoad
jeśli chcesz, aby singleton trwał w różnych scenach.źródło
Inną opcją może być podzielenie klasy na dwie części: zwykłą klasę statyczną dla komponentu Singleton i MonoBehaviour, który działa jako kontroler dla instancji singleton. W ten sposób masz pełną kontrolę nad konstrukcją singletona i będzie on trwał w różnych scenach. Pozwala to również dodawać kontrolery do dowolnego obiektu, który może potrzebować danych singletona, zamiast konieczności przekopywania sceny w celu znalezienia określonego komponentu.
źródło
Oto moja implementacja singletonowej klasy abstrakcyjnej poniżej. Oto jak wypada w porównaniu z 4 kryteriami
Ma kilka innych zalet w porównaniu do niektórych innych metod tutaj:
FindObjectsOfType
który jest zabójcą wydajnościJest bezpieczny dla wątków
źródło
OnApplicationQuitCallback
iOnEnableCallback
jakabstract
zamiast tylko pustymivirtual
metodami? Przynajmniej w moim przypadku nie mam logiki wyjścia / włączenia, a puste zastąpienie jest brudne. Ale coś może mi umknąć.OnApplicationQuitCallback
aOnEnableCallback
: posiadanie go jako metod wirtualnych sprawia, że jest to mniej oczywiste. Może nieco dziwne myślenie, ale o ile pamiętam, było to moje racjonalne podejście.W rzeczywistości istnieje pseudo oficjalny sposób korzystania z Singleton w Unity. Oto wyjaśnienie, w zasadzie stwórz klasę Singleton i spraw, aby twoje skrypty dziedziczyły po tej klasie.
źródło
Zaimplementuję też moje wdrożenie dla przyszłych pokoleń.
Dla mnie ta linia
Destroy(gameObject.GetComponent(instance.GetType()));
jest bardzo ważna, ponieważ po pozostawieniu skryptu singletonowego w innej grze Object w scenie cały obiekt gry był usuwany. Spowoduje to zniszczenie komponentu, jeśli już istnieje.źródło
Napisałem klasę singletonów, która ułatwia tworzenie obiektów singletonów. Jest to skrypt MonoBehaviour, więc możesz używać Coroutines. Opiera się na tym artykule Unity Wiki , a później dodam opcję utworzenia go z prefabrykatu.
Więc nie musisz pisać kodów Singleton. Wystarczy pobrać tę klasę podstawową Singleton.cs , dodać ją do projektu i utworzyć singleton, rozszerzając ją:
Teraz twoja klasa MySingleton jest singletonem i możesz ją nazwać instancją:
Oto kompletny samouczek: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/
źródło