Hero background image

Trennen Sie Spieldaten und Logik 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.

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

Dies ist der zweite in einer Reihe von sechs Mini-Guides, die erstellt wurden, um Unity-Entwickler mit der Demo zu unterstützen, die das E-Book Modulare Spielarchitektur in Unity mit ScriptableObjects erstellen. begleitet.

Die Demo ist inspiriert von klassischen Ball- und Paddle-Arcade-Spielmechaniken und zeigt, wie ScriptableObjects Ihnen helfen können, Komponenten zu erstellen, die testbar, skalierbar und designerfreundlich sind.

Zusammen bieten das E-Book, das Demoprojekt und diese Mini-Guides bewährte Praktiken für die Verwendung von Programmierdesignmustern mit der ScriptableObject-Klasse in Ihrem Unity-Projekt. Diese Tipps können Ihnen helfen, Ihren Code zu vereinfachen, den Speicherverbrauch zu reduzieren und die Wiederverwendbarkeit des Codes zu fördern.

Diese Serie umfasst die folgenden Artikel:

Wichtiger Hinweis, bevor Sie beginnen

Bevor Sie in das ScriptableObject-Demo-Projekt und diese Reihe von Mini-Guides eintauchen, denken Sie daran, dass Designmuster im Kern nur Ideen sind. Sie gelten nicht für jede Situation. Diese Techniken können Ihnen helfen, neue Wege zu finden, um mit Unity und ScriptableObjects zu arbeiten.

Jedes Muster hat Vor- und Nachteile. Wählen Sie nur die aus, die Ihrem spezifischen Projekt sinnvoll zugutekommen. Verlassen sich Ihre Designer stark auf den Unity Editor? Ein auf ScriptableObject basierendes Muster könnte eine gute Wahl sein, um ihnen die Zusammenarbeit mit Ihren Entwicklern zu erleichtern.

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

tab2

Datencontainer

Softwareentwickler sind oft an Modularität interessiert – die Aufteilung einer Anwendung in kleinere, eigenständige Einheiten. Jedes Modul wird für einen bestimmten Aspekt der Funktionalität der Anwendung verantwortlich.

In Unity können ScriptableObjects helfen, Daten von Logik zu trennen.

ScriptableObjects sind hervorragend geeignet, um Daten zu speichern, insbesondere wenn sie statisch sind. Das macht sie ideal für Spielstatistiken, Konfigurationswerte für Gegenstände oder NPCs, Charakterdialoge und vieles mehr.

Die Isolierung von Spieldaten von Verhaltenslogik kann jeden unabhängigen Teil des Projekts einfacher zu testen und zu warten machen. Diese "Trennung der Anliegen" kann unbeabsichtigte und unerwünschte Nebenwirkungen reduzieren, während Sie notwendige Änderungen vornehmen.

tab3

Allgemeiner Arbeitsablauf

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

Definiere ein ScriptableObject: Um eines zu erstellen, definiere eine C#-Klasse, die von der ScriptableObject-Basisklasse erbt, mit Feldern und Eigenschaften für die Daten, die du speichern möchtest. ScriptableObjects können die gleichen Datentypen speichern, die auch für MonoBehaviours verfügbar sind, was sie zu vielseitigen Datencontainern macht. Füge das CreateAssetMenuAttribute aus dem Editor hinzu, um das Erstellen des Assets im Projekt zu erleichtern.

Erstelle ein Asset: Sobald du eine ScriptableObject-Klasse definiert hast, kannst du eine Instanz dieses ScriptableObjects im Projekt erstellen. Dies erscheint als ein Asset, das auf der Festplatte gespeichert ist und das du in verschiedenen GameObjects und Szenen wiederverwenden kannst.

Werte festlegen: Nachdem du das Asset erstellt hast, fülle es mit Daten, indem du die Werte seiner Felder und Eigenschaften im Inspektor festlegst.

