39.装备面板的制作.
制作装备面的时候,这个装备面板对应的格子只能穿戴对应的装备.
所以这些格子都有Slot的基本属性,但是他在这个属性之外有一些限制条件.
那么就要把这些格子分别给不同的脚本控制他们所能穿戴的装备种类.
首先复制一个KnapsackPannel,命名CharacterPannel.
去掉CanvasGroups组件.需要11个格子存放装备.
留下一个格子,剩下全部删除.将这个格子的Slot脚本删除.
添加一个新的脚本EquipmentSlot,这个脚本继承自Slot.
将这个格子做成Prefab.把脚本的ItemPrefab拖进去.
复制11个出来.并且重命名
因为每一个格子都有自己的属性(穿戴特定的装备).
所以要定义一个变量.
public
class
EquipmentSlot
: Slot
{
public
EquipMent.EquipmentType eqType;
}
这个时候在EquipmentSlot脚本上添加各自的装备类型.
添加了装备类型之后,我们每次得到一个装备的时候先确认是什么类型的装备,
然后再把他放到对应的格子里面.
这里有一个问题,主手武器和副手武器没有区分.
我们不能知道这个武器应该放在什么地方.
MainHand上面没有对应的选项.
这时候要在EquipMent脚本里面添加一个None,因为MainHand不属于任何.
然后把MainHandSlot里面的数EqType属性选择为None.
当来了一个武器的时候,要让CharacterPannel脚本来管理.
在CharacterPannel下删除KnapsackPannel脚本,添加一个空的CharacterPannel脚本.
再CharacterPannel里面得到MainHand和OffHand的对应的格子.
这样的话所有的物品格子都有了自己的类型.
CharacterPannel脚本继承自Inventory,再Inventory中可以得到所有的Slot,
如果想再CharacterPannel中也能得到的话就要让Inventory中的
private
Slot[] slotList;
//这里建立一个数组,用来管理所有slot.
变成
protected
Slot[] slotList;
//这里建立一个数组,用来管理所有slot.
Protected关键字可以被继承的类所调用.
(或者可以给slotList提供一个get{}方法)
40.MainHand/OffHand格子的第二种方法.(建议用这种.)
在EquipmentSlot脚本中添加2个属性.装备类型,武器类型.
public
class
EquipmentSlot
: Slot
{
public
EquipMent.EquipmentType eqType;
public
Weapon.WeaponType wpType;
}
并且在武器类型中添加一个空None
这时候就能在给MainHandSlot赋值
在OffHand这地方不仅能放副手武器,也能放装备(根据策划的需求决定)
如果这2个地方都是None,那么什么都不能放.
这种方法不需要CharacterPannel用Start()方法获取对应的格子.
41.分析装备的穿戴情况.
42.穿戴装备.
这个功能要在EquipmentSlot里面做.
要得到鼠标的位置就要重写
OnPointerDown,这个方法在父类里面有,在这里重写.
此外,以前做过PickedItem物品全部移除,但是没有做过格子上面的物品全部移除.
所以这里要在ItemUI里面增加一个方法:
public
void
RemoveAllItem()
//移除全部Item
{
Destroy(gameObject);
}
EquipmentSlot代码如下:
public
class
EquipmentSlot
: Slot
{
public
EquipMent.EquipmentType eqType;
public
Weapon.WeaponType wpType;
public
override
void
OnPointerDown(PointerEventData eventData)
{
if
(InventoryManager.Instance.IsPickedItem)
//鼠标上有物体
{
ItemUI pickedItem = InventoryManager.Instance.PickedItem;
if
(
pickedItem.Item
is
EquipMent
)
//如果鼠标上的装备类型是
EquipMent
{
if
(
((EquipMent) pickedItem.Item).EquipType == transform.GetComponent<EquipmentSlot>().eqType
)
//鼠标上的物体和对应的格子的类型一致
{
if
(transform.childCount > 0)
//格子上有物体
{
//交换鼠标和格子上的物体
Item tempItem = transform.GetChild(0).GetComponent<ItemUI>().Item;
int
tempAmount = transform.GetChild(0).GetComponent<ItemUI>().Amount;
transform.GetChild(0).GetComponent<ItemUI>().SetItem(pickedItem.Item, pickedItem.Amount);
pickedItem.SetItem(tempItem, tempAmount);
}
else
//格子上没有物体
{
//把鼠标的物体放在格子上
StoreItem(InventoryManager.Instance.PickedItem.Item);
transform.GetChild(0).GetComponent<ItemUI>().SetAmount(pickedItem.Amount);
InventoryManager.Instance.RemoveAllItem();
}
}
else
//鼠标上的物体和格子上面的不一致
{
return
;
}
}
else
if
(pickedItem.Item
is
Weapon)
//如果手上是
Weapon
{
if
(((Weapon) pickedItem.Item).WpType == transform.GetComponent<EquipmentSlot>().wpType)
//鼠标上的物体和对应的格子的类型一致
{
if
(transform.childCount > 0)
//格子上有物体
{
//交换鼠标和格子上的物体
Item tempItem = transform.GetChild(0).GetComponent<ItemUI>().Item;
int
tempAmount = transform.GetChild(0).GetComponent<ItemUI>().Amount;
transform.GetChild(0).GetComponent<ItemUI>().SetItem(pickedItem.Item, pickedItem.Amount);
pickedItem.SetItem(tempItem, tempAmount);
}
else
//格子上没有物体
{
//把鼠标的物体放在格子上
StoreItem(InventoryManager.Instance.PickedItem.Item);
transform.GetChild(0).GetComponent<ItemUI>().SetAmount(pickedItem.Amount);
InventoryManager.Instance.RemoveAllItem();
}
}
else
//鼠标上的物体和格子上面的不一致
{
return
;
}
}
}
else
//鼠标上没有物体
{
if
(transform.childCount > 0)
//格子上有物体
{
ItemUI currentItem = transform.GetChild(0).GetComponent<ItemUI>();
InventoryManager.Instance.PickupItem(currentItem.Item, currentItem.Amount);
transform.GetChild(0).GetComponent<ItemUI>().RemoveAllItem();
}
else
//格子上没有物体
{
return
;
}
}
}
}
上面这段代码还能进行优化,在这里就不进行优化了,不然很难看懂.
43.处理鼠标的右击装备事件
我们以前做好的功能,不管鼠标左中右都能控制捡起放下物品.
我们现在要处理右键穿戴,左键捡起.这时候就要用到另外一个事件了.
我们要在Slot和EquipmentSlot的鼠标点击事件加上下面的代码
if
(eventData.button!=PointerEventData.InputButton.Left)
//当鼠标点击不是左键的时候,直接返回
{
return
;
}
此外,我们穿戴装备应该用CharacterPannel来控制,
那么就应该在Character里面做一个单例模式,方便以后调用.
#region
单例模式
private
static
CharacterPannel _instance;
public
static
CharacterPannel Instance
{
get
{
if
(_instance ==
null
)
{
_instance = GameObject.Find(
"CharacterPannel"
).transform.GetComponent<CharacterPannel>();
}
return
_instance;
}
}
#endregion
然后再里面添加一个穿戴装备的方法
public
void
PutOn(Item item)
{
}
在Slot里面做点击右键的功能.
if
(eventData.button == PointerEventData.InputButton.Right)
//当鼠标点击右键的时候.
{
if
(transform.childCount > 0)
{
ItemUI currentItemUI = transform.GetChild(0).GetComponent<ItemUI>();
if
(currentItemUI.Item
is
EquipMent || currentItemUI.Item
is
Weapon)
//当前物品槽是Equipment或者是Weapon
{
//穿戴装备.销毁自身Item
ItemUI currentItemUi = transform.GetChild(0).GetComponent<ItemUI>();
CharacterPannel.Instance.PutOn(currentItemUi.Item);
//调用CharacterPannel里面的PutOn方法穿戴
currentItemUi.RemoveAllItem();
///
这里不用判断目标装备槽是否有装备.因为这个是Slot脚本.
///
这个要放在装备槽那边判断比较合适.
}
}
}
在EquipmentSlot里面做一个判断,判断当前的格子类型和物品类型是否一致
下面粗体字部分我自己写不出来,要好好的看一下.
public
bool
IsRightItem(Item item)
{
if
((item
is
EquipMent && ((EquipMent)(item)).EquipType ==
this
.eqType) ||
(item
is
Weapon && ((Weapon)(item)).WpType ==
this
.wpType))
//这里不用惯性思维 这里不需要GetChild(0) 直接得到这个物体上的脚本就好了
{
return
true
;
}
return
false
;
}
在CharacterPannel里面做PutOn功能
因为要用CharacterPannel管理所有的装备槽.
public
void
PutOn(Item item)
//右击穿戴装备
{
Item exitItem =
null
;
//如果装备槽里面有装备 就把他临时存放在这里
foreach
(Slot slot
in
slotList)
{
EquipmentSlot equipmentSlot = (EquipmentSlot) slot;
//把格子强转成装备槽
if
(equipmentSlot.IsRightItem(item))
//如果当前的装备和物品槽上的类型一样.
{
if
(equipmentSlot.transform.childCount > 0)
//当前装备槽有装备
{
//选中的装备放进装备槽,原装备槽的装备暂存于exitItem然后放置在背包里.
exitItem = equipmentSlot.transform.GetChild(0).GetComponent<ItemUI>().Item;
Knapsack.Instance.StoreItem(exitItem);
equipmentSlot.transform.GetChild(0).GetComponent<ItemUI>().SetItem(item,1);
}
else
//当前装备槽没有装备
{
equipmentSlot.StoreItem(item);
}
break
;
}
}
}
现在已经可以完成右键穿戴/替换装备了.
但是还有一个问题.
当我们替换一个装备的时候,他不会在第一个物品槽里面显示,而是出现在后面的物品槽中.
这时候我点击右键 应该是他们换位置
但是手套却不能放在第一个格子中,而是找到了后面的格子放置.
修改这个问题要修改2个地方.
1 把Slot里面的Destroy()方法改程DestroyImmediate()方法(立即销毁方法)
DestroyImmediate(currentItem.gameObject);
2 Slot里面穿戴装备的时候的函数也要修改
if
(currentItemUI.Item
is
EquipMent || currentItemUI.Item
is
Weapon)
//当前物品槽是Equipment或者是Weapon
{
//穿戴装备.销毁自身Item
//这里要先销毁自身物体,然后再把这个物体赋值到装备槽
//这里定义一个临时变量来存储要被销毁的物体,然后再销毁,然后再把这个临时存放的物体放在装备槽里面
It
em currentItem = currentItemUI.Item;
currentItemUI.RemoveAllItem();
CharacterPannel.Instance.PutOn(currentItem);
//调用CharacterPannel里面的PutOn方法穿戴
///
这里不用判断目标装备槽是否有装备.因为这个是Slot脚本.
///
这个要放在装备槽那边判断比较合适.
}
44.修改2个小bug.
1.当选中一个物体的时候,在别的格子上点击右键 还是能穿戴该装备.
判断鼠标点击右键的时候 加一个判断条件:
鼠标上没有物体.
if
(
InventoryManager.Instance.IsPickedItem==
false
&&eventData.button == PointerEventData.InputButton.Right)
//当鼠标点击右键的时候.
2.穿戴上装备之后,ToolTip还是显示.
在创带装备之后,调用yincangToolTip的代码.Slot脚本里面
if
(currentItemUI.Item
is
EquipMent || currentItemUI.Item
is
Weapon)
//当前物品槽是Equipment或者是Weapon
{
//穿戴装备.销毁自身Item
//这里要先销毁自身物体,然后再把这个物体赋值到装备槽
//这里定义一个临时变量来存储要被销毁的物体,然后再销毁,然后再把这个临时存放的物体放在装备槽里面
Item currentItem = currentItemUI.Item;
currentItemUI.RemoveAllItem();
CharacterPannel.Instance.PutOn(currentItem);
//调用CharacterPannel里面的PutOn方法穿戴
///
这里不用判断目标装备槽是否有装备.因为这个是Slot脚本.
///
这个要放在装备槽那边判断比较合适.
InventoryManager.Instance.HideToolTip();
}
45.增加装备面板的隐藏功能,并显示出按键功能.
在CharacterPannel脚本中.
override
public
void
Start
()
{
base
.Start();
canvasGroup = transform.GetComponent<CanvasGroup>();
}
public
void
DisplaySwitch()
{
if
(canvasGroup.alpha==1)
{
canvasGroup.alpha = Mathf.Lerp(0, 1, Time.deltaTime * smoothing);
if
(canvasGroup.alpha<0.02f)
{
canvasGroup.alpha = 0;
}
}
else
if
(canvasGroup.alpha == 0)
{
canvasGroup.alpha = Mathf.Lerp(1, 0, Time.deltaTime * smoothing);
if
(canvasGroup.alpha > 0.98f)
{
canvasGroup.alpha = 1;
}
}
}
在Unity的Canvas里面增加Text.
46.显示Player的属性.
在CharacterPannel下创建一个属性面板.
因为这个功能用CharacterPannel来管理比较方便 所以放在他下面也好管理.
一个角色一般都有自己的属性.
这个属性做在Player中.
private
int
strength = 10, intellect = 10, agility = 10, stamina = 10, damage = 10;
public
int
Strength{
get
{
return
strength; }}
public
int
Intellect {
get
{
return
intellect; }}
public
int
Agility {
get
{
return
agility; }}
public
int
Stamina {
get
{
return
stamina; }}
public
int
Damage {
get
{
return
damage; }}
这样就能在外界访问到这些属性了.
在CharacterPannel中管理这个功能.
首先要得到这个文本组件.
private
Text propertyText;
//属性的文字
在Start()方法中
// propertyText = GameObject.Find("PropertyPannel").GetComponentInChildren<Text>();//小毛想到的...棒棒的
propertyText = GameObject.Find(
"PropertyPannel/Text"
).GetComponent<Text>();
//这个直接得到2级子物体,要记得
这个不是直接得到子物体,而是得到子物体的子物体.
public
void
UpdatePropertyText()
//跟新显示的属性文字
{
int
strength = 0, intellect=0, agility = 0, stamina = 0, damage = 0;
//定义一个初始的属性值
foreach
(EquipmentSlot slot
in
slotList)
//遍历所有的装备格子
{
if
(slot.transform.childCount>0)
// 如果一个格子有装备
{
ItemUI currentItemUI = slot.transform.GetChild(0).GetComponent<ItemUI>();
//得到当前格子的ItemUI
if
( currentItemUI.Item
is
EquipMent)
//只有EquipMent类型的装备才会有Strength等,所以这里一定要先判断类型
{
//要把当前的类型转化为对应的装备或武器类型
//不然Item里面没有对应的属性.然而Equipment/Weapon会重写父类里面的方法,让他有了对应的属性
strength += ((EquipMent)(currentItemUI.Item)).Stregth;
intellect += ((EquipMent)(currentItemUI.Item)).Intellect;
agility += ((EquipMent)(currentItemUI.Item)).Agility;
stamina += ((EquipMent)(currentItemUI.Item)).Stamina;
}
else
if
(currentItemUI.Item
is
Weapon)
//这里和上面一样也是要先判断类型.
{
damage += ((Weapon) (currentItemUI.Item)).Damage;
}
}
}
strength += player.Strength;
intellect += player.Intellect;
agility += player.Agility;
stamina += player.Stamina;
damage += player.Damage;
//上面的代码是让自身的属性和武器装备属性相加
string
text =
string
.Format(
"力量:
{0}
\n
智力:
{1}
\n
敏捷:
{2}
\n
体力:
{3}
\n
伤害:
{4}
"
,
strength, intellect, agility, stamina, damage);
//最后将得到的文本赋值给属性面板文字
propertyText.text = text;
}
在
加这个方法之后,要把这个方法在每一次装备有变化的时候调用.
1 鼠标左键穿戴/卸下装备时
2 鼠标右键穿戴/卸下装备时
这里省略这些代码,找到合适的地方放进去这个UpdatePropertyText()方法就行.
47.创建小贩面板
在Unity面板中复制一个小贩出来.
去掉原来的脚本,新增一个空脚本Ventor
并将脚本分一下类.
当前的工程脚本结构:
小贩的格子不需要和鼠标交互,所以里面的Slot要重新做Prefab.
只留下原来的1个Slot.
编辑VendorSlot脚本.继承自Slot脚本.
public
class
VendorSlot
: Slot {
//重写父类的鼠标按下方法,按下鼠标不做任何处理.
public
override
void
OnPointerDown(PointerEventData eventData)
{
}
}
这时候vendor面板就不能放入任何物品了.
47.初始化商店.
在Vendor脚本里面做.
public
class
Vendor
: Inventory
{
///
列一个数组出来,然后可以在这个数组里面添加数字,这个数字就时物品的id,
///
如果以后想改变商店里面的物品,直接修改数组里面的数字就好了
public
int
[] arrayList;
public
override
void
Start
()
//重写Start方法.
{
base
.Start();
InitShop();
}
public
void
InitShop()
//初始化商店
{
foreach
(
int
temp
in
arrayList)
//数组有几个对象 就进行几次存储.
{
StoreItem(temp);
//每次存储都调用StoreItem方法,这个方法会自己找到一个格子然后放进去物品.
//这里放进去的就是物品的ID.也就是在Unity面板上数组里面的数字.以后修改也会很方便.
}
}
}
这里有一个大小为8的数组.每一个对象对应一个商店的格子.里面的数字对应商品的ID.
这里有可能会报空指针.这是因为可能还没有解析Json就开始了调用数组,
所以我们要把解析Json放在程序的最开始Awake()方法中.
在InventoryManager里面
void
Awake
()
{
ParseItemJson();
//开始解析Json
}
48.添加玩家的金币属性.
在Unity面板添加金币属性.
然后再Player脚本中添加金币属性.赚取/消耗金币方法.
private
Text text;
private
int
coinAmount;
void
Start
()
{
text = GameObject.Find(
"Coin"
).GetComponentInChildren<Text>();
//得到Text组件.
text.text = coinAmount.ToString();
//跟新当前金币显示
}
xxxxxx
xxxxxx
public
bool
Consume(
int
amount)
//消耗金币
{
if
(coinAmount-amount>0)
{
coinAmount -= amount;
text.text = coinAmount.ToString();
return
true
;
}
return
false
;
}
public
void
Earn(
int
amount)
//赚取金币
{
coinAmount += amount;
text.text = coinAmount.ToString();
}
49.在VendorSlot里面做按下右键购买装备的方法
public
override
void
OnPointerDown(UnityEngine.EventSystems.PointerEventData eventData)
{
if
(eventData.button == PointerEventData.InputButton.Right)
//当按下右键的时候
{
//Debug.Log("在格子里按下右键");
if
(transform.childCount>0)
{
ItemUI currentItemUI = transform.GetChild(0).GetComponent<ItemUI>();
transform.parent.parent.SendMessage(
"BuyItem"
,currentItemUI.Item);
}
}
}
这里需要得到Player金币的属性,如果在每一个VendorSlot里面都要用GameObject.Find()方法查找就会很浪费性能,把所以把这个功能放在Vendor里面.只要查找一次就可以了.
Vendor脚本:
首先要得到Player的属性.
private
Player player;
public
override
void
Start
()
//重写Start方法.
{
base
.Start();
InitShop();
player = GameObject.FindGameObjectWithTag(
"Player"
).GetComponent<Player>();
}
public
void
BuyItem(Item item)
{
//Debug.Log("BuyItem已经调用");
bool
isSucceed = player.Consume(item.BuyPrice);
if
(isSucceed)
{
Knapsack.Instance.StoreItem(item);
}
}
50.出售装备.
在VendorSlot点击事件下:
if
(eventData.button==PointerEventData.InputButton.Left&&InventoryManager.Instance.IsPickedItem)
{
InventoryManager.Instance.RemoveOneItem() ;
Item pickedItem = InventoryManager.Instance.PickedItem.Item;
transform.parent.parent.SendMessage(
"SellItem"
, pickedItem);
}
在Vendor中脚本中:
public
void
SellItem(Item item)
{
player.Earn(item.SellPrice);
}