开发记录覆盖场景切换、状态恢复、A*、NPC 状态、条件对话、任务、商店和 UI 路由。代码组织围绕事件、ScriptableObject 数据和多场景结构展开。每个模块单独建类,数据依赖和调用顺序落在同一条主链上。
2026 年 3 月 18 日到 2026 年 3 月 20 日:项目初始化与基础结构
工程已有玩家状态机、击退处理、商店入口和部分目录结构。初始化部分先整理脚本目录、命名和公共依赖。单位脚本、对话脚本、场景脚本、UI 脚本、ScriptableObject 资源分别归档。预制件、动画和 SO 资源同步分组。任务、对话、商店和多场景切换共享统一目录结构与 Inspector 绑定规则。
目录结构固定后,脚本职责随目录边界拆开。单位逻辑与 UI 逻辑不放在同一目录,对话系统与场景系统各自持有独立入口。多系统协作依赖稳定命名、稳定资源路径和稳定事件引用。
2026 年 3 月 21 日到 2026 年 3 月 24 日:场景切换与状态保存打底
场景切换和状态恢复由 InitialLoad、SceneChanger、DataManager、ISaveable 四层承担。入口场景通过 Addressables.LoadSceneAsync(persistentScene) 加载持久场景,后续加载事件由 SceneLoadEventSO 广播给 SceneChanger。SceneChanger 收到请求后禁用 PlayerMovement、重置血量、暂停时间、记录目标场景和目标位置,再按淡入标记控制过渡动画。
SceneChanger 的核心流程是:卸载当前场景,以 LoadSceneMode.Additive 异步加载目标场景,设置玩家坐标,在 OnLoadCompleted() 中恢复输入和时间。输入禁用、血量重置、暂停和位置切换都集中在同一个类内,传送门、菜单和碰撞脚本只负责发起请求。
状态恢复层由 DataManager 和 ISaveable 组成。DataManager 带有 [DefaultExecutionOrder(-100)],初始化顺序早于普通脚本,内部维护 List<ISaveable>。保存事件触发时,Save() 遍历注册对象,把 Data 实例交给各对象写入。读取事件触发时,Load() 再把同一份 Data 发回各对象。对象侧实现 GetDataID()、SaveData()、LoadData() 即可。
战利品状态接入保存流程。Data.lootsStatsDic 记录战利品位置和拾取状态,用于重载场景时恢复交互物状态。GUID 与对象状态进入同一张数据表。
2026 年 3 月 25 日到 2026 年 3 月 31 日:A* 寻路
AStarPathFinder 完成主要结构。路径搜索接口是 FindPath(Vector3 optPos, Vector3 startPos, Vector3 endPos),内部先把世界坐标转成网格坐标,再用 Dictionary<Vector3, PathFinderDetails> 维护开启列表,用 HashSet<Vector3> 维护关闭列表。SearchCheapestCost() 负责取总代价最低节点,RetracePath() 负责从终点回溯父节点并生成路径栈。
optPos 是一层前置优化。optPos 不为零时,程序先检查该点对应网格是否可通过,再用 NoCoverObstacleNodes() 判断起点到 optPos 的直线段是否存在障碍。直线路径可用时,搜索起点前移到 optPos,开启列表规模随之缩小。
八方向移动与对角穿墙检测是寻路层的另一条规则。AddNodeToOpen() 在展开邻居时调用 CanWalkDiagonally()。斜向移动时,如果横向和纵向相邻格子都被障碍占用,路径搜索禁止沿对角线穿过。路径结果因此与碰撞结果保持一致。
导航数据由 AStarNodeManager 从 Tilemap 和 Collider2D 生成。地图切换后,沿用相同图层与碰撞规则即可刷新网格数据。NPC、敌人和交互对象共享这层导航数据。
2026 年 4 月 1 日到 2026 年 4 月 6 日:NPC、动画与对话入口
NPC 行为按状态拆开。NPCStateController 管理 Idle、Wander、Patrol、Chat,NPCWander 与 NPCPatrol 处理移动,NPCChat 处理交互入口和对话触发。状态切换通过启用或禁用脚本完成。角色接近 NPC 并触发交互时,移动脚本停下,对话入口接管当前状态。
动画与交互时机跟随同一条状态链移动。NPCChat 负责拉起 DialogManager,NPCStateController 负责停掉游荡或巡逻脚本。移动、动画、交互、面板打开分别由不同脚本承担。
店主和普通 NPC 沿用同样的拆分方式。店主脚本只负责商店入口,商品内容和商店 UI 交给 ShopManager。普通 NPC 只负责对话入口,对话条件与历史回写交给 DialogManager 和历史管理器。
2026 年 4 月 7 日到 2026 年 4 月 10 日:菜单、暂停与 Retry 逻辑
ESC 菜单、暂停和 Retry 共用输入、动画、场景和玩家状态。时序问题集中在暂停状态、死亡重载、淡入淡出和输入恢复几个位置。由UI 管理层统一控制,不让单个按钮各自维护一套条件。
UI 射线阻挡、面板状态残留、按钮事件和场景状态不同步。问题在 UI 管理层。商店、任务、对话、属性和 ESC 面板共用一套开关路由,状态切换由统一管理器分发。
2026 年 4 月 13 日到 2026 年 4 月 19 日:条件对话与对话历史
DialogSO、DialogManager、ConversationHistoryManager、ItemHistoryManager 组成一套结构。DialogSO 保存主说话角色、对话文本数组、选项分支、角色前置条件、物品前置条件、拒绝对话节点和一次性触发限制。对话节点从“文本 + 下一句”扩展成带条件、带分支、带历史回写的数据对象。
DialogManager.StartDialog() 先执行 MatchConditionsToStartDialog()。角色条件通过 ConversationHistoryManager.CharactersHasChated 检查,物品条件通过 ItemHistoryManager.HasPickedOverAmount() 检查,一次性对话通过 DialogSO.HasChated 检查。条件不满足时,StartRefuseDialog() 按 ChatType 切到拒绝分支,界面继续显示对应内容。
对话结束时,EndDialog() 把 mainCharacter 写进 ConversationHistoryManager。ConversationHistoryManager 用 HashSet<CharacterSO> 记录已触发角色,ItemHistoryManager 用 Dictionary<ItemSO, int> 记录物品累计数量。历史系统服务条件判断。任务节点、分支对话和拒绝对话都从历史系统读取结果。
选项按钮纳入 DialogManager。ShowChoices() 遍历 nextDialogOptions,把选项文本写进按钮,再通过 onClick 注册进入下一节点。主分支、拒绝分支、一次性对话和普通默认对话共用同一套结构。
2026 年 4 月 20 日到 2026 年 4 月 24 日:任务系统成型
任务系统主体是 QuestManager。类内部维护 Dictionary<QuestSO, QuestProgressData>,每个任务对应一个状态和一张目标进度表。QuestProgressData 使用 Dictionary<QuestObjective, int> 保存目标当前值。任务板打开时,OnReFreshQuestState() 先初始化进度表,再刷新 QuestLogSlot。任务板为空时,详情页与提示页切到空任务状态。
任务状态流转由 QuestStateChanged() 控制。状态变化时,接取、放弃、完成按钮区域同步切换,QuestLogUI 刷新目标文本。任务完成时,RaiseRewardEvent() 遍历奖励列表,把奖励写进 InventorySlotsStatsSO。奖励发放由事件进入库存更新流程,不直接改背包槽位。
任务进度统计直接依赖历史系统。UpdateObjectiveProgress() 在目标是物品时读取 ItemHistoryManager 的累计数量,在目标是角色时读取 ConversationHistoryManager 的对话记录。任务脚本不需要维护 NPC 交互细节。任务目标和对话历史、拾取记录共享同一套底层数据。
QuestObjective 同时包含目标物品、目标角色和目标地点字段。地点统计接口已经留在数据结构中,地图触发点逻辑预留在该字段上。
2026 年 4 月 26 日到 2026 年 4 月 29 日:奖励、商店、UIManager 与 Addressables
奖励发放、商店、UI 路由和 Addressables 场景加载整理成同一条调用链。QuestManager.RaiseRewardEvent() 把奖励发给库存系统,ShopManager 接收商品列表并打开商店面板,UIManager 负责面板互斥,场景链路由 SceneChanger 和 Addressables 维持。
ShopManager 内部维护一般物品、武器、防具三张列表。OnShopLoad() 接到 ShopLoadEventSO 后,把列表保存到管理器,再打开商店 CanvasGroup。购买调用 RaiseInventoryUpdateRequest(item, price, 1)。出售沿用同一接口,只把价格和数量改成负值。买卖逻辑共用同一条库存更新链路。
UIManager 在 Update() 中读取 ESC、技能、属性、任务、对话、商店等输入,判断当前是否已有面板打开,再通过一组 ToggleCanvasEventSO 广播目标状态。管理器不直接硬编码每个面板的开关。面板开关和互斥关系都经事件系统分发。场景切换事件进入时,ResetCanvas() 把托管面板全部关掉,清空跨场景残留。
Addressables 场景链路固定为统一顺序。场景加载、场景卸载、淡入淡出、玩家状态恢复按同一顺序执行,普通加载接口不混入该路径。场景调试路径和构建路径保持一致。
结果
场景切换、状态恢复、A*、NPC 行为、条件对话、任务进度、奖励发放、商店和 UI 管理纳入同一条主链。系统边界如下:场景切换用事件和 Addressables,状态恢复用 DataManager,路径搜索用 A*,对话和任务读取历史系统,面板互斥由 UIManager 统一处理。
部分信息可能已经过时
粤公网安备44011102484817号