Verwende das Asset: Sobald das Asset Daten enthält, referenziere es von einer Variablen oder einem Feld. Alle Änderungen, die am ScriptableObject-Asset vorgenommen werden, werden im gesamten Projekt widergespiegelt.

Du kannst ScriptableObjects als Datencontainer in verschiedenen Teilen deines Spiels wiederverwenden. Zum Beispiel kannst du die Eigenschaften einer Waffe oder eines Charakters innerhalb eines ScriptableObjects definieren und dann dieses Asset von überall im Projekt referenzieren.

Hinweis: Du kannst auch ScriptableObjects zur Laufzeit über die CreateInstance-Methode generieren. Für die Datenspeicherung wirst du jedoch normalerweise die ScriptableObject-Assets im Voraus mit dem CreateAssetMenuAttribute erstellen.

tab4

ScriptableObjects versus MonoBehaviours

Um besser zu verstehen, warum ScriptableObjects eine geeignetere Wahl für die Datenspeicherung als MonoBehaviours sind, vergleiche leere Versionen von beiden. Stelle sicher, dass du deine Asset Serialization auf Modus: Force Text in den Projekteinstellungen festlegst, um die YAML-Markup als Text anzuzeigen.

Erstellen Sie ein neues GameObject mit einem ansonsten leeren MonoBehaviour. Vergleichen Sie dies dann mit einem leeren ScriptableObject-Asset. Wenn Sie sie nebeneinander platzieren, sollten sie wie der Vergleich im obigen Bild aussehen.

ScriptableObjects sind leichter im Vergleich zu MonoBehaviours und tragen nicht die mit letzterem verbundenen Overhead, wie die Transform-Komponente. Dies gibt ScriptableObjects einen kleineren Speicherbedarf und macht sie optimierter für die Datenspeicherung.

tab5

Muster-Demo

ScriptableObjects werden als Assets gespeichert, sodass sie außerhalb des Spielmodus bestehen bleiben, was nützlich sein kann. Zum Beispiel sind ScriptableObject-Daten von überall verfügbar, selbst wenn Sie eine neue Szene laden.

Das Patterns Demo-Beispiel verfügt über einen grundlegenden Credits-Bildschirm, den Sie selbst testen können. Ändern Sie das Credits_Data ScriptableObject und drücken Sie dann auf Aktualisieren, um den gespeicherten Text erscheinen zu sehen.

Wenn Sie ein RPG mit einer umfangreichen Menge an Dialogen oder einer Tutorialszenen mit einem vorgefertigten Skript hätten, ist dies eine gängige Methode, um viele Daten zu speichern.

Während die Daten innerhalb des ScriptableObject sofort aktualisiert werden, wenn sie geändert werden, benötigt unser Projekt eine Aktualisieren-Schaltfläche, um den Bildschirm manuell zu aktualisieren. Der auf dem UI Toolkit basierende Bildschirm wird nur einmal erstellt und muss benachrichtigt werden, wenn die Daten geändert wurden.

Erstellen Sie ein Ereignis innerhalb des ScriptableObject, wenn Sie Updates automatisch synchronisieren möchten. Zum Beispiel würde dieses ExampleSO-Skript das OnValueChanged Ereignis jedes Mal aufrufen, wenn sich ExampleValue ändert. Schauen Sie sich das Codebeispiel unten an.

Lassen Sie dann Ihr hörendes UI-Objekt sich bei OnValueChanged anmelden und entsprechend aktualisieren.

tab6

Speichern von Spieldaten mit dem Flyweight-Muster

ScriptableObjects glänzen, wenn viele Objekte dieselben Daten teilen. Wenn Sie beispielsweise ein Strategiespiel erstellen, in dem zahlreiche Einheiten die gleiche Angriffsgeschwindigkeit und maximale Gesundheit haben, ist es ineffizient, diese Werte individuell auf jedem GameObject zu speichern.

