Appearance
XMapEngine 三层架构重构
将小鹏一张图的 SR 渲染引擎从“能跑但难扩展”的单体结构,改造成职责清晰、能承接手势交互和地图状态演进的三层架构。
交互延迟1 帧 → 0 帧
架构层级Core / Services / Tools
依赖治理ServiceContainer / DI
背景
XMapEngine 是小鹏一张图 SR 渲染链路中的核心模块,承接节点树、地图状态、相机控制、手势交互和跨端消息。入职初期通读代码时,最大的风险不是某个 bug,而是系统边界已经开始模糊:手势识别、地图状态管理、相机控制互相直接调用,新增功能会牵动多个模块。飞书工作总结中把这条线进一步归纳为 ServiceContainer + DI/IoC 的依赖治理问题,本质是让模块依赖显式化、可替换、可审查。
当时业务还在快速迭代,V5 交互、回自车、LD/SD 切换、车模大小补偿等需求都要进入同一条链路。如果继续在单体结构里叠逻辑,后续每次改手势或镜头都会变成全局风险。
问题
| 问题 | 表现 | 风险 |
|---|---|---|
| 职责堆叠 | 手势、地图状态、渲染控制在同一层互相调用 | 新增功能需要跨模块改动 |
| 状态分散 | 比例尺、相机高度、图层切换散落在多个类 | 回自车和切换场景容易抖动 |
| 扩展困难 | 新手势和新镜头行为缺统一入口 | 每次迭代都要重新理解调用链 |
| 依赖隐式 | 服务对象和工具对象到处被直接拿取 | 测试、替换、灰度和回滚成本都变高 |
约束
- 线上链路不能大拆大换,必须在业务迭代中逐步替换。
- Android 原生层和 Unity 层各有一套手势识别,行为要统一。
- 地图比例尺、相机高度、车模显示和 LD/SD 切换存在耦合,不能只做代码分层。
方案
重构目标不是“为了三层而三层”,而是把变化点放到正确位置。
text
Core
节点树管理 / 生命周期 / 事件总线
Services
ServiceContainer / 手势服务 / 地图状态服务 / 相机服务 / 图层服务
Tools
坐标转换 / 动画工具 / 日志工具 / 调试辅助关键设计包括:
- 手势策略化:单指拖拽、双指缩放、双击、惯性甩图等 8 类手势独立实现,通过统一接口接入。
- 回自车状态机:把 start、ing、end 三态显式化,降低车模、比例尺、相机高度之间的隐式耦合。
- LateUpdate 对齐:LD 地图移动统一在 OnLateUpdate 对齐手势,避免视觉错帧。
- 服务上提:地图状态和相机状态不再散在工具类里,由 Services 层统一协调。
- 依赖容器化:用 ServiceContainer 承载核心服务注册和获取,把“谁依赖谁”从隐式调用改成可审查的依赖关系。
产出
- XMapEngine 分层架构图和节点树说明。
- 手势接口化改造方案。
- ServiceContainer 和 DI/IoC 依赖治理方案。
- IMapEngine 抽象和对接文档。
- 地图状态、相机状态、跨端消息的调用时序图。
效果
这次重构直接支撑了后续几个高频场景:
- 拖拽错 1 帧问题最终被抹平到 0 帧延迟。
- 回自车视觉抖动问题有了状态机承载点。
- V5 车模大小、手势轻扫、比例尺对齐不再只能靠局部补丁。
- 新服务可以先进入容器和接口,再逐步替换旧调用点,降低边做业务边重构的风险。
- 后续工程文档可以围绕 Core / Services / Tools 讲清楚模块边界。
可复用方法论
- 先找变化点,再命名分层。
- 状态问题不能只靠工具类解决,必须有拥有者。
- 交互系统要同时看输入、状态、渲染时机,不能只看手势识别。
- 架构文档不是收尾材料,而是重构过程中的同步工具。
复盘
这篇案例的价值不在于“三层架构”这个名字,而在于它把一个持续变化的业务引擎从经验维护变成可讨论、可扩展、可交接的系统。下一轮补充资料时,应优先从飞书文档中拉取架构图、对接文档和评审记录,脱敏后补入图表。