Hero background image
Trennen von Spieldaten und Logik mit ScriptableObjects
Diese Seite wurde maschinell übersetzt. Um die Originalversion zu sehen, damit Sie die Genauigkeit anhand der Quelle prüfen können,

Auf dieser Seite wird erklärt, wie man ScriptableObjects als Datencontainer verwendet, die die Daten von der Logik in Ihrem Spielcode trennen.

Dies ist der zweite Teil einer Serie von sechs Mini-Handbüchern, die Unity-Entwicklern bei der der Demo zu unterstützen, die das E-Book Create modular game architecture in Unity with ScriptableObjects begleitet .

Die Demo ist von der Mechanik klassischer Arcade-Spiele inspiriert und zeigt, wie Sie mit ScriptableObjects Komponenten erstellen können, die testbar, skalierbar und designfreundlich sind.

Das E-Book, das Demoprojekt und diese Mini-Leitfäden bieten bewährte Verfahren für die Verwendung von Programmier-Entwurfsmuster mit der ScriptableObject-Klasse in Ihrem Unity-Projekt. Mit diesen Tipps können Sie Ihren Code vereinfachen, die Speichernutzung reduzieren und die Wiederverwendbarkeit von Code fördern.

Diese Reihe umfasst die folgenden Artikel:

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.

Letztlich ist die beste Code-Architektur diejenige, die zu Ihrem Projekt und Ihrem Team passt.

tab2
Datenbehälter

Softwareentwickler befassen sich häufig mit der Modularität - der Aufteilung einer Anwendung in kleinere, in sich geschlossene Einheiten. Jedes Modul ist für einen bestimmten Aspekt der Funktionalität der Anwendung verantwortlich.

In Unity können ScriptableObjects bei der Trennung von Daten und Logik helfen.

ScriptableObjects eignen sich hervorragend zum Speichern von Daten, insbesondere wenn diese statisch sind. Das macht sie ideal für Spielstatistiken, Konfigurationswerte für Gegenstände oder NSCs, Charakterdialoge und vieles mehr.

Durch die Isolierung der Spieldaten von der Verhaltenslogik lässt sich jeder unabhängige Teil des Projekts leichter testen und warten. Diese "Trennung der Belange" kann unbeabsichtigte und unerwünschte Nebenwirkungen reduzieren, während Sie die notwendigen Änderungen vornehmen.

tab3
Allgemeiner Arbeitsablauf

Wenn Sie eine Auffrischung des ScriptableObject-Workflows wünschen, kann Ihnen dieser Unity Learn-Artikel helfen. Ansonsten gibt es eine kurze Erklärung:

Definieren Sie ein ScriptableObject: Um eine solche Klasse zu erstellen, definieren Sie eine C#-Klasse, die von der Basisklasse ScriptableObject erbt und Felder und Eigenschaften für die zu speichernden Daten enthält. ScriptableObjects können die gleichen Datentypen speichern, die auch MonoBehaviours zur Verfügung stehen, was sie zu vielseitigen Datencontainern macht. Fügen Sie das CreateAssetMenuAttribute aus dem Editor hinzu, um die Erstellung des Assets im Projekt zu erleichtern.

Erstellen Sie ein Asset: Sobald Sie eine ScriptableObject-Klasse definiert haben, können Sie eine Instanz dieses ScriptableObjects im Projekt erstellen. Dies erscheint als ein auf der Festplatte gespeichertes Asset, das Sie in verschiedenen GameObjects und Szenen wiederverwenden können.

Werte einstellen: Nachdem Sie das Asset erstellt haben, füllen Sie es mit Daten, indem Sie die Werte seiner Felder und Eigenschaften im Inspektor festlegen.

Verwenden Sie das Asset: Sobald das Asset Daten enthält, referenzieren Sie es über eine Variable oder ein Feld. Alle am ScriptableObject-Asset vorgenommenen Änderungen werden für das gesamte Projekt übernommen.

