Files
Echo/Assets/_Project/Docs/TaskSystem_Triggers_and_Flows.md
2026-07-08 20:42:51 +08:00

7.5 KiB
Raw Permalink Blame History

TaskSystem - 触发器与流程节点方案

本文档描述“任务的添加/删除/状态切换”在项目中的推荐落地方式:通过独立脚本组件(触发器/动作节点)把 TaskSystem 接入任意玩法事件、流程节点或 Timeline/动画/对话节点,而不是把逻辑写死在某个互动脚本里。

适用范围:

  • 任务不一定来自交互(门、拉杆等),也可能来自剧情节点、进入区域、时间到、对话选项、成就/教程步骤等。
  • 希望策划/关卡在 Inspector 中配置“触发条件 → 任务动作”,尽量减少硬编码与跨模块耦合。

相关基础系统:

  • Assets/_Project/Scripts/Core/TaskSystem/TaskService.cs
  • Assets/_Project/Scripts/Core/TaskSystem/TaskData.cs
  • Assets/_Project/Docs/TaskSystem_Technical_Reference.md

1. 总体思路

将任务系统的“动作”拆成可复用的独立组件:

  • 动作基类(GameAction):定义统一的 public override void Invoke() 接口。
  • 动作类(Task Actions):继承自 GameAction,只做一件事,调用 TaskService 的某个 API。
    • AddTaskAction
    • RemoveTaskAction
    • SetTaskStatusAction
  • 触发类(Task Triggers):监听某个来源事件,满足条件时调用动作(通过 UnityEvent 绑定 Action.Invoke())。
    • TriggerVolume (进入区域触发)
    • InteractFailedTrigger (交互失败触发)

这样可以保证:

  • 任务系统逻辑(添加/移除/状态规则)仍然集中在 TaskService,只通过 API 进入
  • 具体玩法/流程只负责“何时触发”,不负责“如何维护任务”
  • 同一套任务动作能被任何系统复用(UnityEvent、Timeline、Trigger、脚本回调都能接)

2. 推荐的“任务动作”组件清单(未来要做的脚本)

以下是建议新增的脚本(都应放在 _Project/Scripts/Core/TaskSystem/Actions_Project/Scripts/UI/Tasks/Actions,按你们组织习惯即可),每个脚本尽量只有一个公开入口方法,便于被 UnityEvent 调用。

2.1 AddTaskAction

用途:向任务系统添加一个任务(可选超时秒数)。

Inspector 槽位:

  • TaskService taskService(槽位引用;不写死单例)
  • TaskData task(要添加的任务定义)
  • float timeoutSeconds = -1<0 表示不超时)
  • bool addOrUpdate = true(推荐默认 true:避免重复添加失败)
  • bool resetStatusToTodo = false(可选:用于“重复触发时重置为待做”)

公开方法(示例概念):

  • public void Invoke()

内部应调用:

  • taskService.AddTask(task, timeoutSeconds)
  • taskService.AddOrUpdateTask(task, timeoutSeconds, resetStatusToTodo)

2.2 RemoveTaskAction

用途:按任务 ID 移除任务(建议用 TaskData 引用获取 id,避免手写字符串)。

Inspector 槽位:

  • TaskService taskService
  • TaskData task

公开方法:

  • public void Invoke()

内部应调用:

  • taskService.RemoveTask(task.id)

2.3 SetTaskStatusAction

用途:把某个任务置为 Completed / Expired / Todo。

Inspector 槽位:

  • TaskService taskService
  • TaskData task
  • TaskStatus status

公开方法:

  • public void Invoke()

内部应调用:

  • taskService.SetTaskStatus(task.id, status)

3. 推荐的“触发器”组件清单(未来要做的脚本)

触发器组件的核心原则:

  • 只监听事件 + 过滤条件 + 调用一个或多个动作
  • 尽量支持 UnityEvent:让关卡/流程可以在 Inspector 里做绑定
  • 必须提供“防重复触发”的选项(例如只触发一次、冷却时间、按任务是否已存在等)

3.1 OnStartTrigger / OnEnableTrigger

用途:场景开始/对象启用时自动添加任务或设置状态(替代临时的 TaskBootstrap)。

