
Diese Seite erklärt, wie man ScriptableObjects als Logikcontainer verwendet. Indem Sie dies tun, können Sie sie als Delegatobjekte oder kleine Bündel von Aktionen behandeln, die Sie bei Bedarf aufrufen können.
Dies ist der vierte in einer Reihe von sechs Mini-Guides, die erstellt wurden, um Unity-Entwicklern mit der Demo zu helfen, 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:
Bevor Sie in das ScriptableObject-Demo-Projekt und diese Reihe von Mini-Anleitungen eintauchen, denken Sie daran, dass Designmuster im Kern nur Ideen sind. Sie sind nicht auf jede Situation anwendbar. 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, 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.

Mit dem Strategiemuster können Sie eine Schnittstelle oder eine Basis-ScriptableObject-Klasse definieren und diese Delegatobjekte zur Laufzeit austauschbar machen.
Eine Anwendung besteht darin, Algorithmen für spezifische Aufgaben in einem ScriptableObject zu kapseln und dieses ScriptableObject dann im Kontext von etwas anderem zu verwenden.
Wenn Sie beispielsweise ein KI- oder Pfadsuchsystem für eine EnemyUnit-Klasse schreiben würden, könnten Sie ein ScriptableObject mit einer Pfadsuchtechnik (wie A*, Dijkstra usw.) erstellen.
Die EnemyUnit selbst würde tatsächlich keine Pfadsuchlogik enthalten. Stattdessen würde sie eine Referenz auf ein separates "Strategie"-ScriptableObject behalten. Der Vorteil dieses Designs ist, dass Sie einfach durch den Austausch von Objekten zu einem anderen Algorithmus wechseln können. Dies ist eine Möglichkeit, zur Laufzeit unterschiedliche Verhaltensweisen auszuwählen.
Wenn das MonoBehaviour eine Aufgabe ausführen muss, ruft es die externen Methoden des ScriptableObject anstelle seiner eigenen auf. Das ScriptableObject könnte beispielsweise öffentliche Methoden für BewegeEinheit oder ZielSetzen enthalten, um die feindliche Einheit zu steuern und ein Ziel anzugeben.
Sie können dieses Muster mit einer abstrakten Basisklasse oder einer Schnittstelle verbessern. Das bedeutet, dass jedes ScriptableObject, das die Strategie implementiert, mit einem anderen ausgetauscht werden kann. Dieses hot-swappable ScriptableObject "steckt" in das MonoBehaviour, das darauf verweist – sogar zur Laufzeit.
Wenn du möchtest, dass die EnemyUnit ihr Verhalten aufgrund von Spielbedingungen ändert, kann der äußere Kontext (das MonoBehaviour) nach diesen Bedingungen prüfen. Dann kann es ein anderes ScriptableObject als Antwort einfügen.
Durch die Trennung der Implementierungsdetails in ein ScriptableObject kannst du auch eine bessere Aufteilung der Verantwortlichkeiten in deinem Team ermöglichen. Ein Entwickler könnte sich auf den Algorithmus im ScriptableObject konzentrieren, während ein anderer am MonoBehaviour-Kontext arbeitet.
Um dieses pluggable Verhalten zu erstellen, stelle sicher, dass du:
Deinen Code so zu organisieren, kann es einfacher machen, zwischen verschiedenen Implementierungen derselben Strategie zu wechseln. Dieses pluggable Verhalten wird dann einfacher zu debuggen und zu warten.
Ein Algorithmus oder eine Strategie muss nicht kompliziert sein. Das PaddleBallSO Projekt demonstriert beispielsweise ein ziemlich einfaches Audio-Wiedergabesystem im SimpleAudioDelegate.
Die abstrakte Klasse AudioDelegateSO definiert eine einzelne Play-Methode, die einen AudioSource-Parameter akzeptiert. Die konkrete Implementierung überschreibt dies dann.
Die SimpleAudioDelegateSO Unterklasse definiert ein Array von AudioClips. Es wählt einen zufälligen Clip aus und spielt ihn mit der überschriebenen Play Methodenimplementierung ab. Dies fügt eine Variation in Tonhöhe und Lautstärke innerhalb eines benutzerdefinierten Bereichs hinzu.
Obwohl es nur ein paar Zeilen sind, können Sie mit dem folgenden Code-Snippet viele verschiedene Audioeffekte erzeugen.
Während dieses spezifische Beispiel nicht wirklich für intensive Audio-Nutzung geeignet ist, wird es hier als grundlegende Anwendungsdemonstration von ScriptableObjects in einem Strategie-Muster präsentiert.
Ein Designer kann viele verschiedene ScriptableObjects erstellen, um Soundeffekte darzustellen, ohne den Code zu berühren. Wieder erfordert dies minimale Unterstützung von einem Entwickler, sobald das Basis-ScriptableObject abgeschlossen ist.
In PaddleBallSO kann jetzt jeder ein neues Array von Klängen einrichten, die abgespielt werden, wenn der Ball eine der Levelwände trifft. Designer gewinnen kreative Unabhängigkeit und Flexibilität, da sie vollständig im Editor arbeiten. Dieser Ansatz entlastet die Programmierressourcen, da Entwickler nicht mehr bei jeder Designentscheidung helfen müssen.

