@musashishao/agent-kit 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.agent/agents/ai-asset-factory.md +700 -0
  2. package/.agent/agents/ai-audio-factory.md +503 -0
  3. package/.agent/agents/game-developer.md +4 -4
  4. package/.agent/agents/orchestrator.md +113 -3
  5. package/.agent/agents/project-planner.md +67 -0
  6. package/.agent/agents/unity-mobile-master.md +949 -0
  7. package/.agent/mcp/config/registry.json +65 -51
  8. package/.agent/mcp/servers/notebooklm/README.md +114 -0
  9. package/.agent/mcp/servers/notebooklm/package.json +35 -0
  10. package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
  11. package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
  12. package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
  13. package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
  14. package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
  15. package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
  16. package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
  17. package/.agent/mcp-gateway/README.md +169 -20
  18. package/.agent/mcp-gateway/package.json +22 -7
  19. package/.agent/mcp-gateway/src/auth/index.ts +55 -0
  20. package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
  21. package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
  22. package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
  23. package/.agent/mcp-gateway/src/index.ts +252 -105
  24. package/.agent/mcp-gateway/src/observability/index.ts +5 -0
  25. package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
  26. package/.agent/mcp-gateway/src/transports/index.ts +5 -0
  27. package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
  28. package/.agent/rules/CODEX.md +89 -0
  29. package/.agent/rules/CODE_RULES.md +73 -0
  30. package/.agent/rules/GEMINI.md +25 -0
  31. package/.agent/rules/MEMORY_STATE.md +110 -0
  32. package/.agent/rules/REFERENCE.md +33 -141
  33. package/.agent/rules/REF_SKILLS.md +116 -0
  34. package/.agent/rules/REF_WORKFLOWS.md +81 -0
  35. package/.agent/scripts/ak_cli.py +106 -5
  36. package/.agent/scripts/memory_manager.py +48 -9
  37. package/.agent/skills/anti-hallucination/SKILL.md +295 -0
  38. package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
  39. package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
  40. package/.agent/skills/brainstorming/SKILL.md +80 -6
  41. package/.agent/skills/decision-memory/SKILL.md +317 -0
  42. package/.agent/skills/emergence-detector/SKILL.md +230 -0
  43. package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
  44. package/.agent/skills/explained-qa/SKILL.md +142 -0
  45. package/.agent/skills/explained-qa/game-terminology.md +214 -0
  46. package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
  47. package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
  48. package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
  49. package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
  50. package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
  51. package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
  52. package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
  53. package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
  54. package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
  55. package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
  56. package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
  57. package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
  58. package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
  59. package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
  60. package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
  61. package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
  62. package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
  63. package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
  64. package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
  65. package/.agent/skills/game-development/liveops/SKILL.md +308 -0
  66. package/.agent/skills/game-development/localization/SKILL.md +286 -0
  67. package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
  68. package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
  69. package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
  70. package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
  71. package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
  72. package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
  73. package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
  74. package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
  75. package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
  76. package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
  77. package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
  78. package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
  79. package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
  80. package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
  81. package/.agent/skills/intent-capture/SKILL.md +65 -0
  82. package/.agent/skills/mcp-composition/SKILL.md +362 -0
  83. package/.agent/skills/mcp-observability/SKILL.md +323 -0
  84. package/.agent/skills/mcp-security/SKILL.md +314 -0
  85. package/.agent/skills/trust-spectrum/SKILL.md +291 -0
  86. package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
  87. package/.agent/templates/AGENTS.game.md +63 -0
  88. package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
  89. package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
  90. package/.agent/workflows/ai-agent.md +2 -0
  91. package/.agent/workflows/autofix.md +1 -0
  92. package/.agent/workflows/brainstorm.md +1 -0
  93. package/.agent/workflows/context.md +1 -0
  94. package/.agent/workflows/create.md +39 -8
  95. package/.agent/workflows/dashboard.md +1 -0
  96. package/.agent/workflows/debug.md +14 -0
  97. package/.agent/workflows/deploy.md +14 -0
  98. package/.agent/workflows/enhance.md +44 -0
  99. package/.agent/workflows/gamekit-init.md +177 -0
  100. package/.agent/workflows/gamekit-launch.md +338 -0
  101. package/.agent/workflows/gamekit-plan.md +204 -0
  102. package/.agent/workflows/gamekit-qa.md +153 -0
  103. package/.agent/workflows/gamekit-spec.md +243 -0
  104. package/.agent/workflows/gamekit-tasks.md +208 -0
  105. package/.agent/workflows/marketing.md +2 -0
  106. package/.agent/workflows/next.md +1 -0
  107. package/.agent/workflows/orchestrate.md +12 -0
  108. package/.agent/workflows/pentest.md +2 -0
  109. package/.agent/workflows/plan.md +42 -0
  110. package/.agent/workflows/preview.md +1 -0
  111. package/.agent/workflows/quality.md +1 -0
  112. package/.agent/workflows/saas.md +2 -0
  113. package/.agent/workflows/spec.md +42 -0
  114. package/.agent/workflows/status.md +1 -0
  115. package/.agent/workflows/test.md +14 -0
  116. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  117. package/bin/cli.js +411 -111
  118. package/package.json +1 -2
  119. package/.agent/agents/game-asset-curator.md +0 -317
  120. package/.agent/agents/game-narrative-designer.md +0 -310
  121. package/.agent/agents/game-qa-agent.md +0 -441
  122. package/.agent/workflows/game-prototype.md +0 -154
  123. package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
  124. package/docs/CHANGELOG_AI_INFRA.md +0 -141
  125. package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
