第七章 装备系统
装备系统是提升角色属性的重要系统,本篇博客重点实现装备系统UI的设计以及装备栏与物品栏的交互。
7.1 基础界面的设计
导入装备UI的模型,命名为Equipment,并添加5个方框,用以放置装备,如下图
当点击功能面板的装备按钮时,显示动画(方法见
https://blog.csdn.net/s1314_JHC/article/details/79690742 下的5.5节),不赘述。
我们在每个格子中再创建一个Child Sprite,当实例化时直接给这个Child Sprite添加装备图标即可。拖放到Prefab之中,方便之后使用
在Equipment脚本中管理这6个格子
private GameObject headgear; //创建GameObject并赋值 void Awake() { headgear = transform.Find ("Headgear").gameObject; armor = transform.Find ("Armor").gameObject; left_hand = transform.Find ("Left_hand").gameObject; right_hand = transform.Find ("Right_hand").gameObject; shoe = transform.Find ("Shoe").gameObject; accessory = transform.Find ("Accessory").gameObject; }
7.2 装备信息的添加与管理
这节我们实现装备信息的添加以及模拟拾取功能,与药品信息类似,我们创建装备信息的格式如下。
内容依旧添加在之前的文档ObjectsInfoInList之中,创建示例如下所示。
2001,simpleHeltForMagician,icon-helm-03,Equip,0,10,0,Headgear,Magician,60,150 2002,fineHeltForMagician,icon-helm-02,Equip,0,20,0,Headgear,Magician,80,200 2003,brokenArmorForMagician,armor3-icon,Equip,0,30,0,Armor,Magician,60,150 2004,fineArmorForMagician,armor2-icon,Equip,0,50,0,Armor,Magician,80,200 2005,brokenShield,icon-shield,Equip,0,30,0,Left_hand,Common,80,200 2006,fineShield,icon-shield1,Equip,0,50,0,Left_hand,Common,100,250 2007,brokenRod,rod-icon,Equip,30,0,0,Right_hand,Magician,60,150 2008,fineRod,rod-icon02,Equip,30,0,0,Right_hand,Magician,80,200 2009,brokenShoe,icon-boot0,Equip,0,0,30,Shoe,Common,60,150 2010,fineShoe,icon-boot0-01,Equip,0,0,50,Shoe,Common,80,200 2011,brokenRing,icon-ring-01,Equip,0,30,0,Accessory,Common,60,150 2012,fineRing,icon-ring,Equip,0,50,0,Accessory,Common,80,200
在ObjectsInfo之中,我们在public class ObjectInfo添加几个适用于装备的成员变量
public class ObjectInfo{ public int attack; //攻击 public int defense; //防御 public int speed; //速度 public ApplicationType appType; //武器适用角色 public DressType dressType; //穿戴部位 }
其中ApplicationType和DressType单独定义为枚举类型,即
public enum ApplicationType{ Swordman, Magician, Common } public enum DressType{ Headgear, Armor, Left_hand, Right_hand, Shoe, Accessory }
在从ObjectsInfoInList.txt文档中读取数据到字典objectInfoDict的时候,之前在药品信息系统中所采用的判断语句if(type == ObjectType.Drug),对其进行修改,即可添加装备信息
else if(type == ObjectType.Equip) { info.attack = int.Parse(proArray[4]); info.defense = int.Parse(proArray[5]); info.speed = int.Parse(proArray[6]); info.price_sell = int.Parse(proArray[9]); info.price_buy = int.Parse(proArray[10]); string str_dressType = int.Parse(proArray[7]); switch(str_dressType) { case "Headgear": info.dressType = DressType.Headgear; break; case "Armor": info.dressType = DressType.Armor; break; case "Left_hand": info.dressType = DressType.Left_hand; break; case "Right_hand": info.dressType = DressType.Right_hand; break; case "Shoe": info.dressType = DressType.Shoe; break; case "Accessory": info.dressType = DressType.Accessory; break; } string str_appType = int.Parse(proArray[8]); switch(str_appType) { case "Swordman": info.appType = ApplicationType.Swordman; break; case "Magician": info.appType = ApplicationType.Magician; break; case "Common": info.appType = ApplicationType.Common; break; } }
即可,添加完后应当可以在物品栏中通过“X”键的拾取模拟来添加装备
(参考
https://blog.csdn.net/s1314_JHC/article/details/79690742 5.4节,只需修改随机数的范围即可),判断是否添加成功,如下左图所示
7.3 装备信息提示的更新
模拟添加成功后,将鼠标移到装备上方,此时的物品显示存在问题(上右图),需要进行修改
修改处在InventoryDes的Show()方法下,在之前的判断中,用的是switch (info.type) 的case:Drug来控制药品信息栏的显示,针对装备,我们增加case ObjectsInfo.ObjectType.Equip: 以控制装备的显示,即
switch (info.type) { case ObjectsInfo.ObjectType.Drug: des = GetDrugDes(info); break; case ObjectsInfo.ObjectType.Equip: des = GetEquipDes(info); break; } string GetEquipDes(ObjectsInfo.ObjectInfo info) { string str = ""; str += "名称:" + info.name + "\n"; switch (info.dressType) { case ObjectsInfo.DressType.Headgear: str += "类型:头盔" +"\n"; break; case ObjectsInfo.DressType.Armor: str += "类型:护甲" +"\n"; break; case ObjectsInfo.DressType.Left_hand: str += "类型:左手" +"\n"; break; case ObjectsInfo.DressType.Right_hand: str += "类型:右手" +"\n"; break; case ObjectsInfo.DressType.Shoe: str += "类型:鞋" +"\n"; break; case ObjectsInfo.DressType.Accessory: str += "类型:饰品" +"\n"; break; } switch (info.appType) { case ObjectsInfo.ApplicationType.Swordman: str += "适用角色:剑士"+"\n"; break; case ObjectsInfo.ApplicationType.Magician: str += "适用角色:魔法师"+"\n"; break; case ObjectsInfo.ApplicationType.Common; str += "适用角色:通用"+"\n"; break; } str += "增加速度:" + info.attack + "\n"; str += "增加防御:" + info.defense +"\n"; str += "增加速度:" + info.speed +"\n"; str += "出售价:" + info.price_sell + "\n"; str += "购买价:" +info.price_buy + "\n"; return str; }
即可实现,如下图
7.4 实现穿戴功能
接下来实现点击鼠标右键进行穿戴的功能,需要与装备栏进行交互。在InventoryInsideItem之中,当isHover为true时,即鼠标位于物体之上,我们实现穿戴功能。
在Magician下的PlayerStatus脚本中加入一个角色种类,分为魔法师和剑士,并在函数内赋值
public enum playerRole{ Swordman, Magician } public class PlayerStatus : MonoBehaviour { public playerRole role = playerRole.Magician; }
上述代码在穿戴的角色判断时需要用到,不同的角色所穿戴的部分装备是有限制的,我们在装备界面Equipment脚本中实现穿戴DressEquip()功能
7.4.1 无法穿戴的情形
针对当前的角色魔法师,我们首先考虑三种情况是无法穿戴的:
- 当前物品不是装备
- 当前角色不是魔法师
- 当前角色虽然是魔法师,但穿戴类型为剑士
public bool DressEquip(int id) { ObjectsInfo.ObjectInfo info = ObjectsInfo._instance.GetObjectInfoFromDict (id); if (info.type != ObjectsInfo.ObjectType.Equip) //情况1 { return false; } if (ps.role == playerRole.Swordman) //情况2 { return false; } if (ps.role == playerRole.Magician) //情况3 { if(info.appType == ObjectsInfo.ApplicationType.Swordman) { return false; } } }
7.4.2 根据穿戴类型及部位更新显示
之后考虑可以穿戴的情况,首先判断DressEquip(int id)当前id对应物品的穿戴类型,并用一个名为parentOfEquipItem的GameObject存储,之后根据parentOfEquipItem的GameObject访问它的子类以实现装备的更新。
GameObject parentOfEquipItem = null; switch (info.dressType) { case ObjectsInfo.DressType.Headgear: parentOfEquipItem = headgear; break; case ObjectsInfo.DressType.Armor: parentOfEquipItem = armor; break; case ObjectsInfo.DressType.Left_hand: parentOfEquipItem = left_hand; break; case ObjectsInfo.DressType.Right_hand: parentOfEquipItem = right_hand; break; case ObjectsInfo.DressType.Shoe: parentOfEquipItem = shoe; break; case ObjectsInfo.DressType.Accessory: parentOfEquipItem = accessory; break;
之后在Equipment之中创建一个public GameObject equipmentItem;作为parentOfEquipItem的子类,通过equipmentItem更改显示的装备
在之前存为Prefabs的EquipmentItem中新建一个脚本,命名为EquipmentItem,控制装备栏里物品的脱下与更新操作
private UISprite sprite; //控制装备的外观显示 private int id; //得到当前装备的id void Awake() { sprite = this.GetComponent<UISprite> (); } public void SetId(int id) //通过id得到装备的info信息 { this.id = id; ObjectsInfo.ObjectInfo info = ObjectsInfo._instance.GetObjectInfoFromDict (id); SetInfo (info); } public void SetInfo(ObjectsInfo.ObjectInfo info) //直接得到装备的info信息 { this.id = info.id; sprite.spriteName = info.icon_name; }
有了这些功能,我们直接在Equipment中设置物品的显示
EquipmentItem item = parentOfEquipItem.GetComponentsInChildren<EquipmentItem> (); 实例化一个item,控制物品显示 if (item != null) //如果parentOfEquipItem的子类EquipmentItem不为空,即该位置已穿戴装备 { item.SetInfo(info); //调用SetInfo(info)函数更新Sprite的显示 } else //未穿戴装备 { GameObject itemGO = NGUITools.AddChild(parentOfEquipItem,equipmentItem); //新建一个parentOfEquipItem的子类equipmentItem,显示装备 itemGO.transform.localPosition = Vector3.zero; itemGO.GetComponent<EquipmentItem>().SetInfo(info); }
7.4.3右键点击穿戴,更新装备栏
在InventoryInsideItem中,当点击右键时,实现更新装备显示和物品栏信息更新的效果,在Update()中实现
void Update() { if (isHover) { InventoryDes._instance.Show (id); if(Input.GetMouseButtonDown(1)) //点击右键 { bool successDress = Equipment._instance.DressEquip(id); 判断是否成功穿戴 if(successDress) { transform.parent.GetComponent<InventoryItemGrid>().MinusNumber(); //若成功穿戴,更新显示信息 } } } }
其中,Equipment脚本中的DressEquip()函数如下,除去之前三种无法穿戴的情况,当符合穿戴要求时
EquipmentItem item = parentOfEquipItem.GetComponentInChildren<EquipmentItem> (); //实例化item,用以判断当前装备部位是否有装备 if (item != null) //非空,表示已有装备 { Inventory._instance.GetId(item.id); //将当前装备脱下 item.SetInfo(info); //显示鼠标右键穿上的装备 } else //没穿装备的情况 { GameObject itemGO = NGUITools.AddChild(parentOfEquipItem,equipmentItem); //新建Equipment的子类equipmentItem,实例化为itemGO itemGO.transform.localPosition = Vector3.zero; itemGO.GetComponentInChildren<EquipmentItem>().SetInfo(info); //新建信息 }
当装备成功穿戴后,我们还需要对物品栏信息进行更新
通过InventoryItemGrid脚本中的MinusNumber()函数实现,并在上述代码的 if(successDress)中调用。
public void MinusNumber(int number = 1) { if (this.num >= num) //当前网格数量大于0 { this.num -= number; //数量减1 numberLabel.text = this.num.ToString (); //更新显示 } if (this.num == 0) //若当前网格数量为0 { GameObject.Destroy(this.GetComponentInChildren<InventoryInsideItem>().gameObject); //销毁并清空该网格数据 ClearInfo(); } }
完成后显示效果如下图
7.4.4 装备卸下功能
将Prefabs下的EquipmentItem拖回UI root下,为其添加box collider,并设置
box collider的大小为50x50,即与显示框大小一致。
在EquipmentItem脚本中添加如下代码
private bool isHover = false; void OnHover(bool isOver) //OnHover事件,当鼠标放在物体上方时,isOver为true,离开物体,isOver为false { isHover = isOver; } void Update() { if (isHover) //放在装备栏的装备上方 { if(Input.GetMouseButtonDown(1)) //且按下鼠标右键 { Inventory._instance.GetId(id); //将该装备添加到物品栏中,之后销毁 GameObject.Destroy(this.gameObject); } } }
7.5 实现装备穿戴对属性的影响
在Equipment之中定义三个变量,并控制属性的变化。
private int attack = 0; private int defense = 0; private int speed = 0; void UpdateProperty() { this.attack = 0; //每次更新的时候先赋值为0 this.defense = 0; this.speed = 0; EquipmentItem headgearItem = headgear.GetComponentInChildren<EquipmentItem> (); //以头盔部分为例,实例化EquipmentItem>的headgearItem if (headgearItem != null) //如果装备栏中已经有 { ObjectsInfo.ObjectInfo equipInfo = ObjectsInfo._instance.GetObjectInfoFromDict(headgearItem.id); //通过id得到当前物品的信息equipInfo this.attack += equipInfo.attack; //将属性添加到三个定义的变量中 this.defense += equipInfo.defense; this.speed += equipInfo.speed; } }
其余的5个装备的操作类似,因此可以抽象出一个方法PlusProperty(),控制属性的增加
void PlusProperty(EquipmentItem item) { if (item != null) { ObjectsInfo.ObjectInfo equipInfo = ObjectsInfo._instance.GetObjectInfoFromDict(item.id); this.attack += equipInfo.attack; this.defense += equipInfo.defense; this.speed += equipInfo.speed; } }
至此就可以简化为
EquipmentItem headgearItem = headgear.GetComponentInChildren<EquipmentItem> (); PlusProperty (headgearItem);
当装备穿上或者装备卸下的时候,需要调用UpdateProperty()方法,穿上对应InventoryInsideItem中的DressEquip()方法,卸下对应EquipmentItem中的ifHover为正且鼠标右键点击时触发。即
public bool DressEquip(int id) //仅显示添加部分 { UpdateProperty (); return true; } 以及 if (isHover) { if(Input.GetMouseButtonDown(1)) { Inventory._instance.GetId(id); GameObject.Destroy(this.gameObject); Equipment._instance.UpdateProperty(); } }这里先留出了接口,之后再解决装备信息与状态系统的交互功能。