Mit ScriptableObjects Variablen kannst du in deinem Unity Projekt Abhängigkeiten vermeiden und die meisten Singletons abschaffen.
Das Thema ScriptableObjects Variablen hat Ryan Hipple auf der Unite 2017 im Video über „Game Architecture with ScriptableObjects“ vorgestellt.
Inhaltsverzeichnis
Singleton-Abhängigkeit entfernen
Das Singleton-Modell ist ein häufiger Ansatz in der Unity-Spieleentwicklung, birgt jedoch viele Probleme. Eine Abhängigkeit von Singletons entsteht, wenn ein Objekt von dem Zustand eines anderen Objekts abhängig ist (seinem statischen Attribut). Dies führt zu schwer zu verfolgenden Fehlern aufgrund von Null-Referenz-Ausnahmen und erschwert oder macht oft das Testen von Einheiten unmöglich. Es kann auch zu Zugriffsproblemen führen, indem es einen logischen Verweispfad jenseits des normalerweise zulässigen Bereichs erstellt.
Scriptable Variablen entfernen die Notwendigkeit für die meisten, wenn nicht sogar alle Singletons, indem sie die Daten an einem gegenseitig zugänglichen Ort speichern.
Nehmen wir als Beispiel „Player Health“: sowohl der Spielcharakter als auch die Spiel-UI müssen darauf Zugriff haben. Man könnte die Daten auf dem Spielcharakter definieren und diesen als Singleton verwenden und die Spiel-UI darauf verweisen. Dadurch entsteht jedoch eine Abhängigkeit zwischen dem Spielcharakter und der Spiel-UI. Es bedeutet auch, dass die Spiel-UI jetzt Methoden auf dem Spielcharakter ausführen kann, da sie einen Verweis darauf hat. Dies fördert schlechtes Design, erschwert das Testen der Spiel-UI ohne den Spielcharakter und bedeutet, dass sowohl der Spielcharakter als auch die Spiel-UI in der gleichen Szene definiert sein müssen oder zur Laufzeit mittels einer kostspieligen Suche referenziert werden müssen.
Mit Scriptable Variablen könnte man „Player Health“ als Variable in der Asset-Datenbank definieren und sowohl Spielcharakter als auch Spiel-UI auf diese Variable verweisen. Jetzt sind weder der Charakter noch die UI voneinander abhängig. Sie können in separaten Szenen definiert werden und sogar in mehreren Szenen gleichzeitig verwendet werden. Noch wichtiger ist, dass die Scriptable Variable meldet, wenn seine Daten geändert wurden, sodass man keine Update-Schleife verwenden muss, um auf Datenänderungen zu reagieren. Man könnte stattdessen einen eventbasierten Ansatz wählen.
Datenduplizierung reduzieren
Ein häufiges Problem bei Spiel-Datenmodellen ist die Duplizierung von Daten. Oft werden die gleichen Werte in mehreren Orten berechnet oder gespeichert und müssen bei Änderungen synchronisiert werden.
Ein Beispiel dafür könnte die Farbe des Spielers in der Benutzeroberfläche sein. Normalerweise würde man diesen Wert in jeder Benutzeroberflächen-Steuerung speichern, die ihn verwendet. Wenn sich der Wert ändert, müsste man also alle Verweise darauf aktualisieren.
Eine andere Möglichkeit wäre, einen Singleton zu verwenden, der den Wert hält. Das führt jedoch zu einer großen und weitreichenden Abhängigkeit, die das Testen von Einheiten erschwert oder unmöglich macht. Schließlich könnte man ein System erstellen, um den Wert aus den App-Einstellungen oder ähnlichem zu lesen, aber das führt zu einer hohen Verarbeitungsbelastung für alles, was ihn verwendet und skaliert deshalb schlecht.
ScriptableObjects bieten hier eine einfache, effiziente und robuste Lösung. Man kann solche globalen oder häufig verwendeten Datenpunkte als ScriptableObjects definieren und sie in mehreren Szenen verwenden, auf Änderungen reagieren und sicherstellen, dass es keine Duplizierung dieser Daten gibt und dass alle Nutzer immer über den aktuellsten Wert informiert sind.
Szenenübergreifende Referenzierung
In einem Spiel mit mehreren Szenen tritt häufig das Problem auf, dass der Unity Editor keine Referenzen zwischen Szenen auflösen kann, d.h. man kann während der Entwicklung kein Objekt in Szene A von Szene B aus referenzieren.
Diese Einschränkung gilt jedoch nicht für ScriptableObjects, da sie in der Asset-Datenbank definiert sind und somit immer „in Bereich“ sind.
ScriptableObjects können leicht in mehreren Szenen referenziert werden, was ein häufiges Problem bei der Struktur von mehrszenigen Spielen löst. Mit ScriptableObjects kann man Daten global verfügbar machen, ohne auf die Einschränkungen von Unity’s Editor angewiesen zu sein. Sie ermöglichen es, Daten einfach und effizient zu verwalten und sicherzustellen, dass alle Szenen immer Zugriff auf die aktuellsten Daten haben.
Immer verfügbar
Im Gegensatz zu der Singleton-Spielobjektstruktur gibt es bei Scriptable Variablen keine Notwendigkeit zu warten, bis sie initialisiert werden. Der Verweis auf die Scriptable Variable ist von dem Moment an verfügbar, in dem das Spiel in den Speicher geladen wird.
Dies ist im Gegensatz zur Singleton-Methode, bei der man eine statische Variable auf dem Spielobjekt erstellt, die erst beim Start des Objekts gesetzt wird. Das bedeutet, dass der Zeitraum zwischen der Initialisierung des Spiels und dem Start des Objekts, die Variable null ist.
ScriptableObjects ermöglichen es, Daten jederzeit und überall im Spiel zugänglich zu machen, ohne dass man auf eine Initialisierung warten muss. Sie sind auch einfach zu verwalten und zu organisieren, da sie in der Asset-Datenbank definiert sind und sich leicht in mehreren Szenen referenzieren lassen.
ScriptableObjects Variable erstellen
Das Script für eine ScriptableObject Variable ist sehr einfach, es die Klasse beinhaltet nur EINE kurze Zeile.
public float value;
es können auch andere Datentypen wie int
, bool
usw. verwendet werden.
Der Name der Variable ist einfach nur value
und nicht playerHealth
oder so, damit kann man dieses ScriptableObject für alle Variablen verwenden die man benötigt und greift immer mit dem selben Variablennamen darauf zu.
Hier sind zwei Beispiele für eine float
und eine int
Scriptable Variable:
[CreateAssetMenu] public class FloatVariable : ScriptableObject { public float value; }
[CreateAssetMenu] public class IntVariable : ScriptableObject { public int value; }
[CreateAssetMenu]
ist auch wichtig um später im Kontextmenü eine Variable zu erstellen.
Wenn man mehrere Datentypen als Variable verwendet kann es Sinn machen diese etwas eleganter im Kontextmenü unterzubringen.
[CreateAssetMenu(fileName = "Float Variable", menuName = "Variables/Float Variable", order = 0)] public class FloatVariable : ScriptableObject { public float value; }
Mit fileName = "Float Variable"
gibt man den Standardnamen bei der Erstellung an.
menuName = "Variables/Float Variable"
bedeutet, es gibt im Kontextmenü ein Untermenü namens Variables.
order = 0
gibt die Position im Kontextmenü an, in diesem Fall wird es ganz oben eingereiht.
ScriptableObject Variable verwenden
Um die Variable zu verwenden kannst man einfach [SerializeField] FloatVariable health;
verwenden und greift mit health.value
darauf zu.
public class Player : MonoBehaviour { [SerializeField] FloatVariable health; private void Start() { health.value = 100f; } }
Die selbe Variable kann man zum Beispiel im UI verwenden und einem Text mit health.value.ToString();
zuweisen.
public class UI : MonoBehaviour { [SerializeField] FloatVariable health; [SerializeField] Text healthText; private void Start() { healthText.text = health.value.ToString(); } }
Erweiterte Möglichkeiten
Um flexibler zu testen kann man ein weiteres Script erstellen, dieses stellt die Referenz zu der Variable zur verfügung.
Wenn man dieses Script für die UI verwendet, kann man wie gewohnt auf die Scriptable Variable zugreifen, oder man aktiviert im Unity Editor useConstant
und man kann den Wert von constantValue
den man im Editor eingibt.
[Serializable] public class FloatVariableReference { [SerializeField] bool useConstant; [SerializeField] float constantValue; [SerializeField] FloatVariable variable; public float Value { get { return useConstant ? constantValue : variable.value; } } }
Fazit
Das Fazit zum Thema ScriptableObjects Variablen in Unity ist, dass sie eine leistungsfähige Lösung für die Datenverwaltung und -organisation in Spielen darstellen. Sie ermöglichen es, Abhängigkeiten zu vermeiden und die meisten Singletons abzuschaffen.
Ryan Hipple hat das Thema ScriptableObjects Variablen auf der Unite 2017 im Video über „Game Architecture with ScriptableObjects“ vorgestellt und gezeigt, wie sie die Probleme des Singleton-Modells lösen können. Singletons erzeugen Abhängigkeiten, die schwer zu verfolgende Fehler verursachen und das Testen von Einheiten erschweren oder unmöglich machen können. Scriptable Variables speichern die Daten an einem gegenseitig zugänglichen Ort und entfernen die Notwendigkeit für die meisten Singletons.
Scriptable Variables ermöglichen auch die Reduzierung von Datenduplizierung. Oft werden die gleichen Werte in mehreren Orten berechnet oder gespeichert und müssen bei Änderungen synchronisiert werden. Scriptable Variablen definieren solche globalen oder häufig verwendeten Datenpunkte und ermöglichen es, sie in mehreren Szenen zu verwenden, auf Änderungen zu reagieren und sicherzustellen, dass es keine Duplizierung dieser Daten gibt und dass alle Nutzer immer über den aktuellsten Wert informiert sind.
ScriptableObjects Variablen bieten in Unity eine leistungsfähige Lösung für die Datenverwaltung und -organisation, die es ermöglicht, Abhängigkeiten zu vermeiden und die meisten Singletons abzuschaffen sowie die Reduzierung von Datenduplizierung. Sie ermöglichen eine einfache, effiziente und robuste Verwaltung von Daten und sicherstellen, dass alle Szenen immer Zugriff auf die aktuellsten Daten haben.