存档重构,游戏内容解锁机制;教程完善(未完成)

This commit is contained in:
SoulliesOfficial
2026-07-18 16:51:18 -04:00
parent d48ef1e65e
commit dda354ebb9
123 changed files with 4032 additions and 558 deletions

View 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 节点标题。
- 只能包含小写英文字母、数字和下划线。
- 必须以小写字母开头,长度为 196。
- 推荐格式:`来源_章节_事件_状态`
- 示例:`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`
- 当前 SchemaUnlock 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 均清除,歌曲成绩保留。