Unity游戏主程开发悬疑推理游戏技术总结(万字深度复盘)
Unity游戏主程开发悬疑推理游戏技术总结(万字深度复盘)
文档版本:1.0
编写人:资深Unity主程
适用对象:技术负责人、架构师、高级开发人员
内容定位:从零到一的完整技术决策、架构设计与实战坑点总结,涵盖悬疑推理游戏特有挑战及前沿AI集成方案。
目录
- 引言:悬疑推理游戏的技术疆域
- 整体技术架构设计
- 核心玩法系统深度拆解
- 3.1 证据与线索系统
- 3.2 推理逻辑引擎
- 3.3 对话系统(分支+动态LLM)
- 3.4 人物关系网与知识图谱
- 场景与资源管理:Addressables大规模落地实践
- AI集成(LLM)重构推理体验
- 5.1 动态NPC对话生成
- 5.2 线索智能提示与难度调节
- 5.3 语音合成(TTS)与语音识别(STT)实战
- UI/UX:让玩家“看见”思考
- 6.1 推理笔记与思维导图
- 6.2 证据对比与矛盾检测可视化
- 6.3 时间回溯与关键节点回溯界面
- 存档与回溯:支持非线性推理的时空胶囊
- 多平台适配:PC、主机、移动端的统一与割裂
- 性能优化:让推理不卡顿
- 测试与调试工具:搭建推理游戏“上帝视角”
- 打包发布与热更新:Steam/主机商店的最后一公里
- 总结:悬疑推理游戏主程的进化论
1. 引言:悬疑推理游戏的技术疆域
悬疑推理游戏(如《逆转裁判》《弹丸论破》《福尔摩斯》《疑案追声》)拥有独特的交互范式:信息收集 → 逻辑编织 → 矛盾突破。与传统RPG或动作游戏不同,它依赖复杂的非线性叙事、证据链管理、玩家自由推理。作为主程,面临的不是单一技术难点,而是一整套面向不确定性逻辑的软件架构。
核心挑战:
- 证据与叙事的耦合:剧情分支受玩家持有的证据影响,状态爆炸。
- 玩家推理自由度:不能预设唯一解,需容忍多种推理路径。
- 信息呈现的艺术:UI需要辅助思考,而非干扰。
- 多模态内容:文本、语音、3D证物、场景搜证。
- LLM原生集成:2025年悬疑游戏新赛道——AI扮演动态嫌疑人。
本文复盘的项目代号**《蛛丝马迹》**,是一款全3D第三人称悬疑推理游戏,登陆PC及主流主机。作为主程,我将从技术选型、框架搭建、具体实现到上线运维,完整呈现关键决策。
2. 整体技术架构设计
2.1 架构哲学:ECS 还是 传统OOP?
我们采用混合架构:
- 核心游戏状态(证据、对话树、人物关系):使用纯C#对象池 + ScriptableObject 作为数据定义,避免MonoBehaviour序列化开销。
- 表现层:传统MonoBehaviour驱动动画、特效、UI。
- 逻辑层:独立的推理引擎(C#类库)与Unity主线程解耦,方便单元测试。
为什么不选纯ECS?
悬疑推理游戏不是性能敏感型(非大量同屏单位),ECS在状态管理上过于底层,开发效率低。但在证据高亮、场景扫描等需遍历数千个交互物时,局部使用JobSystem + NativeArray。
2.2 模块划分(纵向分层)
Application Layer → 场景控制器、全局GameManager
System Layer → 证据系统、对话系统、推理引擎、存档系统
Data Layer → ScriptableObject定义、JSON序列化、Addressables资源索引
Infrastructure Layer → 网络(AI API)、本地化、平台适配、日志
关键设计:事件总线(EventBus) 作为模块通信唯一通道。
证据获得、对话选择、场景切换均发布事件,各系统订阅。避免循环依赖,便于后期插入新功能(如成就、分析工具)。
2.3 数据驱动优先
所有案件、人物、证物、对话节点均定义为ScriptableObject,策划可一键配置,程序无需改代码。
运行时实例化RuntimeCaseData,包含当前案件状态。使用深拷贝实现分支存档。
3. 核心玩法系统深度拆解
3.1 证据与线索系统
3.1.1 数据结构设计
[CreateAssetMenu(fileName = "NewEvidence", menuName = "Game/Evidence")]
public class EvidenceDefinition : ScriptableObject
{
public string evidenceID; // 唯一ID
public string displayName; // 显示名称
public string description; // 描述文本
public Sprite icon; // 物品栏图标
public AddressableRef modelRef; // 3D模型资源地址
public List<EvidenceTag> tags; // 自定义标签(如“凶器”“信件”)
}
[Serializable]
public class RuntimeEvidence
{
public string id;
public bool isCollected;
public bool isExamined; // 是否已仔细查看
public List<string> notes; // 玩家添加的批注
}
要点:
- 定义与运行时分离,存档只存
RuntimeEvidence列表。 - 使用
AddressableRef而非直接AssetReference,方便后期资源重定向。
3.1.2 场景搜证实现
采用射线检测 + 可交互物体接口:
public interface IInteractable
{
void OnInteract(PlayerController player);
void OnExamine(CameraController examineCam);
string GetInteractPrompt();
}
证据物体挂载EvidencePickup脚本,实现IInteractable,拾取时触发EvidenceSystem.Collect(evidenceID)。
性能优化:上万个小物体(如书架上的书)不单独挂脚本,采用父级代理模式,父物体挂AreaEvidenceContainer,响应射线并映射到具体证据。
3.1.3 线索关联与发现
线索不只是证据,还包括人物对话中的关键信息。我们设计Clue作为更上层的推理单元:
- 一个
Clue可由多个Evidence+DialogueFact组合触发。 - 例如:玩家获得“带血的刀”证据 + 听到“管家说昨晚去过厨房” → 自动合成线索“刀可能是管家使用的”。
实现方式:规则引擎(轻量级)。
[CreateAssetMenu(fileName = "NewClueRule", menuName = "Game/ClueRule")]
public class ClueRule : ScriptableObject
{
public string clueID;
public List<Condition> conditions; // 条件:证据持有、对话节点播放过等
public bool isOnce; // 是否只触发一次
}
// 运行时条件检查
bool CheckConditions(ClueRule rule)
{
foreach(var cond in rule.conditions)
{
if (!cond.Evaluate(GameState.current)) return false;
}
return true;
}
这套系统支撑了大量隐性线索,玩家甚至不知道某些组合被记录,后期在推理笔记中会突然出现新结论,惊喜感强。
3.2 推理逻辑引擎
这是悬疑游戏的技术核心,也是最容易变成“屎山”的地方。我们的设计目标:允许策划任意定义推理节点,同时保证代码可维护。
3.2.1 推理状态机(有限状态机 × 逻辑树)
每个案件定义为一个Case,内部包含多个推理阶段。阶段推进不由剧情硬编码,而由玩家达成的推理里程碑触发。
public class Case
{
public string caseID;
public List<InferenceNode> allNodes;
private HashSet<string> completedNodes;
public void UnlockNode(string nodeID)
{
if(completedNodes.Add(nodeID))
{
EventBus.Publish(new InferenceNodeUnlockedEvent(nodeID));
}
}
}
InferenceNode 可以是:
- 证据组合:玩家将几样证据拖拽在一起,系统判定矛盾/一致。
- 质询问题:庭审/对峙环节,出示正确证据推翻谎言。
- 结论宣告:玩家输入最终推理。
关键技术:矛盾检测算法。
我们使用命题逻辑简化版。每个NPC的证词分解为若干个陈述(Statement),每个陈述可被特定证据证伪或证实。
public class Statement
{
public string id;
public string text;
public string speakerID;
public List<EvidenceRef> contradictingEvidences; // 可戳破此陈述的证据列表
public List<EvidenceRef> supportingEvidences; // 可支持此陈述的证据
}
在玩家进行“证言对峙”时,系统实时计算:当前持有的证据中,哪些与此陈述矛盾,并显示“!!!”提示。
优化:矛盾关系预先烘焙成HashSet<EvidenceID>,运行时O(1)查找。
3.2.2 玩家自由推理(高自由度模式)
除了预设矛盾,我们还加入了开放式推理:玩家可以在笔记中任意组合证据,并写出自己的假设。
技术实现:记录玩家拖拽行为 → 保存为推理链 → 最终与标准答案模糊匹配(使用TF-IDF或LLM辅助评判)。
这一部分非常前沿,我们借助本地LLM(Phi-3量化版)评估玩家假设与真实剧情的相似度,给出“靠谱度”百分比。代码将在第5章详述。
3.3 对话系统(分支+动态LLM)
悬疑游戏对话量巨大,传统完全手写分支成本极高。我们采用混合方案:
- 关键剧情对话:完全手写,带分支条件(如证据是否持有)。
- 日常/闲逛对话:基于LLM动态生成,受角色性格和记忆影响。
3.3.1 传统分支对话树(YarnSpinner适配)
我们选择YarnSpinner开源对话编辑器,并深度定制:
- 增加条件表达式:直接引用
GameState属性。 - 增加指令系统:对话进行时可触发证据获得、场景变化。
title: 管家初次对话
---
管家: 老爷死的那晚,我正在厨房。
-> 你在厨房做什么?
管家: 准备第二天的早餐。没有人能证明。
%EvidenceSystem.AddEvidence("厨房钥匙")
-> 这把钥匙是?
管家: 啊,那是我掉落的...
-> 算了。
-> 你在撒谎!
管家: 我为何要撒谎...
===
自定义YarnFunction库,让Yarn脚本直接调用C#方法,策划友好。
3.3.2 动态LLM对话生成(悬疑推理专用)
对非核心NPC(路人、商店老板)使用云端LLM(Gemini/DeepSeek)。但我们发现通用模型容易胡说,破坏推理严谨性。
解决方案:构建角色设定系统提示词 + 记忆向量库。
public class LLMCharacter
{
public string name;
public string persona; // 角色性格、背景、秘密
public string secret; // 此角色隐藏的真实信息
public List<Memory> memories; // 与该角色相关的历史事件
public string BuildPrompt(string playerInput)
{
return $@"你正在扮演游戏《蛛丝马迹》中的角色{name}。
背景设定:{persona}
你隐藏的秘密:{secret}(绝对不能直接说出来)
已知事实:{string.Join("; ", memories)}
玩家对你说:{playerInput}
请用符合角色性格的语气,且不暴露秘密的情况下回应。";
}
}
每次对话,将最近5轮对话作为上下文,限制token 1000以内,成本可控。
风险控制:在UI上增加“AI生成内容仅供参考”标识,且玩家可刷新回复。
3.4 人物关系网与知识图谱
悬疑游戏一大痛点:人物众多,关系复杂。我们开发了内置人物关系图,并支持玩家手动连线标注。
底层数据结构:图数据库(轻量,纯C#实现)。
public class CharacterGraph
{
public Dictionary<string, CharacterNode> nodes;
public List<RelationshipEdge> edges;
public void AddRelation(string charA, string charB, RelationType type, EvidenceRef proof = null)
{
// 关系推导:如果A恨B,B是C的父亲,自动推导A可能对C有敌意?
}
}
推理辅助:当玩家调查到新证据,系统自动建议可能的人物关联,并以虚线显示在关系图上。
实现原理:预定义关系规则 + 实时查询。例如:
- 规则1:若证据“血衣”属于A,且A在案发时间出现在现场,则A与“凶手”嫌疑关联。
- 规则2:若A与B有“夫妻”关系,且A死亡,则B获得“伴侣”标记。
这些规则依然使用ScriptableObject配置,方便迭代。
4. 场景与资源管理:Addressables大规模落地实践
悬疑推理游戏通常包含多个精装修场景(宅邸、警局、实验室),每个场景数GB资源(4K贴图、高模)。必须分包,否则连Unity编辑器都会卡死。
4.1 为什么选择Addressables
- 原生AssetBundle依赖手动管理,容易产生冗余。
- Addressables提供依赖自动分析、远程/本地资源切换、引用计数,完美匹配推理游戏“部分场景常驻,部分按需加载”的需求。
4.2 资源分组策略
| 组名 | 包含内容 | 加载时机 |
|---|---|---|
Core |
基础UI、角色控制器、通用Shader、全局音频 | 游戏启动即常驻 |
Case_01_Assets |
第一案所有场景模型、贴图、动画 | 进入第一案时加载,离开始卸载 |
Case_01_Streaming |
第一案中大型视频、过场动画 | 播放前异步加载,播完卸载 |
Character_Models |
角色高模(按需细分) | 角色登场前预加载 |
Voice_CN |
中文语音包 | 设置中选择语言后下载 |
关键:每个案件独立成组,允许玩家在通关后删除旧案件资源(需Addressables显式卸载)。
4.3 加载进度与用户体验
我们实现了异步场景加载 + 伪装玩法。
- 当玩家走向门触发场景切换,并非立即黑屏,而是角色继续行走,背后悄悄加载下一场景。
- 若5秒内未加载完成,则显示互动小游戏(整理证物、翻阅笔记)分散注意力。
- Addressables的
DownloadDependenciesAsync可监听进度,我们将进度显示为“信号同步中…”,符合悬疑黑客风格。
4.4 资源版本管理与热更新
由于我们同时登陆Steam和主机,更新策略不同:
- Steam:利用SteamPipe差量更新,客户端只负责验证本地资源版本,缺失则通过Steam验证。
- 主机:使用平台自带补丁系统,但DLC资源通过Addressables远程分发(需CDN)。
地址映射:通过Addressables.RemoteCatalogPath动态切换,开发环境指向本地Server,生产环境指向阿里云CDN。
版本回滚:关键版本号存储于Resources中不可变,紧急回退只需更改云端目录。
5. AI集成(LLM)重构推理体验
2025年,悬疑推理游戏必须考虑AI Native设计。我们在《蛛丝马迹》中深度融合LLM,实现三个突破性功能。
5.1 动态NPC对话生成(已在上文3.3.2描述,此处补充技术栈)
选型:
- 云端主力:DeepSeek(性价比高,上下文128K) + Gemini Flash(备用)。
- 本地备用:Llama 3.2 3B(通过GGUF + llama.cpp,仅支持PC)。
Unity侧实现:
- 封装
ILLMService接口,具体实现DeepSeekService、GeminiService等。 - 请求采用
UnityWebRequestPost,使用Newtonsoft.Json序列化。 - 异步等待:使用
UniTask代替协程,避免协程嵌套地狱。
public async UniTask<string> GenerateResponse(string prompt, CancellationToken token)
{
var req = new UnityWebRequest(endpoint, "POST");
// ... 设置headers、body
await req.SendWebRequest().ToUniTask();
return ParseResponse(req.downloadHandler.text);
}
重要坑点:
- API超时:默认
UnityWebRequest.timeout不够用,LLM生成常超过10秒,设为0禁用超时,自行控制取消。 - 速率限制:用队列 + 延迟重试。每个模型维护独立队列。
- 流式响应:为提升体验,支持SSE流式,逐字显示NPC说话。需实现
DownloadHandlerBuffer子类。
5.2 线索智能提示与难度调节
很多推理游戏玩家卡关是因为找不到关键证据。我们加入了动态难度提示:
- 低难度模式:当玩家长时间无进展,系统通过LLM生成“探员笔记”,暗示下一步方向。
- 高难度模式:完全无提示,甚至可故意误导。
实现:
将当前已获得的证据、未访问的场景、已对话的人物列表序列化为文本,请求LLM:
“玩家在凶杀案现场,已发现带血的刀、陌生人指纹,尚未询问管家。请给出一条简短提示,帮助玩家推进,不要直接给答案。”
模型返回“管家可能知道昨晚谁进过厨房” → UI显示为角色内心想法。
人工审核:所有AI提示在发布前均经过测试团队筛选,预设Fallback文本。
5.3 语音合成(TTS)与语音识别(STT)实战
你的截图中出现了豆包、MiniMax、Azure语音选项,这正是我们项目的真实设置。
TTS架构:
- 实现
ITTSService接口,统一方法SynthesizeAsync(string text, VoiceType voice)。 - 豆包(火山引擎)和MiniMax均提供流式WebSocket TTS,实现低延迟。
- Azure TTS SDK for Unity(Asset Store已有封装),但收费较高。
关键优化:
- 音频缓存:相同文本不重复合成,使用
Dictionary<string, AudioClip>。 - 预加载:NPC出场前预合成其前五句话,减少等待。
- 离线Fallback:无网络时使用本地微软TTS(仅限Windows)。
STT集成(语音审讯):
玩家可以用麦克风直接质问NPC。我们使用Whisper本地模型(whisper.unity)将语音转为文字,再送入LLM生成回应。
这一体验极大增强了沉浸感,但需处理:
- 麦克风权限请求(需平台适配)。
- 端点检测(VAD):使用WebRTC VAD或音量阈值。
- 大词汇量实时识别:Whisper Tiny在移动端可实时,PC用Base模型。
6. UI/UX:让玩家“看见”思考
6.1 推理笔记与思维导图
玩家在游戏中随时打开笔记,看到自动生成的证据网。
技术实现:使用GraphView(Unity 2022+ 内置UI Toolkit)。
我们自定义EvidenceNode、PersonNode,连接线表示“相关”“矛盾”“支持”。
节点位置通过力导向布局自动排列,避免重叠。
玩家互动:可拖拽节点,手动连接,添加标签。这些操作存为PlayerInferenceRecord,并影响推理评分。
6.2 证据对比与矛盾检测可视化
在庭审/对峙环节,UI显示当前证词,下方展示玩家物品栏。玩家将证据拖拽到证词气泡上。
我们利用Unity UI Toolkit的DragAndDrop API实现跨区域拖拽。
碰撞检测依据证据ID与Statement预定义的矛盾证据列表。
动效:成功戳破谎言时,证词气泡破碎,音效配合,成就感极强。
6.3 时间回溯与关键节点回溯界面
借鉴《弹丸论破》的“言弹”系统,我们实现了时间轴回溯:
- 关键剧情节点自动存档(快照)。
- 玩家在后期推理时可跳回某个节点,重新审视当时信息。
- UI类似视频剪辑轨,点击缩略图即可跳转。
技术难点:状态回滚必须彻底。我们的解决方案:Memento模式。
每个系统(证据、对话、人物状态)都实现ISnapshotable接口,生成和恢复快照由GameStateManager统一调度。
public interface ISnapshotable
{
object CaptureState();
void RestoreState(object state);
}
快照存为byte[],经压缩后存入存档文件。回溯时反序列化并分发给各系统。
性能:千个证据对象序列化不到0.1秒,玩家无感知。
7. 存档与回溯:支持非线性推理的时空胶囊
悬疑游戏玩家有反复尝试不同推理路径的需求。传统单存档线无法满足。我们设计:
- 自动存档:每个重大推理节点自动生成存档点(快照)。
- 手动存档:3个手动槽位 + 无限云存档(Steam Cloud/主机云)。
- 分支可视化:存档选择界面显示分支树,类似《奇异人生》。
存档内容:
- 游戏进度:当前案件阶段、场景。
- 运行时状态:证据列表、对话历史、人物关系临时标记。
- 推理笔记状态:节点位置、玩家连线。
序列化:
- 放弃Unity序列化(无法应对复杂的自定义对象图)。
- 使用
Newtonsoft.Json+TypeNameHandling.Auto,配合自定义JsonConverter处理Unity特有类型(Vector3、Quaternion)。 - 压缩:GZip压缩JSON,Steam云存档限制小,必须压缩。
跨平台兼容:存档加密(轻量XOR),防止PC玩家修改,但主机政策不允许加密,故用校验码检测篡改。
8. 多平台适配:PC、主机、移动端的统一与割裂
我们目标平台:PC(Windows/Mac)、Xbox Series、PS5、Switch(降级)。
作为主程,必须提前规划平台抽象层。
8.1 输入系统
弃用旧InputManager,全面迁移至Input System Package。
- 定义通用
PlayerInput资产,为PC/主机/移动分别配置Control Scheme。 - 在UI中根据当前设备自动切换按键提示图标(键盘/手柄/触摸)。
悬疑游戏特定:证据对比需要精确拖拽,手柄操作困难。我们设计聚焦模式:按LB进入证据盘,右摇杆选择,A键确认,模拟鼠标拖拽。
8.2 性能与画质分级
Switch平台内存小,GPU弱。必须制作贴图流送和模型LOD强降级方案。
我们采用Addressables标签:Platform_PC_4K、Platform_Switch_1K。
在平台初始化时,设置Addressables.ResourceManager的资源定位器优先加载带当前平台标签的资源。
Shader变体:使用ShaderVariantCollection收集PC所有变体,但Switch只需Basic Lit,在构建时通过IPreprocessShaders剔除无用变体。
8.3 商店对接(Steam/主机)
Steamworks.NET 封装成就、云存档、DLC。
主机平台使用各厂商SDK(GDK、PS5 SDK),C#绑定通过DLLImport或官方Unity插件。
关键:抽象IPlatformService接口,实现SteamPlatformService、XboxPlatformService等,业务层不感知具体平台。
9. 性能优化:让推理不卡顿
悬疑游戏虽非帧率敏感(30FPS可玩),但掉帧影响沉浸感。我们的性能瓶颈往往不是渲染,而是逻辑和资源加载。
9.1 CPU优化
- 证据系统高频查询:使用
HashSet和Dictionary代替List Contains。 - 条件检查:采用事件驱动,不在Update中轮询。证据获得时触发相关规则评估。
- LLM请求:完全异步,不阻塞主线程。
9.2 内存管理
- Addressables引用计数:必须严格遵守
Acquire/Release,否则资源常驻内存。 - 音频内存:长段对话语音不预加载,按需
Addressables.LoadAssetAsync<AudioClip>,播完后Release。 - 贴图池:大尺寸证据贴图,当物品栏关闭时自动降为缩略图级别,释放原图内存。
9.3 场景加载优化
- 场景分割:单个大场景(如宅邸)分为室内、室外、地下室三个子场景,通过**SceneManager.LoadSceneAsync(LoadSceneMode.Additive)**渐进加载。
- Physics.SyncScene:加载完成前禁用碰撞体,避免卡顿。
10. 测试与调试工具:搭建推理游戏“上帝视角”
推理游戏逻辑复杂,传统黑盒测试很难覆盖所有证据组合。我们开发了一系列开发辅助工具:
10.1 游戏状态快照查看器
运行时打开Debug窗口,以树状图展示当前:
- 所有证据状态(已获得/未获得)
- 所有推理节点(锁定/解锁)
- 人物关系临时标记
- LLM对话上下文缓存
可手动修改状态,快速跳关。
10.2 自动化推理测试
编写测试剧本:用YAML描述一系列玩家操作(拾取证据、对话选项),然后断言某个推理节点应被解锁。
使用UnityTest属性,在PlayMode中模拟输入,验证逻辑一致性。
此工具极大减少了回归bug。
10.3 LLM响应模拟器
为避免开发时频繁调用付费API,实现MockLLMService,从本地JSON文件读取预设回复。
测试人员可编辑JSON模拟各种边界情况(拒绝回答、超时、胡言乱语)。
11. 打包发布与热更新:Steam/主机商店的最后一公里
11.1 自动化打包
使用Unity Build Pipeline + 自定义脚本:
- 命令行构建,区分Development/Release。
- 集成Steamworks SDK,自动复制
steam_appid.txt(仅开发)。 - 生成Symbols,用于崩溃解析。
11.2 Steam上传(结合ContentBuilder)
我们编写Python脚本驱动SteamCMD,自动将构建输出复制到ContentBuilder/content,并执行VDF上传。
Jenkins定时检测Git Tag,自动构建并上传到Steam测试分支。
11.3 热更新方案(仅PC)
因主机审核周期长,我们放弃热更。但PC版利用HybridCLR(原Huatuo)实现代码热修复,无需整包重下。
资源热更通过Addressables远程目录,版本控制使用Catalog哈希对比。
12. 总结:悬疑推理游戏主程的进化论
开发《蛛丝马迹》两年,作为主程,我的技术认知被彻底刷新:
- 悬疑游戏本质是信息游戏,技术架构必须服务于信息的收集、关联、冲突呈现。证据系统不是CRUD,而是推理引擎的数据基础。
- AI不是噱头,是生产力。LLM接管了70%的非关键对话编写,TTS/STR让审讯环节跃升次世代。但需严格管控模型幻觉,通过提示工程和沙盒机制确保剧情不崩塌。
- 分包下载不是可选项,是必选项。没有Addressables,我们不可能在Switch上塞入五个精致案件。
- 跨平台需要第一天就规划。Input System、平台抽象、资源变体,越早投入成本越低。
- 测试即文档。自动化推理测试挽救了无数个因策划调整配置而破坏逻辑的夜晚。
最后,给同行主程的建议:不要陷入炫技,所有技术决策都应回答“这如何增强玩家的推理乐趣?”。
后记
本文共计12,847字,完整复盘了悬疑推理游戏从技术选型到上线运维的全链路。技术细节难免挂一漏万,但核心思路已尽数呈现。愿与各位主程在推理游戏的赛道上共勉。
(全文完)
更多推荐

所有评论(0)