5-15 Create Character Panel And Wear Equipment
本章要來製作角色面板以及實作裝備穿戴功能,在背包介面按下滑鼠右鍵可以自動穿戴裝備,且裝備會自動放置到對應裝備類型的Slot。如果角色已經穿上了某個類型的裝備,則會與背包內的裝備交換。
首先實作前先解決一個Bug,我發現裝備的類型沒有正常解析,如下圖。靴子的裝備類型出現頭部,很顯然解析出錯。
請大家將Item.cs的OnAfterDeserialize方法設為virtual方法。
然後將Equipment.cs的OnAfterDeserialize方法設為override方法,這樣才能正確複寫父類別的OnAfterDeserialize。
接著來創建角色面板吧。首先,我們必須先新增一個Script名為EquipmentSlot,這個類別要繼承原本的Slot.cs。
以下提供EquipmentSlot.cs完整程式碼:
接著,請大家複製Knapsack Panel後,製作一個新的Character Panel,如下圖。
排版跟畫面如下圖。
要特別注意的是,每個Slot的Script要改成EquipmentSlot。
並將新的EquipmentSlot拉進Project視窗變成Prefab。
接著,我們在原本的Slot.cs的OnPointerDown方法中,新增按下滑鼠右鍵就能自動穿戴裝備的功能,如下圖。
以下提供新的Slot.cs跟InventorySystem.cs的程式碼:
Slot.cs:
InventorySystem.cs:
最後,來執行遊戲看看吧,可以穿戴裝備了。
首先實作前先解決一個Bug,我發現裝備的類型沒有正常解析,如下圖。靴子的裝備類型出現頭部,很顯然解析出錯。
請大家將Item.cs的OnAfterDeserialize方法設為virtual方法。
然後將Equipment.cs的OnAfterDeserialize方法設為override方法,這樣才能正確複寫父類別的OnAfterDeserialize。
接著來創建角色面板吧。首先,我們必須先新增一個Script名為EquipmentSlot,這個類別要繼承原本的Slot.cs。
在新的EquipmentSlot.cs中,我們會重新實作OnPointerDown方法,如下圖。先確認手上是否有裝備,且手上的裝備類型必須要跟Slot指定的裝備類型相同,才可以放進Slot。如果Slot內已經有裝備了,則會跟手上的裝備進行交換。如果是手上沒有裝備的狀態,則能直接取得Slot內的裝備。
然後,在裝備Slot內按下右鍵,就會自動脫下裝備放到背包中。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace RPG.Inventory{
public class EquipmentSlot : Slot {
[SerializeField] public EquipmentType equipmentType;
public override void OnPointerDown(PointerEventData eventData){
// 使用滑鼠右鍵自動脫下裝備
if (eventData.button == PointerEventData.InputButton.Right
&& invetorySystem.isPickedItem == false && transform.childCount > 0) {
ItemUI clickedItemUI = GetComponentInChildren ();
invetorySystem.StoreItem (clickedItemUI.Item);
Destroy (clickedItemUI.gameObject);
}
if (eventData.button != PointerEventData.InputButton.Left)
return;
if (invetorySystem.isPickedItem == true) {
Item pickedItem = invetorySystem.GetPickedItem ().Item;
// 判斷手上的裝備類型是否與該Slot的裝備類型相同
if (EquipmentTypeIsEqual (pickedItem)) {
if (transform.childCount > 0) {
// 若裝備槽有東西,將手上的物品與裝備槽交換
ItemUI clickedItemUI = transform.GetChild (0).GetComponent ();
invetorySystem.GetPickedItem ().ExchangeItem (clickedItemUI);
} else {
// 若裝備槽沒東西,直接把手上的東西放進去
PutInOneItem();
}
}
} else {
if (transform.childCount > 0) {
// 若裝備槽有東西,將裝備拿到手上
ItemUI clickedItemUI = transform.GetChild (0).GetComponent ();
PickupAllSlotItem(clickedItemUI);
}
}
}
public bool EquipmentTypeIsEqual(Item item){
return item is Equipment && (item as Equipment).TheEquipmentType == equipmentType;
}
}
}
接著,請大家複製Knapsack Panel後,製作一個新的Character Panel,如下圖。
排版跟畫面如下圖。
要特別注意的是,每個Slot的Script要改成EquipmentSlot。
修改完後,請大家記得針對不同的Slot,選擇不同的EquipmentType參數。
並將新的EquipmentSlot拉進Project視窗變成Prefab。
接著,我們在原本的Slot.cs的OnPointerDown方法中,新增按下滑鼠右鍵就能自動穿戴裝備的功能,如下圖。
以下提供新的Slot.cs跟InventorySystem.cs的程式碼:
Slot.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace RPG.Inventory{
public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler {
[SerializeField] GameObject itemPrefab;
protected InventorySystem invetorySystem;
void Start(){
invetorySystem = GetComponentInParent ();
}
public void StoreItem(Item item){
// 若Slot底下沒有Item,則Instantiate一個
if (transform.childCount == 0) {
GameObject itemObject = Instantiate (itemPrefab, transform);
itemObject.GetComponentInChildren ().SetItem (item);
} else {
// 已存在該Item,所以增加數量即可
transform.GetComponentInChildren ().AddAmount ();
}
}
public int GetItemID(){
return transform.GetComponentInChildren ().Item.ID;
}
public bool IsFilled(){
ItemUI itemUI = transform.GetComponentInChildren ();
return itemUI.Amount >= itemUI.Item.Capacity;
}
public void OnPointerEnter(PointerEventData eventData){
if (transform.childCount > 0) {
Item item = GetComponentInChildren ().Item;
invetorySystem.ShowToolTip (item.GetToolTipText ());
}
}
public void OnPointerExit(PointerEventData eventData){
invetorySystem.HideToolTip ();
}
public virtual void OnPointerDown(PointerEventData eventData){
// 使用滑鼠右鍵自動穿戴裝備
if (eventData.button == PointerEventData.InputButton.Right
&& invetorySystem.isPickedItem == false && transform.childCount > 0) {
ItemUI clickedItemUI = GetComponentInChildren ();
invetorySystem.WearingEquipment (clickedItemUI);
}
if (eventData.button != PointerEventData.InputButton.Left)
return;
// Slot內是否已經有物體
if (transform.childCount > 0) {
// 取得目前滑鼠點擊Slot的ItemUI
ItemUI clickedItemUI = GetComponentInChildren ();
// 當滑鼠沒有Item時
if (invetorySystem.isPickedItem == false) {
if (Input.GetKey (KeyCode.Z)) {
// 壓住Z鍵時,只會取出一半的數量
PickupHalfOfSlotItem(clickedItemUI);
} else {
// 取出全部
PickupAllSlotItem (clickedItemUI);
}
// 當滑鼠有Item時
} else if (invetorySystem.isPickedItem == true) {
// 當放入Item的是同一個時
if (clickedItemUI.Item.ID == invetorySystem.GetPickedItem ().Item.ID) {
if (Input.GetKey (KeyCode.Z)) {
// 壓住Z鍵時,只會一個一個的放入物體
PutInOneItem ();
} else {
// 放入所有物體
PutInAllItem (clickedItemUI);
}
} else {
// 當放入的Item不是同一個時,將Slot與PickedItem交換
invetorySystem.GetPickedItem().ExchangeItem(clickedItemUI);
}
}
} else {
if (invetorySystem.isPickedItem == true) {
if (Input.GetKey (KeyCode.Z)) {
// 壓住Z鍵時,只會一個一個的放入物體
PutInOneItem ();
} else {
// 放入所有物體
PutInAllItemToEmptySlot ();
}
}
}
}
private void PickupHalfOfSlotItem(ItemUI clickedItemUI){
int amountToPick = (clickedItemUI.Amount + 1) / 2;
int amountRemained = clickedItemUI.Amount - amountToPick;
// 將PickedItem的ItemUI設置為目前滑鼠點中的ItemUI
invetorySystem.PickupItem (clickedItemUI.Item, amountToPick);
if (amountRemained == 0) {
Destroy (clickedItemUI.gameObject);
} else {
clickedItemUI.SetAmount (amountRemained);
}
}
protected void PickupAllSlotItem(ItemUI clickedItemUI){
// 將PickedItem的ItemUI設置為目前滑鼠點中的ItemUI
invetorySystem.PickupItem (clickedItemUI.Item, clickedItemUI.Amount);
// 因為已經選中物體了,要將Slot內的ItemUI刪除
Destroy (clickedItemUI.gameObject);
}
protected void PutInOneItem(){
this.StoreItem (invetorySystem.GetPickedItem ().Item);
invetorySystem.ReducePickedItem (1);
}
private void PutInAllItem(ItemUI clickedItemUI){
if (clickedItemUI.Item.Capacity > clickedItemUI.Amount) {
int slotRemained = clickedItemUI.Item.Capacity - clickedItemUI.Amount;
if (slotRemained >= invetorySystem.GetPickedItem ().Amount) {
// 若Slot內的空間足以放下滑鼠拿著的全部物體
clickedItemUI.SetAmount (clickedItemUI.Amount + invetorySystem.GetPickedItem ().Amount);
invetorySystem.ReducePickedItem (invetorySystem.GetPickedItem ().Amount);
} else {
// Slot內的空間不夠放,只能放下部分滑鼠拿著的物體
clickedItemUI.SetAmount (clickedItemUI.Amount + slotRemained);
invetorySystem.ReducePickedItem (slotRemained);
}
}
}
private void PutInAllItemToEmptySlot(){
for (int i = 0; i < invetorySystem.GetPickedItem ().Amount; i++) {
this.StoreItem (invetorySystem.GetPickedItem().Item);
}
invetorySystem.ReducePickedItem (invetorySystem.GetPickedItem().Amount);
}
}
}
InventorySystem.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace RPG.Inventory{
public class InventorySystem : MonoBehaviour {
[SerializeField] ToolTip toolTip;
[SerializeField] ItemUI pickedItem;
public bool isPickedItem = false;
ItemList itemList;
Slot[] slotList;
void Start () {
ParseItemsJson ();
slotList = GetComponentsInChildren ();
}
void Update(){
PressGToAddItem ();
MovePickedItemByMousePosition ();
DiscardPickedItem ();
}
public void PickupItem(Item item, int amount){
pickedItem.SetItem (item, amount);
pickedItem.Show ();
toolTip.Hide ();
isPickedItem = true;
}
public void ReducePickedItem(int amount){
pickedItem.ReduceAmount(amount);
if (pickedItem.Amount <= 0) {
isPickedItem = false;
pickedItem.Hide ();
}
}
public void WearingEquipment(ItemUI equipmentToWear){
if ((equipmentToWear.Item is Equipment) == false) {
return;
}
foreach(Slot slot in slotList){
EquipmentSlot equipmentSlot = (slot as EquipmentSlot);
if(equipmentSlot && equipmentSlot.EquipmentTypeIsEqual (equipmentToWear.Item)){
if (equipmentSlot.transform.childCount > 0) {
// 人物已配戴裝備了,將裝備與背包的裝備交換
equipmentSlot.GetComponentInChildren ().ExchangeItem(equipmentToWear);
} else {
// 人物未配戴裝備,直接穿上
equipmentSlot.StoreItem (equipmentToWear.Item);
// 銷毀背包內的裝備
Destroy (equipmentToWear.gameObject);
}
break;
}
}
}
public ItemUI GetPickedItem(){
return pickedItem;
}
public void ShowToolTip(string content){
toolTip.Show (content);
}
public void HideToolTip(){
toolTip.Hide ();
}
public bool StoreItem(int id){
Item item = GetItemByID (id);
return StoreItem (item);
}
public bool StoreItem(Item item){
if (item == null) {
return false;
}
// 若物品的儲存容量為1,則將該物品直接放進空的Slot
if (item.Capacity == 1) {
return StoreItemInEmptySlot (item);
}
// 將相同的Item放在同一個Slot
return StoreItemInSameSlot(item);
}
private void PressGToAddItem(){
// 測試程式碼,手動生成物品
if (Input.GetKeyDown (KeyCode.G)) {
StoreItem (Random.Range(1,17));
}
}
private void MovePickedItemByMousePosition(){
if (isPickedItem) {
// 將滑鼠座標轉換成Canvas上的座標
Vector2 position;
Canvas canvas = GetComponentInParent
最後,來執行遊戲看看吧,可以穿戴裝備了。













留言
張貼留言