diff --git a/docs/superpowers/specs/2026-08-04-player-avatar-selection-design.md b/docs/superpowers/specs/2026-08-04-player-avatar-selection-design.md new file mode 100644 index 00000000..7c0af97e --- /dev/null +++ b/docs/superpowers/specs/2026-08-04-player-avatar-selection-design.md @@ -0,0 +1,142 @@ +# 角色属性头像选择设计 + +## 背景 + +`Client/Assets/Script/xmain/Module/Player` 下已有角色资料数据、资料存储和 HUD/详情面板。`PlayerProfileData` 已包含 `AvatarId`,但现有界面只显示固定的占位头像,用户无法选择头像。 + +当前工作区已有 54 张头像资源: + +`Client/Assets/Game/Art/UI/Texture/Icon/GamerPic/GamerPic_01.png` ~ `GamerPic_54.png` + +本功能在现有角色资料系统上增加头像选择能力,并提供独立的选择界面。 + +## 目标 + +- 在角色资料详情中提供“更换头像”入口。 +- 展示 54 张可选头像。 +- 支持点击头像后即时预览,但只有点击“确认”才保存。 +- 确认后同步刷新资料详情、HUD 和其他依赖玩家资料的头像展示。 +- 保持已有 `AvatarId = 0` 的旧数据兼容。 +- 使用项目现有的异步资源加载、JSON UI 描述和 Prefab 生成规范。 + +## 非目标 + +- 不新增头像解锁、付费、分类或搜索功能。 +- 不修改角色战斗属性、职业或等级系统。 +- 不实现服务端头像保存协议;本次只更新现有客户端 `PlayerProfileStore` 数据。 +- 不重构现有 UI 资源目录或处理与本需求无关的工作区资源变更。 + +## 方案 + +采用固定 JSON/Prefab 槽位方案,而不是运行时创建网格。 + +新增 `UI_PlayerAvatarPicker` Dialog 预制体,固定包含 54 个头像槽位。每个槽位直接引用一个 `GamerPic_XX` Sprite,运行时代码只处理选择、高亮、预览和确认。这样布局可在 Unity 编辑器中检查,资源引用稳定,也符合项目 UI 持久化规范。 + +## 数据与资源 + +### AvatarId 规则 + +- `0`:兼容旧存档,显示 `GamerPic_01`。 +- `1`~`54`:对应同编号的 `GamerPic_XX`。 +- 负数或大于 54:显示默认头像,不阻塞界面。 +- 用户点击确认后保存 `1`~`54`。 + +### 资源目录 + +- 头像:`Client/Assets/Game/Art/UI/Texture/Icon/GamerPic/` +- 通用 UI 资源:使用当前工作区中已有的 `Client/Assets/Game/Art/UI/Texture/UI/Common/` 资源。 +- 头像选择预制体:`Client/Assets/Game/Art/UI/Prefab/UI_PlayerAvatarPicker.prefab` +- 头像选择 JSON:`Doc/UIPrefabCreater/UI_PlayerAvatarPicker.json` + +## 组件边界 + +### PlayerAvatarCatalog + +新增头像目录类,负责: + +- 暴露头像数量和默认头像 ID。 +- 将头像 ID 转换为 `GamerPic_XX` 资源路径。 +- 将旧 ID、负数和超范围 ID 归一化为可显示的默认头像。 + +目录类不保存玩家状态,也不处理 UI。 + +### PlayerProfileModule + +扩展现有角色资料模块,负责: + +- 异步加载并刷新 HUD、详情面板中的头像。 +- 绑定详情面板的头像入口。 +- 异步打开和关闭头像选择弹窗。 +- 持有临时 `pendingAvatarId`,隔离预览状态与已保存资料。 +- 更新选择槽位高亮和预览头像。 +- 点击确认时更新 `PlayerProfileStore`。 + +异步回调必须校验当前视图仍存在,且回调对应的头像 ID 仍是当前请求,避免关闭界面或快速切换时旧回调覆盖新状态。 + +### PlayerProfileStore + +继续作为玩家资料的唯一状态源。确认头像时通过已有 `Update` 接口修改 `AvatarId`,由 `Changed` 事件驱动所有资料视图刷新。 + +## 界面设计 + +### 资料详情面板 + +- 保留现有昵称、UID、职业、货币和战斗属性布局。 +- 头像区域增加可点击的“更换头像”入口。 +- 当前头像继续使用头像框包裹,并替换内部固定占位图。 + +### 头像选择弹窗 + +挂载到 `UILayer.Dialog`,包含: + +- 遮罩层:点击后取消选择。 +- 弹窗标题:`选择头像`。 +- 当前头像预览区:头像框、预览头像和选择提示。 +- 可滚动头像列表:6 列 × 9 行,共 54 个头像槽位。 +- 每个头像槽位:头像 Image、选中高亮 Image、点击 Button。 +- 底部按钮:`取消`、`确认`。 +- 右上角关闭按钮:等同取消。 + +界面使用现有蓝绿描边弹窗、深色文字、TextMeshProUGUI 和通用按钮/列表资源。头像 Image 保持等比显示。 + +## 交互流程 + +1. 用户打开角色资料详情。 +2. 用户点击头像区域或更换入口。 +3. 模块异步加载并实例化头像选择弹窗。 +4. 弹窗以当前资料头像初始化 `pendingAvatarId` 和选中态。 +5. 用户点击任意头像时,只更新预览和选中高亮。 +6. 点击取消、关闭或遮罩时销毁弹窗,不调用 `PlayerProfileStore.Update`。 +7. 点击确认时调用 `PlayerProfileStore.Update` 保存头像 ID,然后关闭选择弹窗。 +8. `PlayerProfileStore.Changed` 触发 HUD、详情面板和头像展示刷新。 + +## 异常处理 + +- 头像资源加载失败:保留 `ico_user` 占位头像并记录一次错误日志。 +- 头像 ID 非法:按默认头像显示,不抛出 UI 阻塞异常。 +- 重复点击打开:只允许一个选择弹窗实例。 +- 关闭弹窗后仍返回的异步加载:丢弃回调结果。 + +## 测试与验收 + +### EditMode 测试 + +- 验证头像 ID 到 `GamerPic_XX` 路径的映射。 +- 验证 0、负数和超范围 ID 都回退默认头像。 +- 验证取消不会触发资料变更事件。 +- 验证确认只修改 `AvatarId`,不改变其他资料字段。 +- 验证头像变更后资料模块刷新 HUD 与详情视图。 +- 验证 `UI_PlayerAvatarPicker.json` 通过 JSON Prefab Builder 校验。 + +### Unity 编辑器验证 + +- 头像选择预制体可正常加载。 +- 54 个头像槽位排列为 6 列 × 9 行并可滚动。 +- 当前头像有选中高亮。 +- 点击头像会更新预览但不会提前保存。 +- 确认、取消、关闭和遮罩点击行为符合交互流程。 +- 确认后的头像同时出现在资料详情和 HUD。 + +## 验收标准 + +用户进入角色属性详情后,点击头像可以打开选择界面;选择任意头像可以即时预览但在确认前不保存;点击确认后头像 ID 保存并同步更新详情页和 HUD;取消选择后原头像保持不变。