存档重构,游戏内容解锁机制;教程完善(未完成)
This commit is contained in:
133
docs/offline-content-unlock-system.md
Normal file
133
docs/offline-content-unlock-system.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Offline 内容解锁系统
|
||||
|
||||
> **适用项目**:`ichni Official`
|
||||
> **状态**:Core-003 已完成并经 Unity 运行时测试通过
|
||||
> **最后更新**:2026-07-18
|
||||
|
||||
本文是章节、歌曲和未来内容使用 Key 解锁机制的唯一使用说明。修改解锁规则、编写 Yarn 解锁命令或排查“内容为什么锁定”时,应先阅读本文。
|
||||
|
||||
## 1. 目标与边界
|
||||
|
||||
首发版本为纯 Offline。内容解锁记录在玩家本地 ES3 存档中,用于控制章节和歌曲是否可进入;它不是付费凭证、云端权益或反作弊机制。
|
||||
|
||||
解锁状态独立于下列数据域:
|
||||
|
||||
- 歌曲成绩:Accuracy、Max Combo、FC/AP、Chart Revision。
|
||||
- 剧情记录:故事树 Block、对话选项、Yarn 变量。
|
||||
- 选曲缓存:本次运行中每章节最后选中的歌曲和难度。
|
||||
|
||||
当前不实现 Payment Unlock。未来若接入商店、DLC 或在线 entitlement,应由对应模块向本系统授予同样的 Key;不得在 UI 或歌曲数据中重新建立支付专用判断。
|
||||
|
||||
## 2. 主要代码与职责
|
||||
|
||||
| 位置 | 职责 |
|
||||
|---|---|
|
||||
| `Assets/Scripts/Saving/UnlockSaveModule.cs` | Key 存档、Schema、规则求值、章节/歌曲统一授权。 |
|
||||
| `Assets/Scripts/Saving/GameSaveManager.cs` | 启动时创建并加载 `UnlockSaveModule`。 |
|
||||
| `Assets/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs` | 为章节和歌曲配置 `UnlockRequirement`。 |
|
||||
| `Assets/Scripts/NewStorySystem/YarnFunctions/StoryTreeCommands.cs` | 提供 `grant_unlock` 与兼容的 `unlock_song` Yarn 命令。 |
|
||||
| `Assets/Scripts/UI/ChapterSelection/ChapterSelectionUI.cs` | 阻止锁定章节进入剧情和选曲。 |
|
||||
| `Assets/Scripts/UI/SongSelection/PlaySongUI.cs`、`SongSelectionTab.cs` | 阻止标准 Play 与快速点击绕过锁定。 |
|
||||
|
||||
## 3. Key 命名规则
|
||||
|
||||
Key 是稳定的内部 ID,不是玩家可见文本,也不是歌曲标题、章节标题或 Yarn 节点标题。
|
||||
|
||||
- 只能包含小写英文字母、数字和下划线。
|
||||
- 必须以小写字母开头,长度为 1–96。
|
||||
- 推荐格式:`来源_章节_事件_状态`。
|
||||
- 示例:`story_ch0_prologue_completed`、`story_ch0_route_a_completed`、`chapter_ch1_available`。
|
||||
- 禁止使用点号、空格、连字符、中文/日文等本地化文本,或会频繁更名的显示名称。
|
||||
|
||||
不符合规则的 Key 无法被授予;运行时遇到配置错误时会保持锁定并只输出一次警告,避免错误配置意外开放内容。
|
||||
|
||||
## 4. 配置章节与歌曲
|
||||
|
||||
`ChapterSelectionUnit.unlockRequirement` 控制该章节的剧情入口和选曲入口。`SongItemData.unlockRequirement` 控制歌曲自身。
|
||||
|
||||
根节点为空时表示无条件开放。需要锁定时,在 Odin Inspector 的 `Unlock Requirement` 中选择下列节点:
|
||||
|
||||
| 节点 | 含义 | 使用示例 |
|
||||
|---|---|---|
|
||||
| `Key` | 玩家必须持有该 Key。 | 一首歌曲要求 `story_ch0_prologue_completed`。 |
|
||||
| `All Of (AND)` | 所有子条件都满足才开放。 | 同时完成两个剧情分支。 |
|
||||
| `Any Of (OR)` | 满足任意一个子条件即可开放。 | 完成路线 A 或路线 B。 |
|
||||
|
||||
真正开始歌曲时,章节规则和歌曲规则必须同时满足。即使歌曲本身无条件开放,只要所属章节锁定,仍不能进入。
|
||||
|
||||
空 `Key`、空的 `All Of` / `Any Of` 列表、或组合中的空子节点都被视为错误配置并失败关闭。
|
||||
|
||||
## 5. 从 Yarn 授予 Key
|
||||
|
||||
### 通用内容解锁
|
||||
|
||||
```yarn
|
||||
<<grant_unlock story_ch0_prologue_completed>>
|
||||
```
|
||||
|
||||
`grant_unlock` 立即写入本地存档,适用于章节、歌曲、教程和未来任意内容。它本身不显示提示;若需要文案,应在 Yarn 中额外使用 `show_message`。
|
||||
|
||||
### 兼容的歌曲命令
|
||||
|
||||
```yarn
|
||||
<<unlock_song story_ch0_song_02_available>>
|
||||
```
|
||||
|
||||
`unlock_song` 同样授予 Key,并在首次授予后保留旧有歌曲解锁提示行为。当前提示仍会显示英文和内部 Key;此显示问题属于后续本地化任务,不影响授权结果。
|
||||
|
||||
两个命令都具备幂等性:已拥有同一 Key 时不会重复保存或重复弹出歌曲解锁提示。
|
||||
|
||||
## 6. 存档、Schema 与清档
|
||||
|
||||
- 文件:`Application.persistentDataPath/GameSaves/UnlockKeys.json`
|
||||
- ES3 数据 Key:`UnlockKeys`
|
||||
- 当前 Schema:Unlock Save v1
|
||||
|
||||
本系统保留了旧 `UnlockKeys.json` 的文件名与 ES3 Key。此前仅保存 `HashSet<string>`、但没有 Schema 的预发布文件会作为 v0 无损读取,并补写为 v1;文件中不符合当前命名规则的 Key 会被删除。
|
||||
|
||||
若读取到非 v1、且不是旧 v0 的 Schema,当前预发布策略会重置该文件。正式发布后若变更解锁存档结构,必须编写显式迁移,不能继续删档。
|
||||
|
||||
`StoryManager.ClearAllStorySave()` 会同时清空剧情树、Yarn 变量和全部内容解锁 Key,便于测试完整剧情进度。它不会清除歌曲成绩。
|
||||
|
||||
## 7. 入口防护
|
||||
|
||||
所有现有可进入内容的入口均需遵守同一授权规则:
|
||||
|
||||
| 入口 | 防护行为 |
|
||||
|---|---|
|
||||
| 章节剧情入口 | 按钮禁用,并在点击回调中再次检查章节规则。 |
|
||||
| 章节选曲入口 | 按钮禁用,并在点击回调中再次检查章节规则。 |
|
||||
| 歌曲标准 Play | 通过 `UnlockSaveModule.CanEnterSong(...)` 作最终检查。 |
|
||||
| 歌曲快速点击 | 使用同一最终检查,不能绕过标准 Play。 |
|
||||
|
||||
`SongSelectionTab.isLocked` 只用于锁图标和预览表现,不能作为安全判断的唯一来源。剧情刚授予 Key 或 UI 被其他逻辑刷新后,真正进入前仍会重新计算规则。
|
||||
|
||||
## 8. 内容作者工作流
|
||||
|
||||
1. 先定义需要代表的进度事实,并按规则起一个稳定 Key。
|
||||
2. 在 Chapter 或 Song 的 `Unlock Requirement` 中引用这个 Key 或组合规则。
|
||||
3. 在对应 Yarn 节点完成时添加 `grant_unlock` 或兼容的 `unlock_song`。
|
||||
4. 使用新档测试锁定状态,再完成一次剧情测试授予后的即时状态。
|
||||
5. 重启游戏确认 Key 仍存在;清空剧情存档后确认内容重新锁定。
|
||||
|
||||
不要为了“解锁某首歌”而直接修改 UI、`isLocked` 或 `UnlockKeys.json`;所有授予必须经由 `UnlockSaveModule.GrantKey(...)` 或 Yarn 命令。
|
||||
|
||||
## 9. 未来扩展原则
|
||||
|
||||
- 新内容类型优先复用 `UnlockRequirement` 和 `UnlockSaveModule`。
|
||||
- 需要更复杂的组合时在现有规则树中增加节点,不要在各页面复制 if 判断。
|
||||
- 商店、DLC、成就或云端 entitlement 将来只负责授予/撤销 Key;章节和歌曲 UI 不应关心 Key 的来源。
|
||||
- 如需玩家可见的“为何锁定”说明,应添加本地化提示 Key,不要展示内部 Unlock Key。
|
||||
- `UnlockSaveModule.UnlockStateChanged` 预留给未来需要在当前页面即时刷新解锁状态的 UI。
|
||||
|
||||
## 10. 回归测试清单
|
||||
|
||||
1. 无规则的章节和歌曲在新档可进入。
|
||||
2. 配置 `Key` 后,新档中章节/歌曲锁定。
|
||||
3. 锁定歌曲的标准 Play 和快速点击均无法进入。
|
||||
4. 锁定章节的剧情入口和选曲入口均无法进入。
|
||||
5. `grant_unlock` 后规则立即满足;返回菜单后显示正确。
|
||||
6. 重启后 Key 仍存在。
|
||||
7. `All Of` 只有全部 Key 存在时开放;`Any Of` 任一 Key 存在时开放。
|
||||
8. 空 Key 或错误组合不会开放内容,并只产生一次可定位警告。
|
||||
9. `ClearAllStorySave()` 后剧情进度与解锁 Key 均清除,歌曲成绩保留。
|
||||
Reference in New Issue
Block a user