当前位置 : 145z游戏站 | 热血传奇 | 技术教程 | 

Unity物品系统开发:数据结构和模块化架构完整方案

热度:
Unity里面做物品系统,最怕的是做到一半发现架构撑不住。前期图省事用几个List存物品,后面加背包加仓库加交易加装备栏的时候,代码改得跟面条一样。一开始把数据结构定好,后面几百种物品往里塞都不会乱。

**物品数据结构的设计**

物品数据分成两层。底层是ItemData,所有物品共享的基类,放共有的字段。上层派生类放特定类型的独有字段。ItemData里至少包含itemId、itemName、iconPath、itemType、stackable、maxStack、sellPrice、buyPrice这些基本字段。

itemType用枚举做区分,Weapon、Armor、Consumable、Material、Quest、Currency。不同的枚举值决定后续逻辑分支怎么走。

派生类WeaponData加damage、attackSpeed、weaponType字段。ArmorData加defense、durability、armorType字段。ConsumableData加restoreHealth、restoreMana、buffId字段。关键是派生类不重复定义基类已有字段,用继承把共用的东西收上去。

**物品配置文件:ScriptableObject**

每个物品作为单独的配置文件存在,用ScriptableObject做。在Project面板右键Create创建ItemData的派生资产,填好各项数值。这样做的好处是不需要维护一个大表格,单个物品改属性不会影响其他物品,也方便版本管理。

项目里加一个ItemDatabase的ScriptableObject,把所有物品引用装进去,运行时通过itemId查找。查找用Dictionary存,启动时ItemDatabase初始化把列表转成字典,查的时候O(1)复杂度,比遍历List快得多。

**背包系统的数据管理**

背包的数据层和显示层分离。InventoryManager单例管数据,InventoryUI管显示。InventoryManager里存一个List<ItemSlot>,每个ItemSlot包含itemId和quantity两个字段。空槽位用itemId=0表示。

添加物品的逻辑:先检查背包里有没有同ID的可堆叠物品,有的话加数量到maxStack上限,剩下的再开新槽位。没有可堆叠的就直接找空槽位填入。加完触发OnInventoryChanged事件,InventoryUI监听事件刷新显示。

移除物品的逻辑:指定槽位和数量,数量减到0就把槽位置空。同样触发事件更新UI。

格子锁定功能用额外字段记录每个槽位的locked状态。角色死亡或者特定场景下锁定部分格子,玩家拖拽不了也使用不了里面的物品。

**装备栏和快捷栏的关联**

装备栏也是ItemSlot列表,但每个部位固定一个槽位。Head、Chest、Legs、Weapon、OffHand、Accessory这些部位枚举绑定对应的槽位索引。

穿戴装备时从背包里移除物品,生成一个装备实例放进装备栏对应槽位。属性加成立即生效,加到角色状态管理器上。卸下装备时反向操作,装备实例转成物品数据放回背包。

快捷栏是背包的子集。快捷栏的每个格子绑定一个背包槽位索引,显示背包里对应槽位的物品。使用快捷栏时直接操作背包数据。

**物品生成与掉落系统**

掉落系统用ObjectPool或者直接Instantiate创建地面物品实例。地面物品挂一个Pickup组件,包含itemId和quantity。玩家靠近按交互键触发拾取,调用背包的添加接口。

掉落表用LootTableScriptableObject配置。每个怪物掉落表里存一组LootEntry,entry包含itemId、minQuantity、maxQuantity、dropChance。掉落计算时遍历所有entry,随机数判定是否掉落。

**拖拽交互的实现**

Unity的UI事件系统处理拖拽。背包格子挂IBeginDragHandler、IDragHandler、IEndDragHandler接口。拖拽开始的时候记录源槽位和源容器类型,拖拽过程中生成一个跟鼠标移动的图标,拖拽结束的时候检测鼠标悬停的目标槽位,执行交换或者移动逻辑。

交换逻辑几种情况要分开处理:源和目标都是背包,直接交换两个槽位的数据。源是背包目标是装备栏,尝试穿戴。源是装备栏目标是背包,尝试卸下。源和目标是同一个槽位,取消操作。源是快捷栏目标是背包,从快捷栏解绑背包槽位再绑定新槽位。

**存档与读档**

物品系统的存档用JSON序列化。整个背包数据转成string存PlayerPrefs或者文件里。装备栏的数据单独存。序列化的时候注意ItemSlot里的itemId存的是配置文件ID,不是运行时实例引用。反序列化时通过ItemDatabase重新获取物品配置。

存档数据的结构用SaveData类包装,包含背包列表、装备栏列表、快捷栏绑定列表。版本号字段留好,以后加新字段的时候做兼容处理。

**模块化设计的关键**

InventoryManager不直接操作UI,UI只负责展示数据。物品的加属性效果不写在物品类里,用独立的EffectSystem处理。掉落系统不关心背包满了怎么处理,只管生成掉落物。各模块通过事件通讯,减少耦合。

新增一种物品类型的时候,只需要建一个新的派生类加新字段,其他系统不需要改动。这就是把基类设计好带来的扩展性。UI部分的物品显示用预制体模板,数据驱动刷新,有新品种物品也不用改UI代码。
[顶部]