
So verwenden Sie ein ScriptableObject-basiertes Laufzeitset
Diese Website wurde aus praktischen Gründen für Sie maschinell übersetzt. Die Richtigkeit und Zuverlässigkeit des übersetzten Inhalts kann von uns nicht gewährleistet werden. Sollten Sie Zweifel an der Richtigkeit des übersetzten Inhalts haben, schauen Sie sich bitte die offizielle englische Version der Website an.
Wichtiger Hinweis, bevor Sie beginnen
Bevor Sie sich in das ScriptableObject-Demoprojekt und diese Reihe von Mini-Leitfäden vertiefen, sollten Sie daran denken, dass Entwurfsmuster im Grunde nur Ideen sind. Sie werden nicht auf jede Situation zutreffen. Diese Techniken können Ihnen helfen, neue Wege für die Arbeit mit Unity und ScriptableObjects zu finden.
Jedes Muster hat Vor- und Nachteile. Wählen Sie nur diejenigen aus, die für Ihr spezifisches Projekt von Bedeutung sind. Sind Ihre Designer stark auf den Unity-Editor angewiesen? Ein ScriptableObject-basiertes Muster könnte eine gute Wahl sein, um die Zusammenarbeit mit Ihren Entwicklern zu erleichtern.
Letztendlich ist die beste Code-Architektur diejenige, die zu Ihrem Projekt und Ihrem Team passt.
Runtime-Sätze
ScriptableObjects können statische Daten speichern. Sie können so verändert werden, dass sie auch dynamische Daten enthalten. Dies ist besonders nützlich, um eine Sammlung von GameObjects oder Komponenten zur Laufzeit zu referenzieren.
Ein solches "Runtime Set" ist der Grund, warum manche Entwickler Singletons verwenden - einfacher globaler Zugriff. Leider bringen Singletons auch unerwünschte Abhängigkeiten mit sich. Diese zusätzlichen Abhängigkeiten können das Testen erschweren und die Fehlersuche erschweren.
Als Alternative können Sie ein ScriptableObject-basiertes Runtime Set für häufig referenzierte Komponenten verwenden. Verwenden Sie sie zum Beispiel, um Spielelemente wie gespawnte Feinde, Sammelobjekte oder Kontrollpunkte zu verfolgen.
Als Assets auf Projektebene bieten ScriptableObjects globalen Zugriff auf Daten für alle Objekte innerhalb einer Szene. Diese Funktion ermöglicht die gemeinsame Nutzung von Informationen ohne viele der Nachteile, die mit Singletons verbunden sind.
Grundlegende Umsetzung
Hier ist eine Möglichkeit, ein Runtime-Set für GameObjects zu erstellen. Stellen Sie es sich wie einen Datencontainer vor, der eine öffentliche Sammlung von Elementen enthält - aber auch Methoden zum Hinzufügen und Entfernen von Mitgliedern.
Zur Laufzeit kann jedes MonoBehaviour auf die öffentliche Eigenschaft Items verweisen, um die vollständige Liste der GameObjects zu erhalten. Ein anderes Script oder eine andere Komponente kann Add oder Remove aufrufen, um die Liste Items zu ändern.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "GameObject Runtime Set", fileName
= "GORuntimeSet")]
public class GameObjectRuntimeSetSO: ScriptableObject
{
private List<GameObject> items = new List<GameObject>();
public List<GameObject> Items => items;
public void Add(GameObject thingToAdd)
{
if (!items.Contains(thingToAdd))
items.Add(thingToAdd);
}
public void Remove(GameObject thingToRemove)
{
if (items.Contains(thingToRemove))
items.Remove(thingToRemove);
}
}
Generische Version
Sie können ein Laufzeitset spezifischer gestalten, so dass es nur einen bestimmten Typ von MonoBehaviour verfolgt. In diesem Fall sollten Sie für jeden Typ spezifische Sets erstellen (z. B. EnemyRuntimeSet, PickupRuntimeSet usw.).
Eine generische abstrakte Klasse kann diesen Prozess rationalisieren. Von dieser Basisklasse können Sie dann andere Laufzeitsets ableiten. Wenn Sie beispielsweise ein RuntimeSet für eine benutzerdefinierte Enemy-Komponente erstellen möchten, könnten Sie Klassen für ein generisches RuntimeSetSO<T> und ein konkretes EnemyRuntimeSetSO definieren, wie im folgenden Codeschnipsel.
Beachten Sie, dass die konkrete Klasse EnemyRuntimeSetSO keine Implementierungsdetails hat, sondern nur ein CreateAssetMenu-Attribut.
Für jede neue Komponente, die Sie verfolgen wollen, brauchen Sie nur ein entsprechendes Laufzeitset, um sie zu verwalten. Erstellen Sie so viele konkrete Klassen, wie Sie benötigen. Feinde, NPCs, Inventargegenstände, Quests und mehr können alle ihre eigenen Laufzeitsets haben.
public abstract class RuntimeSetSO<T>: ScriptableObject
{
public List<T> Items = new List<T>();
public void Add(T thing)
{
if (!Items.Contains(thing))
Items.Add(thing);
}
public void Remove(T thing)
{
if (Items.Contains(thing))
Items.Remove(thing);
}
}
[CreateAssetMenu(menuName = "Enemy Runtime Set", fileName =
"EnemyRuntimeSet")]
public class EnemyRuntimeSetSO: RuntimeSet<Enemy>
{
}

