Ich habe mehrere Videos und Tutorials zum Erstellen von Singleton-Objekten in Unity gesehen, hauptsächlich für a GameManager
, die unterschiedliche Ansätze zum Instanziieren und Validieren eines Singleton zu verwenden scheinen.
Gibt es einen richtigen oder eher bevorzugten Ansatz dafür?
Die beiden wichtigsten Beispiele, die ich angetroffen habe, sind:
Zuerst
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);
}
}
Zweite
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;
}
}
Der Hauptunterschied, den ich zwischen den beiden sehe, ist:
Der erste Ansatz versucht, im Spielobjektstapel zu navigieren, um eine Instanz der zu finden GameManager
, obwohl dies nur einmal vorkommt (oder sollte), als ob es sehr unoptimiert erscheinen könnte, wenn Szenen während der Entwicklung an Größe zunehmen.
Außerdem markiert der erste Ansatz das Objekt, das nicht gelöscht werden soll, wenn die Anwendung die Szene ändert, wodurch sichergestellt wird, dass das Objekt zwischen den Szenen beibehalten wird. Der zweite Ansatz scheint dem nicht zu entsprechen.
Der zweite Ansatz erscheint seltsam, da in dem Fall, in dem die Instanz im Getter null ist, ein neues GameObject erstellt und ihm eine GameManger-Komponente zugewiesen wird. Dies kann jedoch nicht ausgeführt werden, ohne dass diese GameManager-Komponente zuvor bereits an ein Objekt in der Szene angehängt wurde, was mich verwirrt.
Gibt es andere Ansätze, die empfohlen würden, oder eine Mischung aus beiden? Es gibt viele Videos und Tutorials zu Singletons, aber sie unterscheiden sich alle so sehr, dass es schwierig ist, Vergleiche zwischen den beiden zu ziehen, und somit eine Schlussfolgerung darüber zu ziehen, welches der beste / bevorzugte Ansatz ist.
quelle
GameManager
soll, sondern wie sichergestellt werden kann, dass es immer nur eine Instanz des Objekts gibt und wie dies am besten durchgesetzt werden kann.Antworten:
Das hängt davon ab, aber normalerweise verwende ich eine dritte Methode. Das Problem bei den von Ihnen verwendeten Methoden besteht darin, dass das Objekt in dem Fall, in dem es zunächst enthalten ist, nicht aus der Struktur entfernt wird. Sie können dennoch durch das Instanziieren von zu vielen Aufrufen erstellt werden, was zu Verwirrung führen kann.
Das Problem bei beiden Implementierungen ist, dass sie kein Objekt zerstören, das später erstellt wird. Es könnte funktionieren, aber man könnte einen Schraubenschlüssel in das Werk werfen, was zu einem sehr schwer zu behebenden Fehler führen könnte. Stellen Sie sicher, dass Sie in Awake prüfen, ob bereits eine Instanz vorhanden ist, und in diesem Fall die neue Instanz zerstören.
quelle
OnDestroy() { if (this == _instance) { _instance = null; } }
, wenn Sie in jeder Szene eine andere Instanz haben möchten.Instance
erneutes Laden des Editors nicht überlebt, da die statische Eigenschaft gelöscht wird , oder wiki.unity3d.com/index.php/Singleton (was zwar veraltet sein könnte, aber von meinem Experimentieren damit zu funktionieren scheint)Hier ist eine kurze Zusammenfassung:
Wenn Sie sich also nur für den globalen Zugriff interessieren, erhalten Sie mit allen drei Funktionen das, was Sie benötigen. Die Verwendung des Singleton-Musters kann ein bisschen zweideutig sein, ob es sich um eine verzögerte Instanziierung, eine erzwungene Eindeutigkeit oder einen globalen Zugriff handelt. Überlegen Sie sich also genau, warum Sie nach dem Singleton greifen , und wählen Sie eine Implementierung, die diese Funktionen richtig ausführt als einen Standard für alle drei zu verwenden, wenn Sie nur einen benötigen.
(zB wenn mein Spiel immer einen GameManager haben wird, interessiert mich die verzögerte Instanziierung vielleicht nicht - vielleicht ist es nur ein globaler Zugriff mit garantierter Existenz und Einzigartigkeit, die mir wichtig sind - in diesem Fall bringt mir eine statische Klasse genau diese Funktionen sehr präzise, ohne Überlegungen zum Laden der Szene)
... aber auf keinen Fall Methode 1 wie geschrieben anwenden. Die Suche kann mit dem Awake () - Ansatz von Methode 2/3 einfacher übersprungen werden. Wenn wir den Manager über Szenen hinweg beibehalten, möchten wir höchstwahrscheinlich das Duplizieren, falls wir jemals zwischen zwei Szenen laden, in denen bereits ein Manager vorhanden ist.
quelle
Die beste Implementierung eines generischen
Singleton
Musters für Unity, von dem ich weiß, ist (natürlich) meine eigene.Es kann alles und das ordentlich und effizient :
Andere Vorteile:
OnApplicationQuit()
. (Und das mit einem einzigen globalen Flag, anstatt dass jeder Singleton-Typ seinen eigenen hat.)Und weil Teilen wichtig ist , ist es hier:
quelle
MonoBehaviour
, und ich glaube, dass jede Klasse von Unity direkt im Allgemeinen verwendet wird.SampleSingletonClass : Singleton
mitSampleSingletonClass.Instance
zurückSampleSingletonClass does not contain a definition for Instance
.Singleton<>
Klasse verwenden. Deshalb ist das Generikum ein Kind der BasisklasseSingleton
.Ich möchte nur hinzufügen, dass es nützlich sein kann, anzurufen,
DontDestroyOnLoad
wenn Ihr Singleton über Szenen hinweg bestehen bleiben soll.quelle
Eine weitere Möglichkeit besteht darin, die Klasse in zwei Teile zu unterteilen: eine reguläre statische Klasse für die Singleton-Komponente und ein MonoBehaviour, das als Controller für die Singleton-Instanz fungiert. Auf diese Weise haben Sie die volle Kontrolle über die Konstruktion des Singletons und diese bleibt über Szenen hinweg erhalten. Auf diese Weise können Sie auch jedem Objekt Controller hinzufügen, für das möglicherweise die Daten des Singletons erforderlich sind, anstatt die Szene durchsuchen zu müssen, um eine bestimmte Komponente zu finden.
quelle
Hier ist meine Implementierung einer abstrakten Singleton-Klasse. Hier ist, wie es gegen die 4 Kriterien stapelt
Es hat ein paar andere Vorteile im Vergleich zu einigen anderen Methoden:
FindObjectsOfType
was ein Leistungskiller istEs ist threadsicher
quelle
OnApplicationQuitCallback
undOnEnableCallback
wieabstract
statt nur leerevirtual
Methoden? Zumindest in meinem Fall habe ich keine Quit / Enable-Logik und ein leerer Override fühlt sich schmutzig an. Aber mir könnte etwas fehlen.OnApplicationQuitCallback
undOnEnableCallback
: es als virtuelle Methode zu haben, macht es weniger offensichtlich. Vielleicht ein bisschen seltsam, aber soweit ich mich erinnere, war das meine Vernunft.Es gibt tatsächlich eine pseudo-offizielle Möglichkeit, Singleton in Unity zu verwenden. Hier ist die Erklärung: Erstellen Sie im Grunde genommen eine Singleton-Klasse und lassen Sie Ihre Skripte von dieser Klasse erben.
quelle
Ich werde meine Implementierung auch für zukünftige Generationen versuchen.
Für mich ist diese Zeile
Destroy(gameObject.GetComponent(instance.GetType()));
sehr wichtig, da ich einmal ein Singleton-Skript für ein anderes gameObject in einer Szene hinterlassen hatte und das gesamte Spielobjekt gelöscht wurde. Dadurch wird die Komponente nur dann zerstört, wenn sie bereits vorhanden ist.quelle
Ich habe eine Singleton-Klasse geschrieben, mit der Singleton-Objekte einfach erstellt werden können. Es ist ein MonoBehaviour-Skript, sodass Sie die Coroutinen verwenden können. Es basiert auf diesem Unity-Wiki-Artikel und ich werde die Option hinzufügen, es später aus Prefab zu erstellen.
Sie müssen also nicht die Singleton-Codes schreiben. Laden Sie einfach diese Singleton.cs-Basisklasse herunter , fügen Sie sie Ihrem Projekt hinzu und erstellen Sie eine Singleton-Erweiterung:
Jetzt ist Ihre MySingleton-Klasse ein Singleton und Sie können sie nach Instanz aufrufen:
Hier ist ein vollständiges Tutorial: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/
quelle