# 52 · 完成度与多结局设计(Completion & Ending Design) > **命名空间** `BaseGames.Completion` > **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md) > **依赖** `BaseGames.Core.Events` · `BaseGames.Narrative`(NarrativeStateMachine)· `BaseGames.World`(SaveManager) > **关联** 50_NarrativeDesignSystem(结局门控)· 14_ProgressionSystem · 32_AchievementSystem · 16_MapSystem --- ## 目录 1. [系统总览](#1-系统总览) 2. [完成度计算规则](#2-完成度计算规则) 3. [完成度显示界面](#3-完成度显示界面) 4. [NG+(新游戏+)设计](#4-ng新游戏-设计) 5. [多结局实现流程](#5-多结局实现流程) 6. [隐藏 Boss / 隐藏区域门控](#6-隐藏-boss--隐藏区域门控) 7. [收集品查看界面(Codex + 收藏室)](#7-收集品查看界面codex--收藏室) 8. [完成度与成就联动](#8-完成度与成就联动) 9. [SaveData 集成](#9-savedata-集成) 10. [事件频道](#10-事件频道) 11. [编辑器友好设计](#11-编辑器友好设计) --- ## 1. 系统总览 完成度系统为 Metroidvania 品类的核心留存引擎:玩家通关后看到"68%",会被驱动去探索剩余 32%。 ``` 完成度系统职责: ├─ CompletionTracker → 追踪所有计入完成度的元素状态 ├─ CompletionCalculator → 实时计算并缓存完成度百分比 ├─ CompletionUI → 暂停菜单中的完成度概览面板 ├─ NgPlusManager → NG+ 流程管理(继承哪些数据、新增哪些内容) └─ EndingDirector → 根据完成度和叙事状态导演最终结局 ``` --- ## 2. 完成度计算规则 ### 2.1 计入完成度的元素及权重 | 类别 | 总分 | 子元素 | 每项分值 | |------|------|--------|---------| | **Boss 击败** | 250 | 5 个主线 Boss × 50 | 50 / Boss | | **房间探索** | 200 | ~100 个非 Boss 房间 × 2 | 2 / 房间 | | **能力获取** | 100 | 10 个能力道具 × 10 | 10 / 能力 | | **核心符文** | 80 | 16 枚核心 CharmSO × 5 | 5 / 符文 | | **幻境碎片** | 100 | 10 个 × 10 | 10 / 碎片 | | **Lore 道具** | 150 | ~45 件 × 约 3.3 | ≈3.3 / 件 | | **NPC 最大好感度** | 80 | 4 个 NPC × 20 | 20 / NPC | | **挑战房间 S 级** | 40 | 5 个挑战房间 × 8 | 8 / 挑战 | | **全地图探索** | 0(隐藏加分)| 探索全部房间 | 解锁隐藏成就,不计入% | | **合计** | **1000 分(100%)** | | | **完成度百分比 = 当前总分 / 1000 × 100%** > 分值设计约束:Boss 和房间探索构成基础 45%(主线通关约达到 35~40%),收集品构成另 55%(鼓励深度探索)。 ### 2.2 不计入完成度的内容 | 内容 | 原因 | |------|------| | 普通 NPC 对话 | 太多台词,不应成为义务 | | 道具使用次数 | 不应要求玩家刷数值 | | 死亡次数 | 不惩罚探索型玩家 | | 钢铁之魂通关 | 计入独立成就,不影响主线 % | ### 2.3 CompletionTracker ```csharp namespace BaseGames.Completion { public class CompletionTracker : MonoBehaviour { [SerializeField] CompletionConfigSO _config; // SO:定义所有元素及分值 [SerializeField] WorldStateRegistry _worldState; // 叙事标志来源 [SerializeField] SaveData _saveData; // 数据来源(注入) // 实时计算当前完成度分数(缓存,每次 SaveData 变化时更新) public int CurrentScore { get; private set; } public float CompletionPercent => (float)CurrentScore / _config.TotalScore * 100f; public void RecalculateScore() { int score = 0; foreach (var element in _config.Elements) score += element.CalculateScore(_saveData, _worldState); CurrentScore = score; } // 订阅所有可能影响完成度的事件频道,任意变化时重新计算 void OnEnable() { _onBossDefeated.OnEventRaised += _ => RecalculateScore(); _onLoreCollected.OnEventRaised += _ => RecalculateScore(); _onRoomEntered.OnEventRaised += _ => RecalculateScore(); // ... 其余事件 } } } ``` --- ## 3. 完成度显示界面 ### 3.1 暂停菜单完成度概览 暂停菜单 → "完成度" Tab 页(与任务日志并列): ``` ┌─────────────────────────────────────────────────────────────┐ │ 游戏完成度 │ │ │ │ 总体进度: ████████████████░░░░░░░░░░░░░░░ 62% │ │ │ │ ▸ Boss 击败 ██████████████████████ 4 / 5 │ │ ▸ 房间探索 ████████████████░░░░░░ 78 / 100 │ │ ▸ 能力获取 ████████████████████░░ 9 / 10 │ │ ▸ 核心符文 ████████░░░░░░░░░░░░░░ 7 / 16 │ │ ▸ 幻境碎片 ████████████░░░░░░░░░░ 6 / 10 │ │ ▸ Lore 道具 ██████░░░░░░░░░░░░░░░░ 14 / 45 │ │ ▸ NPC 好感满级 ██████████░░░░░░░░░░░░ 2 / 4 │ │ ▸ 挑战房间 S 级 ████████████████░░░░░░ 3 / 5 │ │ │ │ [查看图鉴] [查看地图] │ └─────────────────────────────────────────────────────────────┘ ``` - 每个类别可点击展开,显示具体哪些已完成/哪些未完成(未完成项显示"???"隐藏具体内容) - 各类别的未完成项,仅当玩家到达该内容所在区域后才从"???"变为具体提示 - 完成度达到 80%、90%、100% 时各有一次屏幕闪光 + 成就解锁 ### 3.2 通关结算画面 击败最终 Boss 后,在加载结局过场动画前,显示本次游玩的结算数据: ``` ┌──────────────────────────────────────────────────────────────┐ │ 泽灵的旅程 │ │ │ │ 游玩时长 22小时 34分 │ │ 死亡次数 47 次 │ │ 总完成度 68% │ │ 结局 真实结局 · 幻境之归 │ │ │ │ [ 继续游玩(探索剩余内容) ] [ NG+ 开始 ] │ └──────────────────────────────────────────────────────────────┘ ``` --- ## 4. NG+(新游戏+)设计 ### 4.1 NG+ 开放条件 - 任意结局通关后,在主菜单该存档槽上显示"NG+"标记 - 进入 NG+ 时,可选择继承程度(见下方) ### 4.2 NG+ 继承选项(玩家选择) | 继承项 | 默认 | 选项说明 | |--------|------|---------| | 符文收集 | ✅ 继承 | 所有已获得 CharmSO | | 能力解锁 | ✅ 继承 | 玩家已获得的运动能力 | | 地图探索 | ✅ 继承 | 已探索的房间记录 | | Geo 数量 | ✅ 继承 | 通关时持有的 Geo | | 叙事进度 | ❌ 重置 | 故事节点、NPC 台词重置,但 NPC 会"认得"玩家 | | 好感度 | 50% 继承 | 好感度减半(NPC 记得玩家但需重新深交)| | Lore 收集 | ✅ 继承 | 图鉴内容保留 | | Boss 进程 | ❌ 重置 | Boss 重新出现,但有 NG+ 强化版行为 | ### 4.3 NG+ 专属内容 每个 NG+ 层(最多 NG+3)叠加以下变化: | NG+ 层级 | 新增内容 | |---------|---------| | **NG+1** | Boss 新增隐藏攻击相位(第3阶段);敌人攻击频率 ×1.15;解锁"命魂·极限形态"符文 | | **NG+2** | 新增隐藏 Boss 入口(幻境回廊,仅 NG+2 起可进入);所有精英怪新增强化变体外观 | | **NG+3** | 解锁"逆位泽灵"皮肤;开放隐藏收藏室最终房间(含最后 3 件 Lore)| ### 4.4 NG+ 保护规则 - NG+ 不会覆盖原存档,系统在同一槽位的 slot 内创建 `ng_plus_1` 子存档 - 玩家可在 NG+ 中随时回到原通关存档继续探索 ### 4.5 NPC 对 NG+ 的特殊台词 若 `SaveData.ngPlusCount >= 1`,所有 NPC 初次见到玩家时会有特殊台词版本: > 商人 MerchantA:"你……以前来过这里?你的眼神像是承载过整个世界的重量。" 此版本台词通过 `DialogueVersion.requiredFlags = ["System_NgPlus"]` 实现(由 NgPlusManager 在 NG+ 开始时设置该 WorldStateFlag)。 --- ## 5. 多结局实现流程 参见 `50_NarrativeDesignSystem.md §8`,此处补充技术实现: ### 5.1 EndingDirector ```csharp namespace BaseGames.Completion { public class EndingDirector : MonoBehaviour { [SerializeField] EndingGate _endingGate; [SerializeField] TimelineController _timelineController; [SerializeField] AssetReference _normalEndingTimeline; [SerializeField] AssetReference _trueEndingTimeline; [SerializeField] AssetReference _hiddenEndingTimeline; public async UniTask PlayEnding() { var endingType = _endingGate.EvaluateEnding(); // 记录到 SaveData _saveData.LastEndingAchieved = endingType.ToString(); _saveData.CompletionPercent = _tracker.CompletionPercent; // 根据结局类型加载对应 Timeline var timelineRef = endingType switch { EndingType.Hidden => _hiddenEndingTimeline, EndingType.True => _trueEndingTimeline, _ => _normalEndingTimeline }; var timeline = await Addressables.LoadAssetAsync(timelineRef); await _timelineController.PlayAndWaitAsync(timeline); // 触发结算画面 UIManager.Instance.ShowEndingResult(endingType, _tracker.CompletionPercent); } } } ``` ### 5.2 结局差异说明 | 场景 | 普通结局 | 真结局 | 隐藏结局 | |------|---------|--------|---------| | 最终 Boss 击败动画 | 相同 | 相同 | 相同 | | 结局过场动画时长 | ~3 分钟 | ~5 分钟 | ~7 分钟 | | 额外旁白 | 无 | 有(泽灵内心独白)| 有(含 KnightExile 对话)| | 片尾字幕曲目 | 标准 ED 曲 | 特别 ED 曲 | 隐藏 ED 曲(特殊编曲)| | 结算后可解锁 | NG+ | NG+ + 特殊皮肤 | NG+ + 收藏室终章 | --- ## 6. 隐藏 Boss / 隐藏区域门控 ### 6.1 隐藏 Boss:幻境守卫(VoidSentinel) | 属性 | 值 | |------|---| | 入口位置 | Ruins 区域的"幻境裂缝"(隐藏房间,需冲刺+双跳才能进入) | | 解锁条件 | 收集 ≥ 5 个 EchoFragment + 与 ElderSage 对话 3 次以上 | | 难度 | 显著高于同区域主线 Boss(T4 精英级) | | 奖励 | EchoFragment #特殊(计入真结局计数)+ 专属符文 | | 完成度 | 计入 Boss 类别(1 个额外 Boss,分值 50)| > 注:隐藏 Boss 不计入 Boss 击败类别的 5/5,而是额外加分(使总分上限达到 1050,最终百分比上限 105% 而非 100%)。显示为"1 个额外 Boss 已击败"。 ### 6.2 隐藏区域:幻境回廊 | 属性 | 值 | |------|---| | 开放条件 | NG+2 开始 | | 内容 | 3 个超高难度挑战房间 + 最终 Lore 3 件 | | 完成度贡献 | 25 分(仅 NG+2 存档计入)| --- ## 7. 收集品查看界面(Codex + 收藏室) ### 7.1 Codex(图鉴) 详见 `50_NarrativeDesignSystem.md §6.3`。图鉴从暂停菜单入口,按区域+类型分章节显示已收集 Lore。 ### 7.2 收藏室(Gallery) 解锁条件:完成度达到 75%。 收藏室是地图中一个真实的房间(Forest 区域隐藏路径),内部墙壁随玩家收集内容逐渐出现装饰: | 收集进度 | 收藏室变化 | |---------|----------| | Boss 全部击败 | 5 幅 Boss 肖像画挂上墙壁 | | 幻境碎片 5/10 | 碎片拼图的一半出现在中央台座 | | 幻境碎片 10/10 | 碎片拼图完整,台座发光,显示真结局入口提示 | | Lore 全收集 | 书架填满,可阅读完整世界史 | | NG+3 | 最后一个房间开启,包含开发者留言和彩蛋 | --- ## 8. 完成度与成就联动 | 完成度 | 成就名 | 平台成就 | |--------|--------|---------| | 任意结局通关 | 泽灵归来 | Steam / Switch 成就 | | 完成度 ≥ 50% | 探索者之心 | Steam 成就 | | 完成度 ≥ 80% | 不留遗憾 | Steam 稀有成就 | | 完成度 = 100% | 世界已属于你 | Steam 超稀有成就(徽章)| | 真结局 | 幻境之归 | Steam 成就 | | 隐藏结局 | 永恒见证者 | Steam 超稀有成就 | | NG+3 通关 | 三度轮回 | Steam 稀有成就 | 成就触发通过 `AchievementManager` → `IPlatformService.UnlockAchievement`,见 `32_AchievementSystem.md`。 --- ## 9. SaveData 集成 ```csharp public class CompletionSaveData { // 完成度分数快照(每次存档时更新) [JsonProperty("completionScore")] public int CompletionScore { get; set; } = 0; // 最后一次通关的结局类型 [JsonProperty("lastEndingAchieved")] public string LastEndingAchieved { get; set; } // "Normal" / "True" / "Hidden" // NG+ 层级 [JsonProperty("ngPlusCount")] public int NgPlusCount { get; set; } = 0; // 各类别已完成项 [JsonProperty("defeatedBosses")] public HashSet DefeatedBosses { get; set; } = new(); [JsonProperty("exploredRooms")] public HashSet ExploredRooms { get; set; } = new(); [JsonProperty("collectedAbilities")] public HashSet CollectedAbilities { get; set; } = new(); [JsonProperty("collectedCharms")] public HashSet CollectedCharms { get; set; } = new(); [JsonProperty("echoFragmentCount")] public int EchoFragmentCount { get; set; } = 0; [JsonProperty("collectedLoreCount")] public int CollectedLoreCount { get; set; } = 0; [JsonProperty("maxAffinityNPCCount")] public int MaxAffinityNPCCount { get; set; } = 0; [JsonProperty("challengeRoomSCount")] public int ChallengeRoomSCount { get; set; } = 0; } ``` --- ## 10. 事件频道 | 频道 SO | 类型 | 触发时机 | |---------|------|---------| | `OnCompletionScoreChanged` | `IntEventChannelSO` | 完成度分数变化(传入新分数)| | `OnEndingAchieved` | `StringEventChannelSO` | 结局过场播放(传入结局类型字符串)| | `OnNgPlusStarted` | `IntEventChannelSO` | NG+ 开始(传入层级)| | `OnCodexComplete` | `VoidEventChannelSO` | 图鉴全部收集完成 | | `OnHiddenAreaUnlocked` | `StringEventChannelSO` | 隐藏区域开放(传入 sceneAddress)| --- ## 11. 编辑器友好设计 - **CompletionConfigSO** Inspector:实时显示当前分值构成(饼图),方便调整权重 - **CompletionTracker** Inspector(Play Mode):显示各类别当前分/满分、总百分比进度条 - **EndingGate** Inspector:显示当前 SaveData 是否满足各结局条件(✅/❌ 清单) - **收藏室状态 Gizmo**:在 Scene View 中显示收藏室当前解锁状态颜色(绿=已解锁、灰=未解锁) --- *本文档版本 1.0 · 2026-04 · 关联 50_NarrativeDesignSystem / 32_AchievementSystem / 14_ProgressionSystem*