Sie können das Audio-Beispiel auch in der Patterns-Demo sehen. Jeder Sound stammt von einem leicht unterschiedlichen SimpleAudioDelegateSO-Asset, mit kleinen Variationen zwischen den Instanzen.
In diesem Beispiel enthält jede Ecke einen AudioSource. Ein benutzerdefinierter AudioModifier MonoBehaviour verwendet einen auf ScriptableObject basierenden Delegaten, um den Sound abzuspielen.
Die Unterschiede in der Tonhöhe ergeben sich nur aus den Einstellungen auf jedem ScriptableObject-Asset (BeepHighPitched_SO, BeepLowPitched_SO usw.).
Die Verwendung eines ScriptableObject zur Steuerung der Aktionslogik kann es Ihrem Designteam erleichtern, mit Ideen zu experimentieren. Dies ermöglicht es einem Designer, unabhängiger von einem Entwickler zu arbeiten.

Das PaddleBallSO Projekt verwendet ebenfalls das Strategiemuster in seinem Zielsystem. Obwohl dies nichts ist, was zur Laufzeit variieren muss, bietet die Kapselung jedes Objekts in einem ScriptableObject eine flexible Möglichkeit, Gewinn- und Verlustbedingungen zu testen.
Die abstrakte Basisklasse, ObjectiveSO, hält Werte wie den Namen des Ziels und ob es abgeschlossen wurde.
Die konkreten Unterklassen, wie ScoreObjectiveSO, implementieren dann die tatsächliche Logik, wie jedes Ziel erreicht wird. Sie tun dies, indem sie die CompleteObjective Methode von ObjectiveSO überschreiben und die Logik für die Gewinnbedingung hinzufügen.
Muss der Spieler eine bestimmte Punktzahl erreichen oder eine bestimmte Anzahl von Feinden besiegen? Müssen sie einen bestimmten Ort erreichen oder einen bestimmten Gegenstand aufheben? Dies sind gängige Gewinnbedingungen, die zu ScriptableObject-basierten Zielen werden könnten.
Der ObjectiveManager dient als der größere Kontext für die ScriptableObjects. Er verwaltet eine Liste von ObjectiveSOs und verlässt sich auf jedes ScriptableObject, um zu bestimmen, ob es abgeschlossen ist. Wenn jedes ObjectiveSO einen Abschlusszustand zeigt, ist das Spiel vorbei.
Zum Beispiel zeigt das ScoreObjectiveSO eine Möglichkeit, ein Punktziel zu implementieren:
Der ObjectiveManager kümmert sich nur darum, dass alle gegebenen Ziele abgeschlossen sind. Es ist sich der Details innerhalb jedes Ziels selbst nicht bewusst.
Das Ziel hier ist erneut Modularität. Dies ermöglicht es Ihnen, jedes ObjectiveSO anzupassen, ohne bestehende zu beeinflussen.
Das PaddleBallSO Spiel hat wirklich nur ein Ziel. Wenn einer der Spieler das Gewinnziel erreicht, endet das Spiel.
Sie könnten dies jedoch erweitern oder Ziele kombinieren, um ein komplexeres Zielsystem zu erstellen. Experimentieren Sie und sehen Sie, ob Sie neue Spielmodi erstellen können (z. B. eine Mindestanzahl von Punkten erzielen, bevor die Zeit abläuft).
Da die Logik in einem ScriptableObject gekapselt ist, können Sie jedes ObjectiveSO gegen ein anderes austauschen. Eine neue Gewinnbedingung zu erstellen, bedeutet einfach, die Liste im ObjectiveManager neu zu konfigurieren. In gewissem Sinne ist das Ziel "steckbar" in den umgebenden Kontext.
Beachten Sie, dass ein praktischer Aspekt des ObjectiveSO das Ereignis ist, das verwendet wird, um Nachrichten zwischen GameObjects zu senden. Als nächstes werden wir erkunden, wie man ScriptableObjects verwendet, um diese ereignisgesteuerte Architektur zu implementieren.

Lesen Sie mehr über Entwurfsmuster mit ScriptableObjects im E-Book Create modular game architecture in Unity with ScriptableObjects. Sie können auch mehr über gängige Entwurfsmuster in der Unity-Entwicklung in Level up your code with game programming patterns erfahren.