《潜抑》游戏开发复盘

绪论

记录游戏《潜抑》开发,我主要负责四个功能,心理稳态数值管理,世界状态与转化控制,对话系统和提示系统。除对话系统外单拎出来都非常简单,重点在于如何将它们串起来。

心理稳态数值管理

需求:

为全局系统提供“应激度/解离度”的统一数据源与变化规则,供对话、世界切换、画面反馈等模块读取与订阅。应激度主要受外部事件的增减,解离度根据应激度按一定规则增减。

如何设计:

用常驻管理器做单一写入入口,单例,DontDestroyOnLoad,变化后必须立即更新“解离度自动变化状态”,广播给其他系统,由明确事件驱动修改。

实际落地:

MentalStatsManager(常驻单例)配 MentalStatsConfig(ScriptableObject),应激度只经 ModifyStress 写入,解离 tick 与变化用 MentalStatsChangedEvent 广播;外部只读属性或 Snapshot,通过 OnStatsChanged 订阅。

世界状态与转化控制

需求:

有两个世界,现实世界和意识世界,根据交互与剧情节点触发状态切换,例如解离度,对话等等。

如何设计:

放置两个场景,作为两个世界。给摄像机加一个灰度滤镜,用于体现不同解离度画面的变化。设置单一入口WorldStateManager.TrySwitchWorld切换世界。

实际落地:

WorldStateManager 对外只走 TrySwitchWorld:读解离度与 WorldStateConfig 判定或按 InteractionType 强切,LoadScene(Single) 换现实/意识场景,加载完成后更新 CurrentWorld 并发 OnWorldSwitched。灰度等表现单独订阅心理数值事件,与切场景逻辑分开。

提示系统

需求:

实现一种“角色靠近物体时,有概率在角色头顶弹出短提示词”的轻量提示机制。

如何设计:

玩家进入触发范围 -> 提示源发起请求 -> 概率判定 -> 玩家提示显示器播提示 -> 自动隐藏。在玩家子物体上挂 TMP 与 PlayerPromptView,玩家根物体挂 PlayerPromptReceiver;提示源物体上为 Collider2D(Is Trigger)+ PromptSource,配置 promptTexts、概率、显示时长与优先级。

实际落地:

触发器进范围后 PromptSourcePlayerPromptReceiver,由 TryBuildRequest 拼出带文案/概率/时长/优先级的 PromptRequestReceiver 判定通过后交给 PlayerPromptView 淡入与定时隐藏;离开范围重置重叠状态,视图只管表现。

对话系统

需求:

从csv表格导入对话数据,和应激度关联,UI要求有打字机效果。

如何设计:

采用CSV表格,导入到unity后根据字段制出DialogueGraphData,然后显示 对话系统效果

1,CSV设计:

  • 使用两个表,一个记录对话,一个记录选项

2,对话表字段设计:

  • NodeId,节点的唯一标识符(主键)。
  • SpeakerName,说话人名字
  • PortraitKey,头像资源的“键”。
  • Text,正文内容。
  • NextId,下一节点。

3,选项表字段设计:

  • ChoiceId,选项的唯一标识符(主键)。
  • ChoiceOrder,选项自上而下的显示顺序。
  • NodeId,归属的节点 ID,把选项挂到对话表的对应节点下。
  • OptionText,选项上显示的文本。
  • NextId,跳转的下一节点。
  • StressModifier,选中后对应激度的变化量。
  • ThresholdLow,应激度过滤区间下界。
  • ThresholdHigh,应激度过滤区间上界;当前应激度落在区间内选项可见。

4,整体流程:CSV导入 -> 解析CSV -> DialogueGraphData -> DialogueRunner -> DialogueView

对话系统流程图

实际落地:

编辑器里用 DialogueCsvImportDefinition 把两张 CSV 生成 DialogueGraphData。运行时 DialogueRunner 读图、驱动 DialogueView(协程打字机,结束回调 NotifyLinePresentationCompleted),选项按当前应激度过滤,点选后走 MentalStatsManager.ModifyStress 再跳节点。

踩过的坑

整体来讲比较顺利,只有一个点,UI遮挡问题。

1,text文本也有射线遮挡,且和图像的位置不一样,需要关掉。 文本遮挡问题

2,另一个人设置了一个panel遮罩,而且是运行时生效的,当时设计时是用于场景切换做视觉效果的,但是他切换完场景后没有关掉这个panel,导致一直遮挡在最上层,挡住了按钮的射线。

如何将这些系统串联起来

这几个系统单拎出来都不复杂,真正麻烦的是它们之间的调用关系。我的处理方式是把心理数值当作全局中枢:对话系统在运行时按当前应激度筛选选项,玩家做出选择后再通过 MentalStatsManager.ModifyStress 修改应激度;数值变化后,画面反馈一类系统只负责订阅变化并更新表现。世界切换则单独走 WorldStateManager.TrySwitchWorld,由床、药物或剧情交互去读取当前解离度和交互类型,决定进入现实世界还是意识世界。提示系统不参与剧情状态判断,只负责玩家靠近物体时给轻量反馈,以及在进入对话时隐藏交互提示,避免和对话 UI 冲突。这样拆下来,每个系统职责都比较单一,但又能通过数值和事件串成一条完整流程。

游戏之外:关于原始思路的留存问题

在使用AI协作开发时,我习惯按照:原始思路->在对话框中输入给AI->AI改进版思路->留档->开始实现->开发日志/使用说明->留档。 问题在于,几乎所有东西都留档了,除了原始思路,而原始思路是唯一完全由我自己写的,思考得来的结果。这就导致不管是复盘还是一个项目过了很久后回头改一些东西,都只能去读AI的改进版思路,而这是相对而言比较陌生的。以后要考虑做完后自己写一个简短的整体思路。


《潜抑》游戏开发复盘
https://yaoyablog.xyz/2026/04/12/unity/《潜抑》游戏开发复盘/
作者
Yaoyawen
发布于
2026年4月12日
许可协议