7.2 KiB
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
通用内容解锁
<<grant_unlock story_ch0_prologue_completed>>
grant_unlock 立即写入本地存档,适用于章节、歌曲、教程和未来任意内容。它本身不显示提示;若需要文案,应在 Yarn 中额外使用 show_message。
兼容的歌曲命令
<<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. 内容作者工作流
- 先定义需要代表的进度事实,并按规则起一个稳定 Key。
- 在 Chapter 或 Song 的
Unlock Requirement中引用这个 Key 或组合规则。 - 在对应 Yarn 节点完成时添加
grant_unlock或兼容的unlock_song。 - 使用新档测试锁定状态,再完成一次剧情测试授予后的即时状态。
- 重启游戏确认 Key 仍存在;清空剧情存档后确认内容重新锁定。
不要为了“解锁某首歌”而直接修改 UI、isLocked 或 UnlockKeys.json;所有授予必须经由 UnlockSaveModule.GrantKey(...) 或 Yarn 命令。
9. 未来扩展原则
- 新内容类型优先复用
UnlockRequirement和UnlockSaveModule。 - 需要更复杂的组合时在现有规则树中增加节点,不要在各页面复制 if 判断。
- 商店、DLC、成就或云端 entitlement 将来只负责授予/撤销 Key;章节和歌曲 UI 不应关心 Key 的来源。
- 如需玩家可见的“为何锁定”说明,应添加本地化提示 Key,不要展示内部 Unlock Key。
UnlockSaveModule.UnlockStateChanged预留给未来需要在当前页面即时刷新解锁状态的 UI。
10. 回归测试清单
- 无规则的章节和歌曲在新档可进入。
- 配置
Key后,新档中章节/歌曲锁定。 - 锁定歌曲的标准 Play 和快速点击均无法进入。
- 锁定章节的剧情入口和选曲入口均无法进入。
grant_unlock后规则立即满足;返回菜单后显示正确。- 重启后 Key 仍存在。
All Of只有全部 Key 存在时开放;Any Of任一 Key 存在时开放。- 空 Key 或错误组合不会开放内容,并只产生一次可定位警告。
ClearAllStorySave()后剧情进度与解锁 Key 均清除,歌曲成绩保留。