Quick Links: Installation | Features | Editor Tools | Contributing
Managing game data in Unity often leads to fragmented solutions: scattered config files, tight coupling between data and logic, and cross-platform inconsistencies. This GameData package addresses these challenges:
| Problem | Solution |
|---|---|
| Scattered config management | Type-safe ConfigsProvider with O(1) lookups and versioning |
| Tight coupling to data changes | Observable types (ObservableField, ObservableList, ObservableDictionary) for reactive programming |
| Manual derived state updates | ComputedField for auto-updating calculated values with dependency tracking |
| Cross-platform float inconsistencies | Deterministic floatP type for reproducible calculations across all platforms |
| Backend sync complexity | Built-in JSON serialization with ConfigsSerializer for client/server sync |
| Dictionary Inspector editing | UnitySerializedDictionary for seamless Inspector support |
| Fragile enum serialization | EnumSelector stores enum names (not values) to survive enum changes |
Built for production: Minimal dependencies. Zero per-frame allocations in observable types. Used in real games.
- Unity 6000.0+ (Unity 6)
- Newtonsoft.Json (com.unity.nuget.newtonsoft-json v3.2.1) — automatically resolved
- UniTask (com.cysharp.unitask v2.5.10) — used by the async backend interface
IConfigBackendService - TextMeshPro (com.unity.textmeshpro v3.0.6) — used by Samples~ UI scripts only
| Unity Version | Status |
|---|---|
| 6000.0+ (Unity 6) | ✅ Fully Tested |
| 2022.3 LTS |
- Open Unity Package Manager (
Window→Package Manager) - Click
+→Add package from git URL - Enter:
https://github.com/CoderGamester/com.gamelovers.gamedata.git
{
"dependencies": {
"com.gamelovers.gamedata": "https://github.com/CoderGamester/com.gamelovers.gamedata.git"
}
}| Component | Responsibility |
|---|---|
| ConfigsProvider | Type-safe config storage with O(1) lookups and versioning |
| ConfigsSerializer | JSON serialization for client/server config synchronization |
| ConfigTypesBinder | Whitelist-based type binder for secure deserialization |
| ObservableField | Reactive wrapper for single values with change callbacks |
| ObservableList | Reactive wrapper for lists with add/remove/update callbacks |
| ObservableDictionary | Reactive wrapper for dictionaries with key-based callbacks |
| ComputedField | Auto-updating derived values that track dependencies |
| floatP | Deterministic floating-point type for cross-platform math |
| MathfloatP | Math functions (Sin, Cos, Sqrt, etc.) for floatP |
| EnumSelector | Enum dropdown that survives enum value changes |
| UnitySerializedDictionary | Dictionary type visible in Unity Inspector |
| Tool | Menu | Purpose |
|---|---|---|
| Config Browser | Tools > Game Data > Config Browser |
Browse configs, validate, export JSON, preview migrations |
| Observable Debugger | Tools > Game Data > Observable Debugger |
Inspect live observables in play mode |
| ConfigsScriptableObject Inspector | Inspector (automatic) | Inline duplicate-key validation and Validate All action |
Type-safe, high-performance configuration storage.
var provider = new ConfigsProvider();
provider.AddConfigs(item => item.Id, itemConfigs);
provider.AddSingletonConfig(new GameSettings { Difficulty = 2 });
var item = provider.GetConfig<ItemConfig>(42);
var settings = provider.GetConfig<GameSettings>();
// Zero-allocation enumeration
foreach (var enemy in provider.EnumerateConfigs<EnemyConfig>())
ProcessEnemy(enemy);JSON serialization with security modes.
var serializer = new ConfigsSerializer(); // TrustedOnly by default
string json = serializer.Serialize(provider, "123");
var restored = serializer.Deserialize<ConfigsProvider>(json);
serializer.RegisterAllowedTypes(new[] { typeof(EnemyConfig) });var score = new ObservableField<int>(0);
score.Observe((prev, curr) => UpdateScoreUI(curr));
score.InvokeObserve((prev, curr) => UpdateScoreUI(curr)); // invokes immediately too
score.Value = 100;
score.StopObservingAll(this);var baseHp = new ObservableField<int>(100);
var bonus = new ObservableField<int>(25);
var totalHp = new ComputedField<int>(() => baseHp.Value + bonus.Value);
totalHp.Observe((prev, curr) => Debug.Log($"HP: {curr}"));
baseHp.Value = 120; // totalHp auto-updates to 145
totalHp.Dispose();var inventory = new ObservableList<string>(new List<string>());
inventory.Observe((index, prev, curr, type) => RefreshUI(index, curr));
inventory.Add("Sword");
var stats = new ObservableDictionary<string, int>(new Dictionary<string, int>());
stats.Observe("health", (key, prev, curr, type) => Debug.Log($"{key}: {curr}"));
stats.Add("health", 100);floatP a = 3.14f;
floatP sum = a + 2.0f;
float result = (float)sum;
uint raw = a.RawValue; // bit-exact for determinism
floatP copy = floatP.FromRaw(raw);[Serializable]
public class StringIntDictionary : UnitySerializedDictionary<string, int> { }
[Serializable]
public class ItemTypeSelector : EnumSelector<ItemType>
{
public ItemTypeSelector() : base(ItemType.Weapon) { }
}
// ItemType type = selector; — implicit conversion
// bool ok = selector.HasValidSelection();Import via Package Manager → GameLovers GameData → Samples
| Sample | Demonstrates |
|---|---|
| Reactive UI Demo | ObservableField, ObservableList, ComputedField, batched updates — uGUI and UI Toolkit |
| Designer Workflow | ConfigsScriptableObject, UnitySerializedDictionary, EnumSelector with PropertyDrawer |
| Migration | IConfigMigration, MigrationRunner, Config Browser migration workflow |
Contributions are welcome! Report bugs or request features via GitHub Issues. For development setup, architecture, coding standards, and test placement, see AGENTS.md.
| Document | Purpose |
|---|---|
| AGENTS.md | Contributor/agent guide (architecture, gotchas, workflows) |
| CHANGELOG.md | Version history |
- Issues: Report bugs or request features
- Discussions: Ask questions and share ideas
MIT — see LICENSE.md.