6.7 KiB
6.7 KiB
TaskSystem 技术参考
本文档定义并说明项目内的“任务系统(TaskSystem)”以及它如何驱动任务面板 UI(TaskPanel)。
相关脚本:
Assets/_Project/Scripts/Core/TaskSystem/TaskService.csAssets/_Project/Scripts/Core/TaskSystem/TaskData.csAssets/_Project/Scripts/UI/Tasks/TaskPanelController.csAssets/_Project/Scripts/UI/Tasks/TaskListItemView.cs
相关 UI 搭建与布局参考:
Assets/_Project/Docs/TaskSystem_UI_Setup.md
1. 设计目标
- 任务数据与 UI 解耦:代码不硬编码层级路径,通过 Inspector 槽位绑定 UI。
- 列表项可做成 Prefab:运行时动态实例化到 ScrollView Content。
- 任务状态可被外部系统调用修改:支持添加、移除、状态切换(待做/完成/超时)。
- 超时逻辑由任务系统统一处理:当任务设置了超时时间,自动切换为“超时”状态。
2. 数据模型
2.1 TaskData(任务静态数据)
文件:TaskData.cs
- 类型:
ScriptableObject - 字段:
id:任务 ID(全局唯一)shortName:短任务名(列表显示)description:任务描述(详情显示)
用途:
- 设计人员可以在 Project 里创建多个
TaskData资源,作为“任务定义库”。
2.2 TaskEntry(任务运行时状态)
文件:TaskEntry.cs
- 运行时由
TaskService创建与维护 - 字段:
id/datastatus:Todo | Completed | ExpiredaddedAt:加入时间(秒)expiresAt:过期时间(秒,0 表示不超时)scheduledRemoveAt:计划移除时间(秒,0 表示不自动移除)
3. 任务状态与规则
状态枚举:TaskStatus(TaskStatus.cs)
Todo:待做Completed:完成Expired:超时
规则(当前版本由系统统一规定):
- 添加任务默认状态为
Todo - 当任务设置了
timeoutSeconds > 0:- 到达
expiresAt后,系统会自动将状态改为Expired
- 到达
- 任务移除策略:
- 默认不自动移除(
autoRemove...AfterSeconds < 0) - 可在
TaskServiceInspector 中设置:autoRemoveCompletedAfterSecondsautoRemoveExpiredAfterSeconds
- 当状态切为
Completed/Expired时,会按配置自动排期移除
- 默认不自动移除(
时间基准:
TaskService.useUnscaledTime = true时,使用Time.unscaledTimefalse时,使用Time.time
4. TaskService(系统 API)
文件:TaskService.cs
4.1 调用方式
- 推荐:在外部脚本中通过 Inspector 挂引用(Slot)调用
- 可选:使用单例
TaskService.Instance(前提是场景里存在一个 TaskService)
4.2 公开 API(最常用)
- 添加(若已存在则失败):
bool AddTask(TaskData data, float timeoutSeconds = -1f)
- 添加或更新(若已存在则更新 data,并可选重置状态):
TaskEntry AddOrUpdateTask(TaskData data, float timeoutSeconds = -1f, bool resetStatusToTodo = false)
- 移除:
bool RemoveTask(string taskId)
- 改状态:
bool SetTaskStatus(string taskId, TaskStatus status)
- 查询:
bool TryGetTask(string taskId, out TaskEntry task)IReadOnlyList<TaskEntry> Tasks
4.3 事件(用于驱动 UI)
Changed:任意任务列表/任务内容变化都会触发- 细粒度事件(可按需订阅):
TaskAddedTaskUpdatedTaskRemovedTaskStatusChanged
5. UI 对接(TaskPanel)
5.1 TaskPanelController(面板控制器)
文件:TaskPanelController.cs
它做的事情:
- 监听
TaskService.Changed,重建列表 - 实例化
taskItemPrefab到listContent - 点击列表项时,更新右侧详情文本(
titleText/detailText)
Inspector 需要绑定的槽位:
taskService:场景里的 TaskServicelistContent:Scroll View 的Viewport/ContenttaskItemPrefab:任务按钮 Prefab(带TaskListItemView)detailText:右侧详情 TMPtitleText:可选(右侧标题 TMP)
5.2 TaskListItemView(列表项视图)
文件:TaskListItemView.cs
它做的事情:
- 通过 Reach 的
ButtonManager.SetText()设置按钮文本 - 按任务状态设置按钮的 Icon 与颜色(待做/完成/超时)
- 将按钮
onClick绑定为“通知 TaskPanelController 选中该任务”
Inspector 需要绑定的槽位:
button:Reach 的ButtonManager(可不填,会在 Awake 尝试自动从同物体 GetComponent)todoIcon/completedIcon/expiredIcon:三种状态的 Icon(Sprite)
6. 编辑器搭建步骤(从 0 到能跑)
目标场景:
Assets/_Project/Scenes/Test/UI_Test.unity
6.1 创建 TaskData 资产
- Project 里新建文件夹(建议):
Assets/_Project/Data/Tasks - 右键 -> Create -> TaskSystem -> Task Data
- 填写:
id(例如:task_open_door)shortName(例如:打开仓库门)description(例如:去工具间找到钥匙,然后打开仓库门。)
6.2 制作任务列表项 Prefab(按钮)
- 在
UI_Test场景里,选中你当前用于测试的任务按钮(例如BtnTaskPrefab) - 确认该按钮上存在 Reach 的
ButtonManager组件 - Add Component:
TaskListItemView - (可选但推荐)Add Component:
Layout Element,设置Preferred Height(例如 56/64),让列表项高度稳定 - 将该按钮拖拽到 Project 生成 Prefab(建议放:
Assets/_Project/Prefabs/UI/Tasks/TaskListItem.prefab)
6.3 在场景中加入 TaskService
- Hierarchy 新建空物体:
TaskSystem - Add Component:
Core.TaskSystem.TaskService - 配置(可选):
Dont Destroy On Load:是否跨场景保留Auto Remove Completed/Expired After Seconds:是否自动移除
6.4 绑定 TaskPanelController
- 在
UI_Test场景中选中TaskPanelRoot(或你认为最合适的面板根) - Add Component:
UI.Tasks.TaskPanelController - 依次绑定槽位:
Task Service->TaskSystem(TaskService)List Content->Left_TaskList/Scroll View/Viewport/ContentTask Item Prefab-> 第 6.2 步创建的 PrefabDetail Text->Right_TaskDetails/DetailTextTitle Text->(可选)右侧标题 TMP
6.5 (可选)使用 TaskBootstrap 快速验证 UI
- 在
TaskSystem物体上 Add Component:Core.TaskSystem.TaskBootstrap Tasks To Add列表中拖入若干TaskData- (可选)设置
Timeout Seconds,观察任务在超时后自动变为“超时”
7. 外部系统如何调用(示例)
示例:当玩家触发某个交互后,完成任务:
- 在你的交互脚本里持有一个
TaskService引用(Inspector 槽位) - 调用:
taskService.SetTaskStatus(taskId, TaskStatus.Completed);
示例:添加一个限时任务:
taskService.AddTask(taskData, timeoutSeconds: 60f);