@@ -0,0 +1,535 @@
1
+ ---
2
+ name: data-driven-balance
3
+ description: Data-driven game balancing systems. External configuration files, hot-patching values, A/B testing for game balance, spreadsheet imports, and live tuning for Unity.
4
+ ---
5
+
6
+ # Data-Driven Balance Skill
7
+
8
+ > **Purpose**: Enable rapid iteration and live balancing without app updates.
9
+
10
+ ## When to Use
11
+ - Setting up external config files
12
+ - Implementing hot-patching for balance
13
+ - Running A/B tests for game values
14
+ - Importing from spreadsheets
15
+
16
+ ---
17
+
18
+ ## 1. External Configuration System
19
+
20
+ ### JSON Config Pattern
21
+ ```csharp
22
+ [System.Serializable]
23
+ public class GameBalanceConfig
24
+ {
25
+ public PlayerConfig player;
26
+ public EnemyConfig[] enemies;
27
+ public ItemConfig[] items;
28
+ public EconomyConfig economy;
29
+ }
30
+
31
+ [System.Serializable]
32
+ public class PlayerConfig
33
+ {
34
+ public float baseHealth = 100f;
35
+ public float baseAttack = 10f;
36
+ public float baseMoveSpeed = 5f;
37
+ public float healthPerLevel = 10f;
38
+ public float attackPerLevel = 2f;
39
+
40
+ // Cultivation specific
41
+ public float[] qiRequirementsPerRealm;
42
+ public float breakthroughBaseChance = 0.5f;
43
+ }
44
+
45
+ [System.Serializable]
46
+ public class EnemyConfig
47
+ {
48
+ public string enemyId;
49
+ public float health;
50
+ public float attack;
51
+ public float expReward;
52
+ public float goldDrop;
53
+ public float[] dropRates;
54
+ }
55
+
56
+ [System.Serializable]
57
+ public class EconomyConfig
58
+ {
59
+ public int startingGold = 100;
60
+ public float goldMultiplier = 1f;
61
+ public float expMultiplier = 1f;
62
+ public int dailyRewardBase = 50;
63
+ public float[] dailyRewardStreak; // Day 1, 2, 3... bonuses
64
+ }
65
+ ```
66
+
67
+ ### Config Loader
68
+ ```csharp
69
+ public class ConfigManager : MonoBehaviour
70
+ {
71
+ public static ConfigManager Instance { get; private set; }
72
+
73
+ private GameBalanceConfig _config;
74
+ private string _configVersion;
75
+
76
+ public GameBalanceConfig Config => _config;
77
+
78
+ public event Action<GameBalanceConfig> OnConfigLoaded;
79
+ public event Action<GameBalanceConfig> OnConfigUpdated;
80
+
81
+ private void Awake()
82
+ {
83
+ Instance = this;
84
+ LoadLocalConfig();
85
+ }
86
+
87
+ private void Start()
88
+ {
89
+ // Fetch remote on start
90
+ StartCoroutine(FetchRemoteConfig());
91
+ }
92
+
93
+ private void LoadLocalConfig()
94
+ {
95
+ // Load bundled default
96
+ TextAsset defaultConfig = Resources.Load<TextAsset>("default_config");
97
+ _config = JsonUtility.FromJson<GameBalanceConfig>(defaultConfig.text);
98
+
99
+ // Check for cached remote
100
+ string cached = PlayerPrefs.GetString("cached_config", "");
101
+ if (!string.IsNullOrEmpty(cached))
102
+ {
103
+ _config = JsonUtility.FromJson<GameBalanceConfig>(cached);
104
+ }
105
+
106
+ OnConfigLoaded?.Invoke(_config);
107
+ }
108
+
109
+ private IEnumerator FetchRemoteConfig()
110
+ {
111
+ string url = "https://your-cdn.com/config/balance.json";
112
+
113
+ using UnityWebRequest request = UnityWebRequest.Get(url);
114
+ yield return request.SendWebRequest();
115
+
116
+ if (request.result == UnityWebRequest.Result.Success)
117
+ {
118
+ string json = request.downloadHandler.text;
119
+ var newConfig = JsonUtility.FromJson<GameBalanceConfig>(json);
120
+
121
+ // Cache locally
122
+ PlayerPrefs.SetString("cached_config", json);
123
+
124
+ // Apply if changed
125
+ if (HasConfigChanged(newConfig))
126
+ {
127
+ _config = newConfig;
128
+ OnConfigUpdated?.Invoke(_config);
129
+ }
130
+ }
131
+ }
132
+
133
+ public T GetValue<T>(string path)
134
+ {
135
+ // Access nested values via path like "player.baseHealth"
136
+ return ReflectionHelper.GetNestedValue<T>(_config, path);
137
+ }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 2. Firebase Remote Config
144
+
145
+ ### Firebase Integration
146
+ ```csharp
147
+ public class FirebaseConfigManager : MonoBehaviour
148
+ {
149
+ private FirebaseRemoteConfig _remoteConfig;
150
+
151
+ public async Task Initialize()
152
+ {
153
+ await FirebaseApp.CheckAndFixDependenciesAsync();
154
+
155
+ _remoteConfig = FirebaseRemoteConfig.DefaultInstance;
156
+
157
+ // Set defaults
158
+ var defaults = new Dictionary<string, object>
159
+ {
160
+ { "player_base_health", 100 },
161
+ { "player_base_attack", 10 },
162
+ { "exp_multiplier", 1.0 },
163
+ { "gold_multiplier", 1.0 },
164
+ { "daily_reward_base", 50 },
165
+ { "feature_flag_new_mode", false }
166
+ };
167
+
168
+ await _remoteConfig.SetDefaultsAsync(defaults);
169
+ }
170
+
171
+ public async Task FetchAndActivate()
172
+ {
173
+ // Fetch with cache expiration
174
+ await _remoteConfig.FetchAsync(TimeSpan.FromHours(1));
175
+ await _remoteConfig.ActivateAsync();
176
+
177
+ GameEvents.OnRemoteConfigUpdated?.Invoke();
178
+ }
179
+
180
+ public float GetFloat(string key)
181
+ {
182
+ return (float)_remoteConfig.GetValue(key).DoubleValue;
183
+ }
184
+
185
+ public int GetInt(string key)
186
+ {
187
+ return (int)_remoteConfig.GetValue(key).LongValue;
188
+ }
189
+
190
+ public bool GetBool(string key)
191
+ {
192
+ return _remoteConfig.GetValue(key).BooleanValue;
193
+ }
194
+
195
+ public string GetString(string key)
196
+ {
197
+ return _remoteConfig.GetValue(key).StringValue;
198
+ }
199
+
200
+ public T GetJson<T>(string key)
201
+ {
202
+ string json = GetString(key);
203
+ return JsonUtility.FromJson<T>(json);
204
+ }
205
+ }
206
+ ```
207
+
208
+ ---
209
+
210
+ ## 3. A/B Testing for Balance
211
+
212
+ ### Experiment System
213
+ ```csharp
214
+ [System.Serializable]
215
+ public class BalanceExperiment
216
+ {
217
+ public string experimentId;
218
+ public string experimentName;
219
+ public bool isActive;
220
+
221
+ public ExperimentVariant[] variants;
222
+ public string[] targetUserGroups; // "new_users", "paying_users"
223
+
224
+ public float[] variantWeights; // Distribution
225
+ }
226
+
227
+ [System.Serializable]
228
+ public class ExperimentVariant
229
+ {
230
+ public string variantId;
231
+ public string variantName;
232
+
233
+ public BalanceOverride[] overrides;
234
+ }
235
+
236
+ [System.Serializable]
237
+ public class BalanceOverride
238
+ {
239
+ public string configPath;
240
+ public string valueJson;
241
+ }
242
+
243
+ public class ABTestManager : MonoBehaviour
244
+ {
245
+ private Dictionary<string, string> _userAssignments = new();
246
+
247
+ public event Action<string, string> OnExperimentAssigned;
248
+
249
+ public void AssignToExperiment(BalanceExperiment experiment)
250
+ {
251
+ if (_userAssignments.ContainsKey(experiment.experimentId))
252
+ return; // Already assigned
253
+
254
+ // Consistent assignment based on user ID
255
+ int hash = GetUserHash(experiment.experimentId);
256
+ float roll = (hash % 1000) / 1000f;
257
+
258
+ float cumulative = 0f;
259
+ for (int i = 0; i < experiment.variants.Length; i++)
260
+ {
261
+ cumulative += experiment.variantWeights[i];
262
+ if (roll < cumulative)
263
+ {
264
+ string variantId = experiment.variants[i].variantId;
265
+ _userAssignments[experiment.experimentId] = variantId;
266
+
267
+ ApplyVariant(experiment.variants[i]);
268
+
269
+ // Log for analytics
270
+ Analytics.LogEvent("experiment_assigned", new Dictionary<string, object>
271
+ {
272
+ { "experiment_id", experiment.experimentId },
273
+ { "variant_id", variantId }
274
+ });
275
+
276
+ OnExperimentAssigned?.Invoke(experiment.experimentId, variantId);
277
+ break;
278
+ }
279
+ }
280
+ }
281
+
282
+ private void ApplyVariant(ExperimentVariant variant)
283
+ {
284
+ foreach (var override_ in variant.overrides)
285
+ {
286
+ ConfigManager.Instance.SetOverride(override_.configPath, override_.valueJson);
287
+ }
288
+ }
289
+
290
+ public string GetAssignedVariant(string experimentId)
291
+ {
292
+ return _userAssignments.TryGetValue(experimentId, out var variant) ? variant : null;
293
+ }
294
+ }
295
+ ```
296
+
297
+ ---
298
+
299
+ ## 4. Spreadsheet Import
300
+
301
+ ### Google Sheets Integration
302
+ ```csharp
303
+ public class SpreadsheetImporter
304
+ {
305
+ private const string SHEET_ID = "your-google-sheet-id";
306
+ private const string API_KEY = "your-api-key";
307
+
308
+ public async Task<List<EnemyConfig>> ImportEnemies(string sheetName)
309
+ {
310
+ string url = $"https://sheets.googleapis.com/v4/spreadsheets/{SHEET_ID}/values/{sheetName}?key={API_KEY}";
311
+
312
+ using var client = new HttpClient();
313
+ string json = await client.GetStringAsync(url);
314
+
315
+ var response = JsonConvert.DeserializeObject<SheetsResponse>(json);
316
+
317
+ var enemies = new List<EnemyConfig>();
318
+ var headers = response.values[0]; // First row = headers
319
+
320
+ for (int i = 1; i < response.values.Count; i++)
321
+ {
322
+ var row = response.values[i];
323
+
324
+ enemies.Add(new EnemyConfig
325
+ {
326
+ enemyId = row[0],
327
+ health = float.Parse(row[1]),
328
+ attack = float.Parse(row[2]),
329
+ expReward = float.Parse(row[3]),
330
+ goldDrop = float.Parse(row[4])
331
+ });
332
+ }
333
+
334
+ return enemies;
335
+ }
336
+ }
337
+
338
+ #if UNITY_EDITOR
339
+ public class SpreadsheetImportEditor : EditorWindow
340
+ {
341
+ [MenuItem("Tools/Import Balance from Sheets")]
342
+ public static async void ImportFromSheets()
343
+ {
344
+ var importer = new SpreadsheetImporter();
345
+
346
+ // Import enemies
347
+ var enemies = await importer.ImportEnemies("Enemies");
348
+ SaveToScriptableObjects(enemies);
349
+
350
+ Debug.Log($"Imported {enemies.Count} enemies from spreadsheet");
351
+ }
352
+ }
353
+ #endif
354
+ ```
355
+
356
+ ### CSV Import for Offline Use
357
+ ```csharp
358
+ public class CSVImporter
359
+ {
360
+ public static List<T> Import<T>(string csvPath) where T : new()
361
+ {
362
+ var results = new List<T>();
363
+ var lines = File.ReadAllLines(csvPath);
364
+
365
+ if (lines.Length < 2) return results;
366
+
367
+ var headers = ParseCSVLine(lines[0]);
368
+ var type = typeof(T);
369
+
370
+ for (int i = 1; i < lines.Length; i++)
371
+ {
372
+ var values = ParseCSVLine(lines[i]);
373
+ var item = new T();
374
+
375
+ for (int j = 0; j < headers.Length && j < values.Length; j++)
376
+ {
377
+ var field = type.GetField(headers[j]);
378
+ if (field != null)
379
+ {
380
+ object value = Convert.ChangeType(values[j], field.FieldType);
381
+ field.SetValue(item, value);
382
+ }
383
+ }
384
+
385
+ results.Add(item);
386
+ }
387
+
388
+ return results;
389
+ }
390
+
391
+ private static string[] ParseCSVLine(string line)
392
+ {
393
+ // Handle quoted values with commas
394
+ var regex = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
395
+ return regex.Split(line).Select(s => s.Trim('"')).ToArray();
396
+ }
397
+ }
398
+ ```
399
+
400
+ ---
401
+
402
+ ## 5. Live Tuning Dashboard
403
+
404
+ ### In-Game Debug Panel
405
+ ```csharp
406
+ public class LiveTuningPanel : MonoBehaviour
407
+ {
408
+ [SerializeField] private GameObject _panelRoot;
409
+ [SerializeField] private Transform _sliderContainer;
410
+ [SerializeField] private GameObject _sliderPrefab;
411
+
412
+ private Dictionary<string, Slider> _sliders = new();
413
+
414
+ private void Start()
415
+ {
416
+ #if DEVELOPMENT_BUILD || UNITY_EDITOR
417
+ CreateTuningSliders();
418
+ #else
419
+ _panelRoot.SetActive(false);
420
+ #endif
421
+ }
422
+
423
+ private void CreateTuningSliders()
424
+ {
425
+ CreateSlider("player.baseHealth", 50, 200, 100);
426
+ CreateSlider("player.baseAttack", 5, 50, 10);
427
+ CreateSlider("economy.goldMultiplier", 0.5f, 3f, 1f);
428
+ CreateSlider("economy.expMultiplier", 0.5f, 3f, 1f);
429
+ }
430
+
431
+ private void CreateSlider(string configPath, float min, float max, float defaultValue)
432
+ {
433
+ var go = Instantiate(_sliderPrefab, _sliderContainer);
434
+ var slider = go.GetComponentInChildren<Slider>();
435
+ var label = go.GetComponentInChildren<TMP_Text>();
436
+
437
+ slider.minValue = min;
438
+ slider.maxValue = max;
439
+ slider.value = ConfigManager.Instance.GetValue<float>(configPath);
440
+
441
+ label.text = $"{configPath}: {slider.value:F1}";
442
+
443
+ slider.onValueChanged.AddListener(value =>
444
+ {
445
+ ConfigManager.Instance.SetOverride(configPath, value);
446
+ label.text = $"{configPath}: {value:F1}";
447
+ });
448
+
449
+ _sliders[configPath] = slider;
450
+ }
451
+
452
+ public void ExportCurrentValues()
453
+ {
454
+ var overrides = new Dictionary<string, float>();
455
+
456
+ foreach (var kvp in _sliders)
457
+ {
458
+ overrides[kvp.Key] = kvp.Value.value;
459
+ }
460
+
461
+ string json = JsonConvert.SerializeObject(overrides, Formatting.Indented);
462
+ GUIUtility.systemCopyBuffer = json;
463
+
464
+ Debug.Log($"Copied to clipboard:\n{json}");
465
+ }
466
+ }
467
+ ```
468
+
469
+ ---
470
+
471
+ ## 6. Balance Analytics
472
+
473
+ ### Track Balance Metrics
474
+ ```csharp
475
+ public class BalanceAnalytics : MonoBehaviour
476
+ {
477
+ public void LogCombatResult(CombatResult result)
478
+ {
479
+ Analytics.LogEvent("combat_end", new Dictionary<string, object>
480
+ {
481
+ { "player_level", result.playerLevel },
482
+ { "enemy_id", result.enemyId },
483
+ { "victory", result.playerWon },
484
+ { "duration_seconds", result.durationSeconds },
485
+ { "damage_dealt", result.playerDamageDealt },
486
+ { "damage_taken", result.playerDamageTaken },
487
+ { "skills_used", result.skillsUsed },
488
+ { "items_consumed", result.itemsConsumed },
489
+ { "player_health_percent", result.playerHealthPercent }
490
+ });
491
+ }
492
+
493
+ public void LogEconomyAction(string action, int amount, string source)
494
+ {
495
+ Analytics.LogEvent("economy", new Dictionary<string, object>
496
+ {
497
+ { "action", action }, // "gain", "spend"
498
+ { "currency", "gold" },
499
+ { "amount", amount },
500
+ { "source", source }, // "quest", "shop", "loot"
501
+ { "balance_after", PlayerStats.Gold }
502
+ });
503
+ }
504
+
505
+ public void LogProgressionMilestone(string milestone, int attempts, float timeSpentMinutes)
506
+ {
507
+ Analytics.LogEvent("milestone", new Dictionary<string, object>
508
+ {
509
+ { "milestone", milestone },
510
+ { "attempts", attempts },
511
+ { "time_minutes", timeSpentMinutes },
512
+ { "player_level", PlayerStats.Level }
513
+ });
514
+ }
515
+ }
516
+ ```
517
+
518
+ ---
519
+
520
+ ## Anti-Patterns
521
+
522
+ | ❌ Don't | ✅ Do |
523
+ |----------|-------|
524
+ | Hardcode balance values | External configs |
525
+ | Require app update to balance | Hot-patch via Remote Config |
526
+ | Guess what to tune | Track metrics, A/B test |
527
+ | Manual data entry | Import from spreadsheets |
528
+ | No version control for configs | Git-track config files |
529
+
530
+ ---
531
+
532
+ ## Related Skills
533
+ - `game-development/liveops` - Live operations
534
+ - `game-development/game-economy-designer` - Economy design
535
+ - `game-development/game-analytics-integrator` - Analytics