建议用法:

  • 教程任务:开场添加“打开背包”“查看平板”等
  • 剧情开场:进入房间后添加“寻找钥匙”

防重复建议:

  • onlyOnce(触发一次后禁用自身)
  • requireTaskNotPresent(任务已存在则不触发)

3.2 InteractFailedTrigger(交互失败触发)

用途:当玩家“按下交互键且条件不满足”时触发任务添加。

关键注意点:

  • 不要挂在“瞄准提示”的高频检测上,否则会重复触发
  • 必须挂在“按键交互尝试”的失败分支上(目前失败分支在 PlayerController 里)

推荐做法:

  • PlayerController 暴露一个“交互失败事件”(UnityEvent 或 C# event
    • 参数至少应包含:目标 collider / 交互者 / failReason
  • InteractFailedTrigger 订阅该事件,并做过滤:
    • 只关心指定门/指定根物体(hit 是否在该物体层级里)
    • 可选:只对某类条件失败生效(例如 RequireUnlockedInteractCondition

3.3 UnlockStateChangedTrigger(解锁状态变化触发)

用途:当 InteractUnlockState 变为 unlocked 时:

  • 自动完成某个任务(例如“寻找钥匙”完成)
  • 或移除过期任务

当前工程里 InteractUnlockState 没有事件回调,只有 SetUnlocked/Unlock/Lock 改字段并 Apply 视觉。

推荐改造方向(未来做):

  • InteractUnlockState 增加 event Action<bool> ChangedUnityEvent<bool> onChanged
  • 或者新增一个 Watcher 组件轮询 IsUnlocked(成本更低但不够优雅)

3.4 TriggerVolume / TimelineSignal / DialogueNode

用途:将任务动作接到“流程节点”上:

  • 进入触发区:添加任务
  • Timeline Signal:切任务状态
  • 对话节点:选择某个选项后添加/移除任务

建议统一入口:

  • 这些系统最终都调用 AddTaskAction/SetTaskStatusAction/RemoveTaskActionInvoke()

4. 配置规范(强烈建议遵守)

4.1 不写死引用

所有动作脚本都应该用 Inspector 槽位引用:

  • TaskService:拖场景里的 TaskService(或者由上层管理注入)
  • TaskData:拖 ScriptableObject 资产

不要在脚本里用:

  • 查找路径 transform.Find("...")
  • GameObject.Find(...)
  • 用字符串写 taskId(除非迫不得已)

4.2 去重策略(默认建议 AddOrUpdate

任务添加最常见的坑是“重复触发导致重复添加/报错/刷 UI”。

推荐默认策略:

  • 触发器 → 调用 AddOrUpdateTask(taskData, ...)
  • 如确实需要“只添加一次”:在触发器侧增加 onlyOncerequireTaskNotPresent

4.3 任务生命周期归属

建议统一归属:

  • 任务的超时判定:由 TaskService(已实现)
  • 任务的自动移除:由 TaskService 的 autoRemove 配置(已实现,可关)
  • 任务的产生与完成:由“触发器 + 动作”拼装(关卡/流程可配置)

5. 示例:门未解锁 → 交互失败 → 添加“寻找钥匙”任务

目标:

  • 门上挂了 RequireUnlockedInteractCondition,未解锁时交互失败
  • 失败后添加任务 task_find_key

推荐配置:

  • Scene:
    • TaskSystemTaskService
    • DoorRoot
      • InteractUnlockStateunlocked=false
      • RequireUnlockedInteractConditionfailReason=“尚未解锁”)
      • InteractFailedTrigger
        • targetFailReason=“尚未解锁”(或留空)
        • onInteractFailedEvent -> 拖入下方的 AddTaskAction,选择 GameAction.Invoke()
      • AddTaskAction
        • taskService -> TaskSystem(TaskService)
        • task -> task_find_key(TaskData)

6. 临时测试与正式接入的区别

  • TaskBootstrap:仅用于快速在场景启动时塞入测试任务(方便 UI 验证)
  • 正式玩法:应该由“触发器 + 动作”驱动任务变化,避免 Start 时就塞进任务导致误判