Engine & platform

Bessere Arbeitsabläufe in Szenen mit ScriptableObjects

AMEL NEGRA / UNITY TECHNOLOGIESContributor
Jul 1, 2020|9 Min.
Bessere Arbeitsabläufe in Szenen mit ScriptableObjects
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.

Die Verwaltung mehrerer Szenen in Unity kann eine Herausforderung sein, und die Verbesserung dieses Workflows ist sowohl für die Leistung Ihres Spiels als auch für die Produktivität Ihres Teams entscheidend. Hier finden Sie einige Tipps, wie Sie Ihre Scene-Workflows so einrichten können, dass sie auch für größere Projekte geeignet sind.

Die meisten Spiele bestehen aus mehreren Ebenen, und die Ebenen enthalten oft mehr als einen Schauplatz. In Spielen, in denen die Szenen relativ klein sind, können Sie sie mit Hilfe von Prefabs in verschiedene Abschnitte unterteilen. Um sie jedoch im Spiel zu aktivieren oder zu instanziieren, müssen Sie auf alle diese Prefabs verweisen. Das bedeutet, dass es effizienter ist, Szenen zu verwenden, je größer das Spiel wird und je mehr Speicherplatz diese Referenzen einnehmen.

Sie können Ihre Ebenen in eine oder mehrere Unity-Szenen unterteilen. Der Schlüssel dazu ist, die optimale Methode zu finden, um sie alle zu verwalten. Sie können mehrere Szenen im Editor und zur Laufzeit mit der Multi-Szenen-Bearbeitung öffnen. Die Aufteilung von Levels in mehrere Scenes hat auch den Vorteil, dass sie die Teamarbeit erleichtert, da sie Merge-Konflikte in Kollaborationstools wie Git, SVN, Unity Collaborate und dergleichen vermeidet.

Mehrere Szenen zum Zusammenstellen eines Levels verwalten

Im folgenden Video zeigen wir, wie man einen Level effizienter laden kann, indem man die Spiellogik und die verschiedenen Teile des Levels in mehrere verschiedene Unity-Szenen aufteilt. Wenn wir dann beim Laden dieser Szenen den Modus des additiven Szeneladens verwenden, laden und entladen wir die benötigten Teile zusammen mit der Spiellogik, die beständig ist. Wir verwenden Prefabs als "Anker" für die Scenes, was auch bei der Arbeit im Team viel Flexibilität bietet, da jede Scene einen Teil des Levels darstellt und separat bearbeitet werden kann.

Sie können diese Szenen auch im Bearbeitungsmodus laden und jederzeit die Wiedergabetaste drücken, so dass Sie sie beim Erstellen des Leveldesigns alle zusammen visualisieren können.

Wir zeigen zwei verschiedene Methoden zum Laden dieser Szenen. Die erste ist entfernungsbasiert, was sich gut für nicht-interne Ebenen wie eine offene Welt eignet. Diese Technik ist auch für einige visuelle Effekte (wie z. B. Nebel) nützlich, um den Lade- und Entladevorgang zu verbergen.

Die zweite Technik verwendet einen Trigger, um zu prüfen, welche Szenen geladen werden sollen, was bei der Arbeit mit Innenräumen effizienter ist.

Da nun alles innerhalb der Ebene verwaltet wird, können Sie eine Ebene darüber legen, um die Ebenen besser zu verwalten.

Mehrere Ebenen innerhalb eines Spiels mit ScriptableObjects verwalten

Wir wollen die verschiedenen Szenen jedes Levels sowie alle Levels während der gesamten Spieldauer im Auge behalten. Eine Möglichkeit, dies zu tun, besteht darin, statische Variablen und das Singleton-Muster in Ihren MonoBehaviour-Skripten zu verwenden, aber diese Lösung birgt einige Probleme. Die Verwendung des Singleton-Musters ermöglicht starre Verbindungen zwischen Ihren Systemen, ist also nicht streng modular. Die Systeme können nicht getrennt voneinander existieren und werden immer voneinander abhängen.

Ein weiteres Problem ist die Verwendung von statischen Variablen. Da man sie im Inspektor nicht sehen kann, muss man den Code ändern, um sie zu setzen, was es Künstlern oder Level-Designern erschwert, das Spiel einfach zu testen. Wenn Daten zwischen den verschiedenen Szenen ausgetauscht werden müssen, verwenden Sie statische Variablen in Kombination mit DontDestroyOnLoad, aber letzteres sollte, wann immer es möglich ist, vermieden werden.

