Zum Hauptinhalt springen

Scriptable Objects

Im Dungeon-Crawler-Genre spielt das Einsammeln und Verwenden von Gegenständen eine zentrale Rolle. Auch in unserem Spiel müssen Gegenstände strategisch genutzt werden um den Dungeon zu überleben. Für die Verwaltung der vielen Gegenstände haben wir jedoch keine klassische SQL-Datenbank benötigt. Stattdessen haben wir uns für Scriptable Objects (SO) entschieden. Das sind Datencontainer, die unabhängig von Szenen oder Objekten funktionieren (d.h. man kann diese auf der Festplatte speichern und im Spiel instanziieren). Sie eignen sich perfekt, um eine große Menge an Itemdaten übersichtlich zu speichern (z.B. Waffen, Rüstungen oder Essen).

Aufbau eines Items

Für jeden Item-Typ haben wir ein eigenes Scriptable Object erstellt und über das AssetMenu verfügbar gemacht. Dadurch konnten wir Items direkt im Editor konfigurieren, inklusive Icons, Name, Beschreibung und Werten.

[CreateAssetMenu(menuName = "Create Items/Item")]
public class InventoryItemData : ScriptableObject
{
public enum Rarity {Common, Uncommon, Rare, Epic, Legendary };
[Header("Set UI Attributes")]
public Sprite Icon;
public int MaxStackSize;
public int ID = -1;
public string DisplayName;
[TextArea(4, 4)]
public string Description;
public Rarity rarity = Rarity.Common;
[Header("Set Item Attributes")]
public GameObject ItemPrefab;
public InventoryItemType ItemType;

[Range(0,100)]
public int dropChance;
public bool consumable = false;
public bool isAbility = false;

public float goldValue = 1;

}

Modelle

Modelle

Loot-System und Item-Datenbank

Die Scriptable Objects dienen nicht nur zur Anzeige, sondern auch zur Steuerung des Loot-Verhaltens. Gegner und Schatztruhen können ihre Items direkt aus einer Item-Datenbank beziehen, die als einfache Liste von SOs aufgebaut ist.

  • Jeder Gegner wählt zufällig bis zu fünf Items aus der Liste aus, die beim Tod fallen können
  • Alternativ kann man gezielt Items pro Gegner-Typ definieren
  • Neue Items werden automatisch erkannt und zur Datenbank hinzugefügt

Database

Design pattern

Damit Items im Spiel unterschiedliche Effekte haben können, haben wir das Strategy Pattern verwendet. Mit dem Strategy Pattern können wir für jedes Item ein eigenes Verhalten definieren, ohne den restlichen Code anpassen zu müssen. Beim Benutzen eines Items wird die Methode UseItem() aufgerufen. Was dabei passiert, wird im jeweiligen Skript festgelegt (z.B. heilen oder Waffe ausrüsten). Die Logik bleibt dadurch flexibel, und wir vermeiden if-Abfragen wie if(itemType == "Weapon"). So können wir neue Itemtypen einfach hinzufügen, ohne bestehende Systeme zu ändern.

[CreateAssetMenu(menuName = "Create Items/Item")]
public class ItemDataProvider : MonoBehavior
{

//ItemData

public virtual void UseItem()
{
Debug.Log($"Using {DisplayName}")
}

}

Zusätzlich gibt es einen Wrapper zur Verbindung zwischen Item und UI bzw. GameObject:

[CreateAssetMenu(menuName = "Create Items/Item")]
public class ItemDataProvider : MonoBehavior
{
[SerializeField] public InventoryItemData item;
public InventoryItemData Item => item;

}

ItemInspector

Vorteile von Scriptable Objects

Scriptable Objects machen unser Projekt übersichtlicher und leichter zu erweitern.

  • Item-Parameter lassen sich anpassen ohne Code zu ändern
  • Neue Items können direkt im Editor erstellt werden
  • Items lassen sich unabhängig vom Inventarsystem verwalten

Ein Spieldesigner kann ganz einfach ein neues Scriptable Object erstellen, es mit Werten füllen und per ItemDataProvider in die Spielszene einbinden. Falls ein passendes 3D-Modell noch fehlt, lässt sich dieses separat in Blender erstellen und dem Objekt direkt zuweisen.

Abbildung Game Scene mit und ohne Scriptable Objects

Modelle Modelle