386 lines
16 KiB
Markdown
386 lines
16 KiB
Markdown
# 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<TimelineAsset>(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<string> DefeatedBosses { get; set; } = new();
|
||
|
||
[JsonProperty("exploredRooms")]
|
||
public HashSet<string> ExploredRooms { get; set; } = new();
|
||
|
||
[JsonProperty("collectedAbilities")]
|
||
public HashSet<string> CollectedAbilities { get; set; } = new();
|
||
|
||
[JsonProperty("collectedCharms")]
|
||
public HashSet<string> 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*
|