Stattdessen können Sie gemeinsame Daten an einem zentralen Ort konsolidieren und jedes Objekt auf diesen gemeinsamen Ort verweisen lassen. In der Softwareentwicklung ist dies eine Optimierung, die als flyweight pattern bekannt ist. Die Umstrukturierung Ihres Codes auf diese Weise vermeidet das Kopieren vieler Werte und reduziert Ihren Speicherbedarf.

In PaddleBallSO fungiert das GameDataSO ScriptableObject als gemeinsamer Datenspeicher.

Anstatt eine separate Kopie gemeinsamer Einstellungen (Geschwindigkeit, Masse, physikalische Sprungkraft usw.) zu behalten, verweisen die Paddle- und Ball-Skripte, wo möglich, auf dieselbe GameDataSO-Instanz. Jedes Spielelement verwaltet eindeutige Daten wie Positionen und Eingabeereignisse, greift jedoch, wo möglich, auf gemeinsame Daten zurück.

Obwohl die Einsparungen im Speicher bei nur zwei oder drei Objekten möglicherweise nicht auffallen, ist das Bearbeiten gemeinsamer Daten schneller und weniger fehleranfällig als das manuelle Bearbeiten jedes einzelnen.

Wenn Sie beispielsweise die Geschwindigkeit des Paddels ändern müssen, wird durch die Anpassung an einem einzigen Ort die Geschwindigkeit beider Paddel in jeder Szene aktualisiert. Wenn Sie sie als eindeutige Felder in den MonoBehaviours gespeichert hätten, könnte ein falscher Klick leicht zwei Werte aus dem Gleichgewicht bringen.

Das Auslagern von Daten in ScriptableObjects kann auch bei der Versionskontrolle helfen und Merge-Konflikte verhindern, wenn Teamkollegen an derselben Szene oder Prefab arbeiten.

tab7

PaddleBallSO Spieldaten

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

  • Paddle-Daten: Attribute wie Paddle-Geschwindigkeit, Luftwiderstand und Masse bestimmen die Bewegung und Physik der Paddles während des Gameplays.
  • Ball-Daten: Dies enthält die aktuelle Geschwindigkeit des Balls, die maximale Geschwindigkeit und den Sprungmultiplikator, der das Verhalten des Balls steuert, wenn er mit einer Simulation interagiert.
  • Match-Daten: GameDataSO enthält Informationen über Verzögerungen zwischen Punkten während eines Spiels, die helfen, das Tempo des Spiels zu steuern.
  • Spieler-IDs: PlayerIDSO ScriptableObjects fungieren als Teamidentifikation für jeden Spieler (z. B. Spieler1 und Spieler2).
  • 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 Ziele und Wände.

Mit diesen Einstellungen und Daten an einem zentralen Ort ermöglicht GameDataSO jedem Objekt, auf diese gemeinsamen Daten zuzugreifen. Dies vereinfacht, wie Sie diese Objekte verwalten, und fördert eine größere Konsistenz in Ihrem Projekt. Paddle-Physik ändern? Ändern Sie hier eine Einstellung, anstatt mehrere Skripte anzupassen.

tab10

Beispiel für doppelte Serialisierung: Level-Layouts

Manchmal kann man den Kuchen haben und ihn auch essen. Mit dualer Serialisierung können Sie Daten in einem ScriptableObject speichern und gleichzeitig in einem anderen Format beibehalten.

Das LevelLayoutSO-Skript demonstriert dieses Konzept. Neben der Speicherung der Startpositionen für die Paddles und den Ball speichert es Transformationsdaten für die Wände und Ziele in einer benutzerdefinierten Struktur.

Diese Werte können über die ExportToJson-Methode auf die Festplatte geschrieben werden. Die JSON-Dateien sind menschenlesbarer Text, der eine einfache Modifikation außerhalb von Unity ermöglicht. Dies ermöglicht es Ihnen, mit ScriptableObjects im Editor zu arbeiten und deren Daten dann an einem anderen Ort zu speichern, z. B. in einer JSON- oder XML-Datei.