Verwendung und Einschränkungen
Die Verwaltung des Laufzeitsets ist Ihnen überlassen. Oft kann man ein zweites MonoBehaviour verwenden, um es während des Spiels einzurichten oder zu aktualisieren.
Wenn Sie möchten, dass sich eine Komponente automatisch in ein Set einfügt, können Sie die Methoden Add oder Remove des Laufzeitsets auch über OnEnable und OnDisable des MonoBehaviour aufrufen.
Ihre fiktive Klasse Enemy könnte zum Beispiel so aussehen wie der folgende Codeschnipsel.
Hier kann sich jede Enemy-Komponente mit ihren OnEnable- oder OnDisable-Methoden selbst hinzufügen oder entfernen. Wenn das Feld m_RuntimeSet im Inspector gesetzt ist, wird die Enemy-Komponente automatisch in der EnemyRuntimeSetSO erscheinen. Dies ist besonders praktisch, wenn Sie die Feindkomponente mit Prefabs verwenden.
Eine Einschränkung dieser Technik besteht darin, dass Sie den Inhalt der Liste " Elemente" im Inspektor nicht sehen können, wenn Sie das ScriptableObject zur Laufzeit untersuchen.
Stattdessen wird in jedem Elementfeld ein "Type mismatch" angezeigt. Ein ScriptableObject kann ein Szenenobjekt nicht serialisieren. Die Liste funktioniert zwar, aber die Daten werden nicht korrekt angezeigt.
Verwenden Sie eine öffentliche Eigenschaft oder das Attribut HideInInspector, wenn Sie Verwirrung vermeiden und verhindern wollen, dass die Liste im Inspector angezeigt wird.
Sie können dieses Problem auch mit einem benutzerdefinierten Editor-Skript und Inspector beheben. Gute Beispiele sind Soap - ScriptableObject Architecture Pattern im Unity Asset Store oder das Patterns Demo-Beispiel unten.
public class Enemy: MonoBehaviour
{
[SerializeField] private EnemyRuntimeSetSO m_RuntimeSet;
private void OnEnable()
{
m_RuntimeSet.Add(this);
}
private void OnDisable()
{
m_RuntimeSet.Remove(this);
}
}

Muster Demo
Obwohl PaddleBallSO keinen Anwendungsfall für Laufzeitsets enthält, enthält die Patterns-Demo des Projekts ein kleines Beispiel, das dieses Entwurfsmuster bei der Arbeit zeigt.
Klicken Sie auf die Schaltfläche Spawn Block, um die Wand aus Blöcken schrittweise aufzufüllen. Der Ball zerstört einen Block bei Kontakt.
Wählen Sie Set auswählen, um die BlockRuntimeSet_Data im Inspektor anzuzeigen. Sie enthält die Liste aller Blöcke in der Szene.
Du kannst kontinuierlich Blöcke in das Gitter einfügen, so schnell wie der Ball sie entfernt. Gleichzeitig verfolgt das Laufzeitset die sich aktiv verändernde Anzahl von Blöcken in der Artikelliste.
Die Patterns-Demo zeigt einige Qualitätsverbesserungen bei der Arbeit mit Laufzeitsets:
- Benutzerdefinierter Inspektor: Da ScriptableObjects nicht in der Lage sind, Szenenobjekte zu serialisieren, bietet das GameObjectRuntimeSetEditor-Skript einen benutzerdefinierten Inspektor, der den standardmäßigen Fehler der Typinkongruenz aufhebt. Klicken Sie auf einen beliebigen Block im Inspektor, um ihn in der Szenenhierarchie zu markieren.
- Ereignisse aktualisieren: Der Laufzeitsatz hat eine System.Action namens ItemsChanged. Dies benachrichtigt das Editor-Skript beim Hinzufügen oder Entfernen von Mitgliedern aus der Artikelliste. Dadurch wird der Inspektor aktualisiert, wenn sich die Liste der Elemente ändert. Ein Ereigniskanal synchronisiert auch die aktuelle Anzahl der Elemente mit einer benutzerdefinierten RuntimeSetCounter-Komponente. Hier wird die Gesamtzahl der Blöcke auf der Bildschirmoberfläche angezeigt.

Wie Laufzeitsets helfen
Wenn Sie das nächste Mal eine zentralisierte Liste von GameObjects oder Komponenten benötigen, sollten Sie Runtime-Sets in Betracht ziehen. Sie können weniger kostspielig sein als eine Suchoperation(Object.FindObjectOfType oder GameObject.FindWithTag) und weniger problematisch als Singletons.
Als ScriptableObjects sind sie von einzelnen GameObjects in der Szene entkoppelt. Behalten Sie so viele Laufzeitsets wie nötig für alles, was Sie häufig zur Laufzeit verfolgen wollen.
Laufzeitsets können die Überwachung von Gameplay-Objekten vereinfachen, ohne unnötige Abhängigkeiten zu schaffen. Die Zeit, die Sie bei der Fehlersuche und beim Testen sparen, können Sie nutzen, um neue Spielerlebnisse für Ihre Spieler zu entwickeln.

Weitere ScriptableObject-Ressourcen
Wir hoffen, dass die Laufzeitsets für Ihre kommenden Projekte von Nutzen sein werden.
Lesen Sie mehr über Entwurfsmuster mit ScriptableObjects in unserem technischen E-Book, Erstellen einer modularen Spielarchitektur in Unity mit ScriptableObjects. Weitere Informationen über gängige Entwurfsmuster für die Unity-Entwicklung finden Sie auch unter Verbessern Sie Ihren Code mit Programmiermustern für Spiele.
Haben Ihnen diese Inhalte gefallen?
Na ja.