Um Informationen über die verschiedenen Szenen zu speichern, können Sie ScriptableObject verwenden, eine serialisierbare Klasse, die hauptsächlich zum Speichern von Daten verwendet wird. Im Gegensatz zu MonoBehaviour-Skripten, die als an GameObjects angehängte Komponenten verwendet werden, sind ScriptableObjects an keine GameObjects angehängt und können daher von den verschiedenen Szenen des gesamten Projekts gemeinsam genutzt werden.

Sie möchten diese Struktur nicht nur für Levels, sondern auch für Menüszenen in Ihrem Spiel verwenden können. Erstellen Sie dazu eine GameScene-Klasse, die die verschiedenen gemeinsamen Eigenschaften von Ebenen und Menüs enthält.

Unbekannter Blocktyp "codeBlock", bitte geben Sie einen Serializer dafür in der `serializers.types` prop an

Beachten Sie, dass die Klasse von ScriptableObject und nicht von MonoBehaviour erbt. Sie können so viele Eigenschaften hinzufügen, wie Sie für Ihr Spiel benötigen. Nach diesem Schritt können Sie Level- und Menüklassen erstellen, die beide von der soeben erstellten GameScene-Klasse erben - sie sind also ebenfalls ScriptableObjects.

Unbekannter Blocktyp "codeBlock", bitte geben Sie einen Serializer dafür in der `serializers.types` prop an

Durch Hinzufügen des CreateAssetMenu-Attributs am oberen Rand können Sie eine neue Ebene über das Assets-Menü in Unity erstellen. Sie können dasselbe für die Klasse Menu tun. Sie können auch eine Aufzählung einfügen, um den Menütyp im Inspektor auswählen zu können.

Unbekannter Blocktyp "codeBlock", bitte geben Sie einen Serializer dafür in der `serializers.types` prop an


Nun, da Sie Ebenen und Menüs erstellen können, fügen wir eine Datenbank hinzu, in der die Ebenen und Menüs aufgelistet sind, damit Sie sie leicht finden können. Sie können auch einen Index hinzufügen, um die aktuelle Stufe des Spielers zu verfolgen. Dann können Sie Methoden hinzufügen, um ein neues Spiel zu laden (in diesem Fall wird das erste Level geladen), um das aktuelle Level erneut zu spielen und um zum nächsten Level zu gelangen. Beachten Sie, dass sich nur der Index zwischen diesen drei Methoden ändert. Sie können also eine Methode erstellen, die die Ebene mit einem Index lädt, um sie mehrfach zu verwenden.

Unbekannter Blocktyp "codeBlock", bitte geben Sie einen Serializer dafür in der `serializers.types` prop an


Es gibt auch Methoden für die Menüs, und Sie können den zuvor erstellten Aufzählungstyp verwenden, um das gewünschte Menü zu laden - achten Sie nur darauf, dass die Reihenfolge in der Aufzählung und die Reihenfolge in der Liste der Menüs übereinstimmen.

Jetzt können Sie endlich ein Level, ein Menü oder ein Datenbank-ScriptableObject aus dem Assets-Menü erstellen, indem Sie mit der rechten Maustaste in das Projektfenster klicken.

Bild

Fügen Sie von dort aus einfach die benötigten Ebenen und Menüs hinzu, passen Sie die Einstellungen an und fügen Sie sie dann der Szenendatenbank hinzu. Das folgende Beispiel zeigt Ihnen, wie die Daten von Level1, MainMenu und Scenes aussehen.

Bild

Es ist an der Zeit, diese Methoden aufzurufen. In diesem Beispiel ruft die Schaltfläche "Next Level" auf der Benutzeroberfläche (UI), die erscheint, wenn ein Spieler das Ende des Levels erreicht, die Methode "NextLevel" auf. Um die Methode der Schaltfläche zuzuordnen, klicken Sie auf die Plus-Schaltfläche des Ereignisses "Bei Klick" der Komponente "Schaltfläche", um ein neues Ereignis hinzuzufügen. Ziehen Sie dann das ScriptableObject "Scenes Data" in das Objektfeld und wählen Sie die Methode "NextLevel" von "ScenesData", wie unten gezeigt.

Bild

Nun können Sie den gleichen Vorgang für die anderen Schaltflächen durchführen - um das Level erneut zu spielen oder zum Hauptmenü zu gelangen, usw. Sie können das ScriptableObject auch von jedem anderen Skript aus referenzieren, um auf die verschiedenen Eigenschaften zuzugreifen, z. B. den AudioClip für die Hintergrundmusik oder das Nachbearbeitungsprofil, und sie im Level zu verwenden.

