# 6. Balance Pitfalls Part of [[Game Balance and Dynamic Difficulty]]. Previous: [[5. Worked Example Arena Wave Balance]]. > [!warning] Code not compiled > References scripts from parts 2 to 5, none of which has been compiled. ## 6.1 Design pitfalls | Pitfall | Symptom | Fix | |---|---|---| | **DDA instead of design** | The adjustment sits at its limit for everyone in one section | That section is mis-tuned. Fix the curve or the encounter; DDA is for individual differences | | **Punishing skill** | Strong players notice enemies getting tougher and feel cheated | Keep `maxChallenge` small (or 0); never lower rewards when challenge rises | | **Secret DDA** | Players discover it online and feel patronised | Show "Adaptive difficulty: On" in settings; offer Off and an explicit Assist | | **Mid-fight changes** | A boss suddenly has less health; players notice | Commit only at safe points (`CommitSample()` at wave start/checkpoint) | | **Rubber-band tells** | An AI car visibly rockets past at the finish | Cap the multiplier, smooth it, apply off-screen, disable it near the finish | | **Greed loops** | Players farm one enemy type for an hour | Run the economy report; flatten the outlier or make farming deliberate | | **Death spiral** | Dying costs so much that the next attempt is harder | `keepOnDeath` in the wallet; assist after repeated deaths (*How to Win at Losing*) | ## 6.2 Technical pitfalls - **Oscillation.** If the adjuster reacts every frame with no dead zone, difficulty bounces between assist and challenge. The dead zone plus `secondsToFullAdjustment` (90 s default) damp this; do not set the time below about 30 s. - **Feedback on stale data.** After a difficulty change, the tracker still holds the old window. `SetProfile` resets it; if you add other "big" changes (new character, new weapon), call `tracker.ResetWindow()` too. - **Curve extrapolation.** `AnimationCurve.Evaluate` outside the key range uses the curve's wrap modes. `DifficultyProfile.Evaluate` clamps progress to 0..1; keep your keys at x = 0 and x = 1. - **Editing assets at runtime.** Changing a `ScriptableObject` field in Play mode in the Editor persists after you stop (it is an asset). Good for tuning sessions, surprising otherwise. Builds never write back. See [[ScriptableObject Architecture]]. - **CSV drift.** Someone edits the `EconomyTable` rows in the Inspector, then the next CSV export overwrites them. Treat the sheet as the source of truth; the rows are generated. Consider a custom inspector that shows the rows read-only ([[Custom Inspectors and Editor Tooling]]). - **Locale.** A spreadsheet exported on a machine with comma decimals writes `0,35` for unlock progress. The importer uses invariant culture, so that cell becomes 0. Export with a dot decimal locale, or store progress as integers (percent). - **Commas in cells.** The simple split in part 4 breaks on quoted cells. Keep commas out of exported cells or use a quote-aware splitter. - **Duplicate ids.** The importer keeps the first and counts duplicates in `LastImportReport`; read it after every import. ## 6.3 Multiplayer and VR - In [[Multiplayer with Netcode for GameObjects]], compute difficulty on the **server** only and replicate the committed sample (a `NetworkVariable` of a small struct or the few floats). Per-client DDA in a shared world makes no sense. - In VR on Quest, comfort is part of difficulty. Fast enemies that force quick turns are harder *and* less comfortable; prefer slower, readable enemies and use the comfort options from [[VR Locomotion and Interaction Deep Dive]]. ## 6.4 Live tuning (optional, online) For builds already in testers' hands, Unity Remote Config (`com.unity.remote-config`; the 6000.5 package page lists **4.2.5** as released, fetched 2026-11-12) can deliver key/value settings from the Unity Dashboard at runtime. A common pattern is to fetch a handful of numbers (for example `maxAssist`, `baseIncomePerMinute`) and override the profile at startup. It needs a UGS project, Authentication and an internet connection, so it sits outside the offline classroom workflow here; read its manual before use. This series does not show its API. ## 6.5 Checklist before a balance pass - [ ] Profiles exist for each difficulty; rewards are not lower on Easy - [ ] Difficulty only changes at commit points - [ ] DDA has a dead zone, a slow rate and asymmetric limits - [ ] Players can see and switch off adaptive difficulty, and can lock assist - [ ] Rubber-banding capped, smoothed, off for time trials - [ ] Economy report run with real income numbers from playtests - [ ] A change log entry with evidence for every change ## Sources - `AnimationCurve` (6000.5): https://docs.unity3d.com/6000.5/Documentation/ScriptReference/AnimationCurve.html - Remote Config for 6000.5: https://docs.unity3d.com/6000.5/Documentation/Manual/com.unity.remote-config.html