Sie können ScriptableObjects als Datencontainer in verschiedenen Teilen Ihres Spiels wiederverwenden. Sie können zum Beispiel die Eigenschaften einer Waffe oder eines Charakters in einem ScriptableObject definieren und dann von einer beliebigen Stelle im Projekt auf dieses Asset verweisen.

Hinweis: Sie können ScriptableObjects auch zur Laufzeit mit der Methode CreateInstance erzeugen. Für die Datenspeicherung werden Sie jedoch in der Regel die ScriptableObject-Assets im Voraus mit dem CreateAssetMenuAttribute erstellen.

tab4
ScriptableObjects versus MonoBehaviours

Um besser zu verstehen, warum ScriptableObjects für die Datenspeicherung besser geeignet sind als MonoBehaviours, vergleichen Sie leere Versionen der beiden. Stellen Sie sicher, dass die Asset-Serialisierung auf den Modus eingestellt ist: Erzwingen Sie Text in den Projekteinstellungen, um das YAML-Markup als Text anzuzeigen.

Erstellt ein neues GameObject mit einem ansonsten leeren MonoBehaviour. Vergleichen Sie dies dann mit einem leeren ScriptableObject-Asset. Wenn Sie sie nebeneinander legen, sollten sie so aussehen wie in der obigen Abbildung verglichen.

ScriptableObjects sind im Vergleich zu MonoBehaviours leichter und haben nicht den mit letzteren verbundenen Overhead, wie die Transform-Komponente. Dadurch haben ScriptableObjects einen geringeren Speicherbedarf und sind für die Datenspeicherung optimiert.

tab5
Muster Demo

ScriptableObjects werden als Assets gespeichert, so dass sie auch außerhalb des Spielmodus bestehen bleiben, was nützlich sein kann. Beispielsweise sind ScriptableObject-Daten von überall her verfügbar, auch wenn Sie eine neue Szene laden.

Das Demo-Beispiel von Patterns enthält einen einfachen Abspannbildschirm, den Sie selbst testen können. Ändern Sie das ScriptableObject Credits_Data und drücken Sie dann auf Aktualisieren, um zu sehen, wie der gespeicherte Text erscheint.

Wenn Sie ein Rollenspiel mit vielen Dialogen oder eine Tutorial-Szene mit einem vorgefertigten Skript haben, ist dies ein üblicher Weg, um viele Daten zu speichern.

Während die Daten innerhalb des ScriptableObjects bei Änderungen sofort aktualisiert werden, benötigt unser Projekt eine Aktualisierungsschaltfläche, um den Bildschirm manuell zu aktualisieren. Der auf dem UI-Toolkit basierende Bildschirm baut sich nur einmal auf und muss benachrichtigt werden, wenn die Daten geändert wurden.

Erstellen Sie ein Ereignis innerhalb des ScriptableObjects, wenn Sie Updates automatisch synchronisieren möchten. Dieses ExampleSO-Skript würde zum Beispiel das Ereignis OnValueChanged jedes Mal aufrufen, wenn sich ExampleValue ändert. Sehen Sie sich das folgende Codebeispiel an.

Lassen Sie dann Ihr zuhörendes UI-Objekt OnValueChanged abonnieren und entsprechend aktualisieren.

tab6
Speichern von Spieldaten mit dem Fliegengewichtsmuster

ScriptableObjects eignen sich hervorragend, wenn viele Objekte dieselben Daten teilen. Wenn man zum Beispiel ein Strategiespiel entwickelt, in dem viele Einheiten die gleiche Angriffsgeschwindigkeit und maximale Gesundheit haben, ist es ineffizient, diese Werte für jedes einzelne Spielobjekt zu speichern.

Stattdessen können Sie gemeinsam genutzte Daten an einem zentralen Ort konsolidieren und jedes Objekt auf diesen gemeinsamen Ort verweisen lassen. In der Softwareentwicklung ist dies eine Optimierung, die als Fliegengewichtsmuster bekannt ist. Wenn Sie Ihren Code auf diese Weise umstrukturieren, vermeiden Sie das Kopieren vieler Werte und reduzieren Ihren Speicherbedarf.

