Data Catalogs are the bridge between simple serialized data and real runtime/editor values.
Instead of storing direct object references everywhere, you store stable ids:
Catalog)Key)Then the system resolves those ids to actual values through registered catalogs.
In the Project window: Create → CupkekGames → Data → Catalog, entries are grouped by kind:
| Submenu | Purpose | Concrete types (samples library) |
|---|---|---|
| Value | Key → typed value (string, int, float, bool). Inspector uses IValueCatalog.GetDisplayValue next to the key (e.g. tier key 0 → Common). | StringCatalog, IntCatalog, FloatCatalog, BoolCatalog |
| Asset | Key → Unity Object (Sprite, ScriptableObject, …). Inspector shows object preview + ObjectField readout. | SpriteCatalog, ScriptableObjectCatalog (both AssetCatalog<T>) |
| Keys | Key list only; no per-key stored value. | KeyCatalog |
Each catalog asset has a Catalog Id field (serialized as _catalogId) that must match the CatalogKey.Catalog string and the ServiceLocator registration key.
Runtime UI (samples): ItemTierFeature resolves tooltip tier text with IValueCatalog.GetDisplayValue under InventoryConstants.ItemTierCatalogId. Slot Background USS uses a slug from that display when present (e.g. common), otherwise a slug from the raw key; ClearSlotVisual strips both those slugs and legacy raw-key classes for every known tier key.
You can keep a field like IconKey as two strings, but in inspector it still behaves like asset selection.
That means:
ICatalog — CatalogId + GetKeys() (every catalog registers this).IAssetCatalog / IAssetCatalog<T> — resolve a key to a Unity Object (T : Object). Used by asset-backed authoring and CatalogKey previews for sprites and other assets.AssetCatalog<T> — typical ScriptableObject implementation for asset catalogs (SpriteCatalog, ScriptableObjectCatalog, custom AssetCatalog<YourSO>).IValueCatalog / IValueCatalog<T> — resolve a key to a non-Object value plus a GetDisplayValue(string key) string for the inspector. The CatalogKey drawer calls this when no IAssetCatalog is registered for that catalog id.StringCatalog, IntCatalog, FloatCatalog, BoolCatalog (each implements IValueCatalog<T> and registers ICatalog, IValueCatalog, and IValueCatalog<T>).Adding another primitive-backed catalog (for example double) follows the same pattern: implement IValueCatalog<double>, register the three interfaces, and add a Create menu under Catalog → Value.
Asset catalog (AssetCatalog<T>) — register with catalog id (often append: true so several assets share one logical id):
ServiceLocator.Register(this, typeof(ICatalog), catalogId, append: true);
ServiceLocator.Register(this, typeof(IAssetCatalog), catalogId, append: true);
ServiceLocator.Register(this, typeof(IAssetCatalog<T>), catalogId, append: true);Value catalog (e.g. StringCatalog) — same id pattern, value interfaces:
ServiceLocator.Register(this, typeof(ICatalog), catalogId, append: true);
ServiceLocator.Register(this, typeof(IValueCatalog), catalogId, append: true);
ServiceLocator.Register(this, typeof(IValueCatalog<string>), catalogId, append: true);Keys-only (KeyCatalog) — ICatalog only.
Consumers resolve by catalog id string, for example:
var iconCatalogs = ServiceLocator.GetAll<IAssetCatalog>("ItemIcon");
var tierStrings = ServiceLocator.GetAll<IValueCatalog>("ItemTier");This model also supports future external content flows:
The key point is that your gameplay schema stays unchanged because it already stores ids, not hard references.
DataSO JSON bootstrapSettings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction