# 6. Telemetry Pitfalls and Privacy
Part of [[Playtest Telemetry and Heatmaps]]. Previous: [[5. Worked Example Classroom Playtest Loop]].
> [!warning] Code not compiled
> Fixes below refer to the scripts in parts 2 to 4, which have not been compiled in Unity.
## 6.1 Privacy and consent (read this first)
- **Tell testers what is recorded** (positions, deaths, times) and that nothing identifies them. Offer an opt-out; `recordingEnabled = false` on the recorder is the opt-out switch, and a settings toggle can set it before the first scene loads.
- **No personal data in the CSV**: no names, emails, account ids or device serials. `SystemInfo.deviceModel` (for example a headset model string) is logged in `session_start` to separate PC and Quest runs; remove it if your school policy prefers.
- **Minors**: for students under 18, follow your school's data policy. Local files that never leave the lab are the simplest case. Do not upload class telemetry to a cloud service without approval.
- **Delete when done**: once the class has read the heatmaps, delete the raw CSVs and keep only the exported grid and screenshots.
## 6.2 Culture-safe numbers
The classic bug: a machine with a comma decimal separator writes `4,21` and every row now has too many columns. Every `ToString` and `TryParse` in this series passes `CultureInfo.InvariantCulture`. If you add a column, do the same. The reader logs "bad number" warnings instead of throwing, so a single bad file will not hide the rest.
## 6.3 File growth and write cost
| Setting | Rows per 10 minutes | Notes |
|---|---|---|
| Path every 0.5 s, 0.25 m min distance | up to about 1,200 | Fine everywhere |
| Path every frame at 72 Hz (Quest) | about 43,000 | Do not do this |
| Every damage tick of a DoT effect | can explode | Log the death, not every tick |
Writes are batched (`flushIntervalSeconds`, `maxBufferedEvents`). If you raise the sample rate, profile it ([[Profiling and Optimization]], [[2. CPU Allocations and Incremental GC]]): `ToCsvRow` allocates strings, which is acceptable at 2 Hz and not at 72 Hz. For high-rate sampling, keep floats in a struct buffer and format only on flush.
## 6.4 Quest specifics
- **Pause, not quit.** Taking the headset off or opening the system menu usually pauses the app; force-closing may never call `OnApplicationQuit`. The recorder flushes in `OnApplicationPause(true)` and on a timer, so the worst case is losing the last few seconds.
- **Storage path.** `persistentDataPath` on Android is the app-specific external files directory (official page). Uninstalling the app deletes it, so pull logs *before* you sideload a new build with a different package name or uninstall.
- **USB debugging prompt.** `adb devices` shows a headset as `unauthorized` until someone puts it on and accepts the prompt. Do this for all five headsets before class.
- **Horizon OS access rules.** Whether `adb pull` of `Android/data/<package>` keeps working on every future Horizon OS build is **unverified**; if it fails, add an in-app "export" that copies the CSV to a shared folder you can reach, or send it over the LAN with the classroom networking from [[Multi-Headset Quest Classroom Sessions]].
## 6.5 Scene view limits
- The window draws one rectangle per cell every repaint. Tens of thousands of cells will make the Scene view sluggish: raise **Cell size**, filter to one event type, or turn off **Show paths**.
- Heat is drawn only in the **Scene view**, not the Game view and not in builds. That is deliberate; it is a designer tool.
- Heat sits at the average event height per cell. On stairs or overlapping floors, use **X-ray** and a smaller cell size, or filter by scene if each floor is its own scene.
## 6.6 Common logic mistakes
- **Logging the XR Origin instead of the head** in VR: the origin only moves on teleport/turn, so your path heatmap looks like a few dots. Sample the camera.
- **Respawn lines**: a path drawn from the death spot to the checkpoint. Call `BreakPath()` on respawn (part 3); the window also breaks at `death` and `teleport` rows.
- **Unstable labels**: `SpikePit (3)` vs `SpikePit`. Use data ids.
- **Comparing different builds by accident**: always set `buildLabel`. Without it, A and B are indistinguishable.
- **Editor sessions polluting class data**: your own Play-mode runs write to the same LocalLow folder as a Windows build on the same PC. Untick `recordingEnabled` while developing, or delete your sessions before class.
## 6.7 When to move online
Local CSV is right for a classroom, a jam, or a small test group you can physically collect from. Move to a hosted service when testers are remote or numerous. Unity's own option is the Analytics package (`com.unity.services.analytics`; the 6000.5 package page lists **6.3.0** as released, fetched 2026-11-12), which sits on the same Unity Gaming Services project and Authentication bootstrap used in [[Cloud Save and Unity Services]] and [[Lobby, Relay and Multiplayer Services]]. It needs player consent handling, an internet connection and a dashboard; this series does not cover its event API, and you should read its manual for consent requirements before using it with students.
## Sources
- `Application.persistentDataPath` (6000.5): https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Application-persistentDataPath.html
- Analytics for 6000.5: https://docs.unity3d.com/6000.5/Documentation/Manual/com.unity.services.analytics.html
- adb: https://developer.android.com/tools/adb