背包系统设计文档 (除了网络协议转发,数据库存储, 前后端逻辑UI 全部由Pink自己改写的,包括prefab 我自己手动改了一点内容)
目录
系统概述
背包系统是一个基于网格的物品管理系统,支持不同尺寸的物品放置、拖拽移动、装备穿戴/卸下、物品丢弃等功能。
主要特性
-
网格化背包:10×4 的网格布局,支持多格物品
-
装备槽位:10个装备槽位,支持拖拽穿戴/卸下
-
实时高亮:拖拽时显示可放置区域的高亮提示
-
服务器验证:所有操作需要服务器确认,失败时自动回滚
核心组件
1. BagPage.ts
背包页面主控制器,负责:
-
网格创建和管理
-
物品节点的创建和销毁
-
装备槽位初始化
-
事件监听和处理
-
拖拽高亮显示
2. BackpackItem.ts
背包物品组件,负责:
-
物品的拖拽交互
-
触摸事件处理(点击/拖拽区分)
-
物品尺寸管理
-
图标和名称显示
3. EquipSlotItem.ts
装备槽位组件,负责:
-
装备槽位的显示和高亮
-
装备图标的拖拽
-
槽位类型匹配检测
4. ItemData.ts / UserItem.ts
数据管理类,负责:
-
物品配置数据
-
用户物品数据
-
背包数据列表管理
网格系统
网格配置
GRID_SIZE = 48 // 单个格子尺寸(像素)
GRID_WIDTH = 10 // 横向格子数
GRID_HEIGHT = 4 // 纵向格子数
GRID_OFFSET_Y = 192 // 网格Y轴偏移(GRID_SIZE * 4)
ITEM_OFFSET_Y = 20 // 物品Y轴偏移修正
坐标系统
-
网格坐标:左上角为 (0, 0),向右为X正方向,向下为Y正方向
-
本地坐标:节点中心为原点的标准坐标系
坐标转换方法
// 网格坐标 → 本地坐标
gridToLocalX(gridX, itemWidth) // X轴转换
gridToLocalY(gridY, itemHeight) // Y轴转换(用于网格)
gridToItemLocalY(gridY, itemHeight) // Y轴转换(用于物品,含偏移修正)
// 本地坐标 → 网格坐标
getGridPosition(itemNode) // 返回 { gridX, gridY }
网格占用管理
usedSlots: Map<string, Node> // key: "x,y", value: 占用该格子的物品节点
物品管理
物品属性
| 属性 | 类型 | 说明 |
|------|------|------|
| series | any | 物品唯一序列号 |
| item_id | number | 物品配置ID |
| width | number | 物品宽度(格子数) |
| height | number | 物品高度(格子数) |
| posX | number | 网格X坐标 |
| posY | number | 网格Y坐标 |
| num | number | 物品数量 |
| storage_id | number | 存储位置(0=背包) |
物品操作
创建物品
createItem(itemCfg, uItem, series): Node
-
实例化物品预制体
-
设置尺寸、序列号、图标
-
添加到背包节点
放置物品
onNetAddPlaceItem(itemNode, gridX, gridY) // 网络添加
tryPlaceItem(itemNode): boolean // 尝试放置
-
边界检查
-
碰撞检测
-
更新位置和存储
移除物品
removeItemFromStorage(itemNode) // 清理网格占用
removeItemFromStorageByGrid(itemNode, gridX, gridY) // 按坐标清理
丢弃物品
tryDiscardItem(itemNode): boolean
-
仅背包物品可丢弃,装备槽位装备不可丢弃
-
弹出确认弹窗
-
确认后发送删除请求
-
服务器返回后销毁节点
装备槽位系统
槽位配置
| SlotIndex | 槽位名称 | 节点属性 |
|-----------|----------|----------|
| 1 | 左手(武器) | equipSlot_weapon |
| 2 | 右手(盾牌) | equipSlot_shield |
| 3 | 衣服(胸甲) | equipSlot_chestarmor |
| 4 | 帽子(头盔) | equipSlot_headarmor |
| 5 | 手套 | equipSlot_glove |
| 6 | 鞋 | equipSlot_boots |
| 7 | 项链(护符) | equipSlot_amulet |
| 8 | 左戒 | equipSlot_ring1 |
| 9 | 右戒 | equipSlot_ring2 |
| 10 | 腰带 | equipSlot_belt |
槽位操作
穿戴装备
tryEquipItemToSlot(itemNode, worldPos): boolean
-
检测悬停的槽位
-
验证装备类型匹配
-
验证使用条件(等级等)
-
隐藏物品节点,禁用触摸事件
-
发送穿戴请求
-
等待服务器确认
卸下装备
tryUnequipToBag(worldPos, series, itemWidth, itemHeight): boolean
-
计算目标网格坐标
-
验证位置有效性
-
发送卸下请求
-
等待服务器确认
槽位高亮
updateEquipSlotHoverHighlight(itemNode, worldPos) // 更新高亮
clearEquipSlotHighlight() // 清除高亮
-
绿色:可装备
-
红色:不可装备
拖拽交互
背包物品拖拽流程
┌─────────────────┐
│ TOUCH_START │
│ 记录起始位置 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ TOUCH_MOVE │
│ 超过阈值(5px)? │
└────────┬────────┘
│
┌────┴────┐
│ 是 │ 否
▼ ▼
┌───────┐ ┌───────┐
│ 拖拽 │ │ 等待 │
│ 模式 │ │ │
└───┬───┘ └───────┘
│
▼
┌─────────────────┐
│ 清理旧位置存储 │
│ 更新高亮显示 │
│ 检测装备槽位 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ TOUCH_END │
└────────┬────────┘
│
┌────┴────────────┬─────────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ 装备槽位 │ │ 背包外 │ │ 背包内 │
│ 穿戴装备 │ │ 丢弃物品 │ │ 移动物品 │
└───────────┘ └───────────┘ └───────────┘
装备槽位拖拽流程
┌─────────────────┐
│ TOUCH_START │
│ 检查是否有装备 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ TOUCH_MOVE │
│ 创建拖拽图标 │
│ 更新背包高亮 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ TOUCH_END │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│背包内 │ │背包外 │
│卸下 │ │无操作 │
│装备 │ │(不可 │
│ │ │丢弃) │
└───────┘ └───────┘
│
▼
┌─────────────────┐
│ 销毁拖拽图标 │
└─────────────────┘
高亮颜色
Colors = {
DEFAULT: Color(180, 180, 180), // 默认
VALID: Color.GREEN, // 可放置
INVALID: Color.RED, // 不可放置
OCCUPIED: Color.BLUE // 已占用
}
网络协议
物品移动 (Protocol 6)
// 请求:移动物品
CSMoveItemReq {
series: ItemSeries, // 物品序列号
posX: number, // 目标X坐标
posY: number // 目标Y坐标
}
// 响应:移动结果
事件: BagViewEventType.MOVE_ITEM_RESULT
data: { series, result } // result: 1=成功
装备穿戴 (Protocol 7-1)
// 请求:穿戴装备
CSFitOutEquip {
series: ItemSeries, // 物品序列号
slot: number, // 槽位索引
type: number // 0=玩家, 1=英雄
}
// 响应:穿戴结果
事件: EquipEventType.EQUIP_RESULT
data: { result } // result: 1=成功
装备卸下 (Protocol 7-3)
// 请求:卸下装备到指定位置
CSTakeOffEquipToPos {
series: ItemSeries, // 装备序列号
posX: number, // 背包X坐标
posY: number // 背包Y坐标
}
物品删除 (Protocol 6)
// 请求:删除物品
CSDeleteItemReq {
series: ItemSeries // 物品序列号
}
// 响应:删除成功
事件: ItemEventType.ITEM_DEL / BAG_ITEM_DEL
装备删除 (Protocol 7-6)
// 响应:删除装备
SCDelOneEquip {
series: ItemSeries // 装备序列号
}
事件系统
物品事件 (ItemEventType)
| 事件名 | 说明 |
|--------|------|
| ITEM_ADD | 物品添加 |
| ITEM_DEL | 物品删除 |
| ITEM_CHANGE | 物品变化 |
| BAG_ITEM_ADD | 背包物品添加 |
| BAG_ITEM_DEL | 背包物品删除 |
| BAG_ITEM_CHANGE | 背包物品变化 |
| ITEM_LIST | 物品列表更新 |
背包事件 (BagViewEventType)
| 事件名 | 说明 |
|--------|------|
| MOVE_ITEM_RESULT | 移动物品结果 |
| FLUSH_BAG_GRID | 刷新背包格子 |
| UPDATE_BAG_GRID | 更新背包格子 |
装备事件 (EquipEventType)
| 事件名 | 说明 |
|--------|------|
| EQUIP_RESULT | 装备穿戴结果 |
对象事件 (ObjectEventType)
| 事件名 | 说明 |
|--------|------|
| EQUIP_DATA_CHANGE | 装备数据变化 |
| MAIN_ROLE_ATTR_CHANGE | 主角属性变化 |
待确认机制
物品移动待确认
pendingMoveItems: Map<string, {
itemNode: Node,
oldGridX: number,
oldGridY: number,
newGridX: number,
newGridY: number
}>
-
移动成功:更新 startPos
-
移动失败:还原到原位置
装备穿戴待确认
pendingEquipItem: {
itemNode: Node,
slotIndex: number,
series: any
} | null
-
穿戴成功:销毁物品节点
-
穿戴失败:恢复显示,还原位置,重新注册事件
文件结构
assets/scripts/game/bag/
├── BagPage.ts # 背包页面主控制器
├── BackPackItem.ts # 背包物品组件
├── EquipSlotItem.ts # 装备槽位组件
├── BagCtrl.ts # 背包控制器(网络请求)
├── ItemData.ts # 物品数据管理
└── UserItem.ts # 用户物品数据结构
assets/scripts/gameui/ui/role/
├── RoleController.ts # 角色控制器(装备协议处理)
├── EquipData.ts # 装备数据管理
└── RoleData.ts # 角色数据
assets/scripts/game/protocol/
├── Protocol6.ts # 物品相关协议
└── Protocol7.ts # 装备相关协议
注意事项
-
服务器验证:所有物品操作都需要服务器确认,客户端只做预处理
-
碰撞检测:放置物品前必须检查目标区域是否被占用
-
边界检查:物品不能超出背包网格范围
-
装备限制:装备槽位的装备不可丢弃,只能卸下到背包
-
事件清理:物品节点销毁前需要移除触摸事件监听
-
坐标一致性:网格坐标和本地坐标转换时注意偏移量
文档版本: 1.0
最后更新: 2026年2月3日