Tipps für die Fehlersicherheit Ihrer Prozesse
  • Minimierung des Be- und Entladens

Im ScenePartLoader-Skript, das im Video gezeigt wird, können Sie sehen, dass ein Spieler den Collider mehrfach betreten und verlassen kann, was das wiederholte Laden und Entladen einer Szene auslöst. Um dies zu vermeiden, können Sie eine Coroutine hinzufügen, bevor Sie die Lade- und Entlademethoden der Szene im Skript aufrufen, und die Coroutine anhalten, wenn der Spieler den Trigger verlässt.

  • Konventionen zur Namensgebung

Ein weiterer allgemeiner Tipp ist die Verwendung solider Namenskonventionen im Projekt. Das Team sollte sich im Vorfeld darauf einigen, wie die verschiedenen Arten von Assets - von Skripten und Szenen bis hin zu Materialien und anderen Dingen im Projekt - benannt werden sollen. Dies erleichtert nicht nur Ihnen, sondern auch Ihren Teamkollegen die Arbeit an dem Projekt und dessen Pflege. Dies ist immer eine gute Idee, aber in diesem speziellen Fall ist es entscheidend für die Szenenverwaltung mit ScriptableObjects. Unser Beispiel verwendet einen einfachen Ansatz, der auf dem Szenennamen basiert, aber es gibt viele verschiedene Lösungen, die sich weniger auf den Szenennamen stützen. Sie sollten den String-basierten Ansatz vermeiden, denn wenn Sie eine Unity-Szene in einem bestimmten Kontext umbenennen, wird diese Szene in einem anderen Teil des Spiels nicht geladen.

  • Kundenspezifische Werkzeuge

Eine Möglichkeit, die Namensabhängigkeit im Spiel zu vermeiden, besteht darin, Ihr Skript so einzurichten, dass es Szenen als Objekttyp referenziert. Auf diese Weise können Sie ein Szenen-Asset per Drag & Drop in einen Inspektor ziehen und dann seinen Namen in einem Skript sicher abrufen. Da es sich jedoch um eine Editor-Klasse handelt, haben Sie zur Laufzeit keinen Zugriff auf die AssetDatabase-Klasse. Sie müssen also beide Daten kombinieren, um eine Lösung zu finden, die im Editor funktioniert, menschliche Fehler verhindert und dennoch zur Laufzeit funktioniert. In der Schnittstelle ISerializationCallbackReceiver finden Sie ein Beispiel für die Implementierung eines Objekts, das bei der Serialisierung den Zeichenfolgenpfad aus dem Scene-Asset extrahieren und für die Verwendung zur Laufzeit speichern kann.

Darüber hinaus könnten Sie auch einen benutzerdefinierten Inspektor erstellen, um das schnelle Hinzufügen von Szenen zu den Gebäudeeinstellungen mithilfe von Schaltflächen zu erleichtern, anstatt sie manuell über dieses Menü hinzufügen und synchron halten zu müssen.

Ein Beispiel für diese Art von Tool ist diese großartige Open-Source-Implementierung des Entwicklers JohannesMP (dies ist keine offizielle Unity-Ressource).

Lassen Sie uns wissen, was Sie denken

Dieser Beitrag zeigt nur eine Möglichkeit, wie ScriptableObjects Ihren Arbeitsablauf bei der Arbeit mit mehreren Scenes in Kombination mit Prefabs verbessern können. Verschiedene Spiele haben sehr unterschiedliche Möglichkeiten, Szenen zu verwalten - keine einzige Lösung funktioniert für alle Spielstrukturen. Es ist sehr sinnvoll, ein eigenes, an die Organisation Ihres Projekts angepasstes Tooling zu implementieren.

Wir hoffen, dass diese Informationen Ihnen bei Ihrem Projekt helfen können oder Sie vielleicht dazu inspirieren, Ihre eigenen Tools für das Szenenmanagement zu entwickeln.

Lassen Sie uns in den Kommentaren wissen, wenn Sie Fragen haben. Wir würden gerne wissen, welche Methoden Sie zur Verwaltung der Szenen in Ihrem Spiel verwenden. Und Sie können uns gerne weitere Anwendungsfälle vorschlagen, die wir in zukünftigen Blogbeiträgen behandeln sollen.