In PaddleBallSO dient das GameDataSO ScriptableObject als gemeinsamer Datenspeicher.

Anstatt eine separate Kopie der gemeinsamen Einstellungen (Geschwindigkeit, Masse, physikalische Sprungkraft usw.) zu behalten, verweisen die Skripte für Paddel und Ball nach Möglichkeit auf dieselbe GameDataSO-Instanz. Jedes Spielelement verwaltet eindeutige Daten wie Positionen und Eingabeereignisse, verwendet aber, wenn möglich, gemeinsame Daten.

Auch wenn die Speichereinsparungen bei nur zwei oder drei Objekten nicht spürbar sind, ist die Bearbeitung gemeinsamer Daten schneller und weniger fehleranfällig als die manuelle Bearbeitung jedes einzelnen Objekts.

Wenn Sie z. B. die Paddelgeschwindigkeit ändern müssen, werden beide Paddel in jeder Szene aktualisiert, wenn Sie sie an einer einzigen Stelle einstellen. Wenn Sie sie als eindeutige Felder in den MonoBehaviours gespeichert haben, könnte ein falscher Klick leicht dazu führen, dass zwei Werte nicht mehr synchron sind.

Das Auslagern von Daten in ScriptableObjects kann auch bei der Versionskontrolle helfen und Zusammenführungskonflikte verhindern, wenn Teamkollegen an der gleichen Szene oder Prefab arbeiten.

tab7
PaddleBallSO game data

Das GameDataSO zeigt, wie man ein ScriptableObject als Datencontainer verwendet. In PaddleBallSO umfasst dies verschiedene Einstellungen zur Konfiguration des Spielablaufs:

  • Paddeldaten: Attribute wie Paddelgeschwindigkeit, Widerstand und Masse bestimmen die Bewegung und die Physik der Paddel während des Spiels.
  • Daten zum Ball: Hier werden die aktuelle Geschwindigkeit, die Höchstgeschwindigkeit und der Abprallmultiplikator des Balls gespeichert, der das Verhalten des Balls bei der Interaktion mit einer Simulation steuert.
  • Spiel-Daten: GameDataSO enthält Informationen über Verzögerungen zwischen Punkten während eines Spiels und hilft so, das Spieltempo zu steuern.
  • Spieler-IDs: PlayerIDSO ScriptableObjects dienen als Teamidentifikation für jeden Spieler (z. B. Player1 und Player2).
  • Spieler-Sprites: Diese optionalen Sprites ermöglichen die Anpassung des Spieler-Avatars.
  • Level-Layout: Das LevelLayoutSO-Objekt definiert die Startpositionen für Spieler und Spielelemente wie Tore und Wände.

Da sich diese Einstellungen und Daten alle an einem zentralen Ort befinden, kann jedes Objekt mit GameDataSO auf diese gemeinsamen Daten zugreifen. Dies vereinfacht die Verwaltung dieser Objekte und sorgt für mehr Konsistenz im gesamten Projekt. Änderung der Paddelphysik? Nehmen Sie hier eine Änderung vor, anstatt mehrere Skripte anzupassen.

tab10
Beispiel für doppelte Serialisierung: Level-Layouts

Manchmal kann man seinen Kuchen haben und ihn auch essen. Mit der doppelten Serialisierung können Sie Daten in einem ScriptableObject speichern und sie gleichzeitig in einem anderen Format pflegen.

Das Skript LevelLayoutSO demonstriert dieses Konzept. Zusätzlich zu den Startpositionen für die Schläger und den Ball speichert es die Transformationsdaten für die Wände und Tore in einer eigenen Struktur.

Diese Werte können mit der Methode ExportToJson auf die Festplatte geschrieben werden. Die JSON-Dateien sind menschenlesbarer Text, so dass eine einfache Änderung außerhalb von Unity möglich ist. So können Sie mit ScriptableObjects im Editor arbeiten und ihre Daten dann an einem anderen Ort speichern, z. B. in einer JSON- oder XML-Datei.