Dateiformate wie JSON und XML können im Editor schwierig zu handhaben sein, aber sie sind einfach außerhalb von Unity in einem Texteditor zu modifizieren. Dies eröffnet die Möglichkeit für benutzerdefinierte oder benutzermodifizierte Levels.

Das GameSetup-Skript kann dann entweder ein LevelLayout ScriptableObject oder eine externe JSON-Datei verwenden, um das Spiellevel zu generieren.

Um ein benutzerdefiniertes modifiziertes Level zu laden, generiert das Setup-Skript zur Laufzeit ein ScriptableObject mit CreateInstance. Dann liest es den Text aus der JSON-Datei, um das ScriptableObject zu befüllen.

Ihre benutzerdefinierten Daten ersetzen den Inhalt des ScriptableObject und ermöglichen es Ihnen, dieses extern modifizierte Level wie jedes andere zu verwenden. Der Rest der Anwendung funktioniert normal, ohne von dem Wechsel zu wissen.

tab11

Andere Verwendungen von ScriptableObject-Datencontainern

Obwohl unser Paddle-Ball-Minispiel nicht jeden Anwendungsfall für ScriptableObject-Datencontainer demonstrieren kann, denken Sie an Folgendes für Ihre eigenen Anwendungen:

  • Spielkonfiguration: Denken Sie an Konstanten, Spielregeln oder andere Setup-Parameter, die sich während des Spiels nicht ändern müssen. Andere Komponenten können dann auf diese Konfigurationsdaten verweisen, ohne fest codierte Werte zu verwenden.
  • Charakter- und Feindattribute: Verwenden Sie ScriptableObjects, um Attribute wie Gesundheit, Angriffskraft, Geschwindigkeit usw. zu definieren. Dies ermöglicht es Ihren Designern, Gameplay-Elemente ohne einen Entwickler auszubalancieren und anzupassen.
  • Inventar- und Gegenstandssysteme: Gegenstanddefinitionen und Eigenschaften wie Namen, Beschreibungen und Symbole sind perfekt für ScriptableObjects. Sie können sie auch als Teil eines Inventarverwaltungssystems verwenden, um Gegenstände zu verfolgen, die der Spieler sammelt, verwendet oder ausrüstet.
  • Dialog- und Narrativsysteme: ScriptableObjects können Dialogtexte, Charakternamen, verzweigte Dialogpfade und andere narrativbezogene Daten speichern. Sie können die Grundlage für komplexe Dialogsysteme legen.
  • Level- und Fortschrittsdaten: Sie können ScriptableObjects verwenden, um Level-Layouts, Feind-Spawn-Punkte, Ziele und andere levelbezogene Informationen zu definieren.
  • Audio-Clips: Wie im PaddleBallSO-Projekt zu sehen, können ScriptableObjects einen oder mehrere Audio-Clips speichern. Diese können Audioeffekte oder Musik über mehrere Teile Ihres Spiels definieren.
  • Animationsclips: ScriptableObjects können verwendet werden, um Animationsclips zu speichern, was nützlich ist, um gemeinsame Animationen zu definieren, die über mehrere GameObjects oder Charaktere geteilt werden.

Wenn Sie tiefer in ScriptableObjects eintauchen und diese an Ihre eigenen Projekte anpassen, werden Sie noch mehr Anwendungen dafür entdecken. Sie sind besonders hilfreich beim Verwalten von Daten und erleichtern die Aufrechterhaltung der Konsistenz über verschiedene Spielelemente hinweg.

scriptable outro

Weitere ScriptableObject-Ressourcen

Lesen Sie mehr über Entwurfsmuster mit ScriptableObjects im E-Book Modulare Spielarchitektur in Unity mit ScriptableObjects erstellen. Sie können auch mehr über gängige Entwurfsmuster in der Unity-Entwicklung in Verbessern Sie Ihren Code mit Programmiermustern für Spiele erfahren.

Trennen Sie Spieldaten und Logik mit ScriptableObjects | Unity