# NarrationSystem 技术参考 本文档定义并说明项目内的“旁白系统(NarrationSystem)”,以及它如何与任务系统(TaskSystem)配合,通过按钮继续/翻页在关键节点触发事件(例如添加/删除任务)。 相关脚本: - `Assets/_Project/Scripts/Core/NarrationSystem/NarrationSystem.cs` - `Assets/_Project/Scripts/Core/NarrationSystem/NarrationSequence.cs` - `Assets/_Project/Scripts/Core/NarrationSystem/NarrationPage.cs` - `Assets/_Project/Scripts/UI/Narration/NarrationPanelView.cs` - (可选动作)`Assets/_Project/Scripts/Core/NarrationSystem/NarrationPlayByIdAction.cs` 任务系统动作脚本(用于旁白事件里绑定): - `Assets/_Project/Scripts/Core/TaskSystem/Actions/AddTaskAction.cs` - `Assets/_Project/Scripts/Core/TaskSystem/Actions/RemoveTaskAction.cs` - `Assets/_Project/Scripts/Core/TaskSystem/Actions/SetTaskStatusAction.cs` ## 1. 设计目标 - 旁白内容以“序列(Sequence)→ 多页(Page)”组织,可在 Inspector 中动态增删页数。 - 每一页都提供事件: - `onEnter`:进入该页时触发 - `onContinue`:玩家点击继续离开该页时触发 - 旁白系统提供统一触发入口,可被开场流程/剧情节点/触发器/互动等系统调用。 - 旁白显示时自动切换为 UI 操作模式并显示鼠标,允许玩家点击继续: - 复用 `PlayerControlLockService.Lock/Unlock`(项目内既定范式)。 ## 2. 数据结构 ### 2.1 NarrationDataSO(基于 ScriptableObject 的数据容器) 脚本:`NarrationDataSO.cs` - 在 Project 窗口右键 `Create -> Systems -> Narration -> Narration Data` 创建。 - 字段: - `id`:序列 ID(用于按 ID 播放) - `pages`:页列表(动态长度) ### 2.2 NarrationPageData(单页旁白数据) 脚本:位于 `NarrationDataSO.cs` 内 - 字段: - `text`:该页显示的文本 - `enterActionId`:进入该页时触发的 Action ID(字符串) - `continueActionId`:点击继续离开该页时触发的 Action ID(字符串) ### 2.3 NarrationEventListener(场景事件监听器) 脚本:`NarrationEventListener.cs` - 用于将 `NarrationDataSO` 中的字符串 Action ID 映射到场景内的 `UnityEvent`,从而触发具体的动作(如 `AddTaskAction`)。 ## 3. 核心系统 ### 3.1 NarrationSystem 脚本:`NarrationSystem.cs` Inspector 槽位: - `Panel View`:旁白 UI 视图(`NarrationPanelView`) - `Data / Narration Database`:将所有需要支持 `PlayById` 的 `NarrationDataSO` 拖入此列表。 - `Lock Player Controls While Showing`:显示时是否锁定玩家控制并显示鼠标(推荐开启) 主要接口: - `PlayById(string sequenceId)`:按 ID 播放序列 - `Play(NarrationDataSO sequence)`:直接播放序列引用 - `Stop()`:停止当前旁白 - `Continue()`:继续到下一页(由 UI 按钮驱动) 行为说明: - 播放开始:显示 UI,显示鼠标(Lock),进入第 0 页并触发全局事件(带 `enterActionId`) - 点击继续: - 先触发当前页全局事件(带 `continueActionId`) - 再进入下一页并触发新页全局事件(带 `enterActionId`) - 最后一页点击继续:结束播放,隐藏 UI,恢复控制(Unlock) ### 3.2 NarrationPanelView 脚本:`NarrationPanelView.cs` Inspector 槽位: - `Root`:UI 根节点(用于 Show/Hide) - `Narration Text`:TMP 文本控件 - `Continue Button`:Reach 的 `ButtonManager`(推荐) - `Fallback Continue Button`:如果你不用 Reach Button,可填 Unity `Button` - `Continue Label / Finish Label`:按钮文案(继续/确认) ## 4. 与 TaskSystem 配合(在旁白页事件里加/删任务) 因为 `TaskService.AddTask(...)` 这类方法需要参数,UnityEvent 直接绑不方便,推荐通过“动作脚本”承接: - `AddTaskAction`: - 槽位:`taskService`、`task`、`timeoutSeconds`、`addOrUpdate`、`resetStatusToTodo` - 事件里绑定:`AddTaskAction.Invoke()` - `RemoveTaskAction`: - 槽位:`taskService`、`task` - 事件里绑定:`RemoveTaskAction.Invoke()` - `SetTaskStatusAction`: - 槽位:`taskService`、`task`、`status` - 事件里绑定:`SetTaskStatusAction.Invoke()` 典型用法: - 进入第一页(onEnter):添加“寻找钥匙”任务 - 点击继续离开某页(onContinue):把“寻找钥匙”改为完成/移除 ## 5. 场景搭建步骤(按顺序) ### 5.1 创建旁白系统根节点 1) Hierarchy 新建空物体:`_NarrationSystem` 2) 在 `_NarrationSystem` 上 Add Component:`Core.NarrationSystem.NarrationSystem` 3) 在 `_NarrationSystem` 下新建空物体:`Sequences` 4) 将 `NarrationSystem.Sequences Root` 绑定到 `_NarrationSystem/Sequences` ### 5.2 绑定 UI 前提:你已经制作好了旁白 UI(对话框 Text + Button)。 1) 在旁白 UI 根对象上 Add Component:`UI.Narration.NarrationPanelView` 2) 依次绑定槽位: - `Root`:旁白 UI 根对象(用于开关) - `Narration Text`:你的 TMP 文本 - `Continue Button`:你的 Reach 按钮(ButtonManager) 3) 把该 `NarrationPanelView` 拖到 `_NarrationSystem/NarrationSystem.Panel View` 槽位 4) 初始建议让 UI 根对象处于隐藏(SetActive=false),由系统播放时显示 ### 5.3 制作一段开场旁白(示例) 1) 在 Project 窗口右键 `Create -> Systems -> Narration -> Narration Data` 创建一个 ScriptableObject 资产,命名为 `IntroNarrationData` 2) 选中它,在 Inspector 中设置: - `id`:`intro_awake` - `pages` 添加 3 页(示例文本,可替换为你指定内容): - 第 0 页:`你醒了。头还有些疼。` - 第 1 页:`先四处看看,找到能离开这里的方法。` - 第 2 页:`如果门打不开,也许需要一把钥匙。` 3) 将这个 `IntroNarrationData` 拖入 `_NarrationSystem` 上的 `Narration Database` 列表中。 ### 5.4 在旁白节点绑定任务事件(示例:添加“寻找钥匙”任务) 1) 准备一个任务数据 `TaskData`:例如 `task_find_key` 2) 在 `IntroNarrationData` 的第 2 页配置: - `continueActionId`:填入 `intro_awake_p2_continue` 3) 在场景里创建一个空物体(建议挂在 `_NarrationSystem` 下):`NarrationEventRouter` 4) 在 `NarrationEventRouter` 上 Add Component:`Core.NarrationSystem.NarrationEventListener` 5) 在同物体(或子物体)上 Add Component:`Core.TaskSystem.Actions.AddTaskAction` - `Task Service`:拖场景里的 `TaskSystem(TaskService)` - `Task`:拖 `task_find_key` - `Add Or Update`:建议勾上(防止重复触发) 6) 回到 `NarrationEventListener`: - 添加一个 Action Mapping - `actionId` 填入 `intro_awake_p2_continue` - `onTrigger` 事件里拖入挂着 `AddTaskAction` 的物体,并选择 `GameAction -> Invoke()` ### 5.5 将开场动画结束点接到旁白(AwakeningSequenceController) 工程已为唤醒流程增加了可绑定事件: - `AwakeningSequenceController.onAwakeningFinished` - `invokeFinishedWhenSkipped`(继续游戏是否也触发同一事件) 按以下步骤绑定: 1) 选中场景中负责开场唤醒动画的对象(挂了 `AwakeningSequenceController`) 2) 在组件 `Events` 分组里找到 `On Awakening Finished` 3) 新建一个空物体(建议挂在 `_NarrationSystem` 下):`IntroNarrationAction` 4) 给 `IntroNarrationAction` Add Component:`Core.NarrationSystem.NarrationPlayByIdAction` - `Narration System`:拖 `_NarrationSystem (NarrationSystem)` - `Sequence Id`:填 `intro_awake` 5) 把 `IntroNarrationAction.NarrationPlayByIdAction.Invoke()` 绑定到 `On Awakening Finished` 运行效果: - 唤醒动画结束 → 自动弹出旁白 UI → 鼠标可点 → 点击继续翻页 → 最后一页确认后关闭 UI 并恢复控制