Dateiformate wie JSON und XML können im Editor schwierig zu bearbeiten sein, aber sie lassen sich außerhalb von Unity in einem Texteditor leicht ändern. Dies eröffnet die Möglichkeit für benutzerdefinierte oder vom Benutzer modifizierte Levels.

Das GameSetup-Skript kann dann entweder ein LevelLayout ScriptableObject oder eine externe JSON-Datei verwenden, um die Spielebene zu erzeugen.

Um einen benutzerdefinierten modifizierten Level zu laden, erzeugt das Setup-Skript zur Laufzeit ein ScriptableObject mit CreateInstance. Dann wird der Text aus der JSON-Datei gelesen, um das ScriptableObject zu füllen.

Ihre benutzerdefinierten Daten ersetzen den Inhalt des ScriptableObjects und ermöglichen es Ihnen, diese extern modifizierte Ebene wie jede andere zu verwenden. Der Rest der Anwendung funktioniert normal, ohne dass die Umschaltung bemerkt wird.

tab11
Andere Verwendungen von ScriptableObject-Datencontainern

Auch wenn unser Paddelball-Minispiel nicht jeden Anwendungsfall für ScriptableObject-Datencontainer demonstrieren kann, sollten Sie die folgenden Beispiele für Ihre eigenen Anwendungen in Betracht ziehen:

  • Konfiguration des Spiels: Denken Sie an Konstanten, Spielregeln oder andere Einrichtungsparameter, die sich während des Spiels nicht ändern müssen. Andere Komponenten können sich dann auf diese Konfigurationsdaten beziehen, ohne fest kodierte Werte zu verwenden.
  • Charakter- und Feindattribute: Verwenden Sie ScriptableObjects, um Attribute wie Gesundheit, Angriffskraft, Geschwindigkeit usw. zu definieren. Auf diese Weise können Ihre Designer Gameplay-Elemente ohne einen Entwickler ausbalancieren und optimieren.
  • Inventar- und Artikelsysteme: Objektdefinitionen und -eigenschaften wie Namen, Beschreibungen und Symbole sind perfekt für ScriptableObjects geeignet. Sie können sie auch als Teil eines Inventarverwaltungssystems verwenden, um Gegenstände zu verfolgen, die der Spieler sammelt, benutzt oder ausrüstet.
  • Dialog und narrative Systeme: ScriptableObjects können Dialogtext, Charakternamen, verzweigende Dialogpfade und andere erzählbezogene Daten speichern. Sie können die Grundlage für komplexe Dialogsysteme bilden.
  • Daten zu Niveau und Verlauf: Sie können ScriptableObjects verwenden, um Level-Layouts, gegnerische Spawn-Punkte, Ziele und andere Level-bezogene Informationen zu definieren.
  • Audio-Clips: Wie im PaddleBallSO-Projekt zu sehen ist, können ScriptableObjects einen oder mehrere Audio-Clips speichern. Diese können Audioeffekte oder Musik für mehrere Teile des Spiels definieren.
  • Animationsclips: ScriptableObjects können verwendet werden, um Animationsclips zu speichern, was nützlich ist, um gemeinsame Animationen zu definieren, die von mehreren GameObjects oder Charakteren genutzt werden.

Wenn Sie sich eingehender mit ScriptableObjects beschäftigen und sie an Ihre eigenen Projekte anpassen, werden Sie noch mehr Anwendungsmöglichkeiten entdecken. Sie sind besonders hilfreich bei der Verwaltung von Daten und erleichtern die Wahrung der Konsistenz zwischen verschiedenen Spielelementen.

skriptfähiges Outro
Weitere ScriptableObject-Ressourcen

Lesen Sie mehr über Entwurfsmuster mit ScriptableObjects im E-Book Modulare Spielarchitektur in Unity mit ScriptableObjects erstellen. 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?