Related Posts Plugin for WordPress, Blogger...

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中,我們會重新實作OnPointerDown方法,如下圖。先確認手上是否有裝備,且手上的裝備類型必須要跟Slot指定的裝備類型相同,才可以放進Slot。如果Slot內已經有裝備了,則會跟手上的裝備進行交換。如果是手上沒有裝備的狀態,則能直接取得Slot內的裝備。

然後,在裝備Slot內按下右鍵,就會自動脫下裝備放到背包中。

以下提供EquipmentSlot.cs完整程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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<itemui> ();
    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<itemui> ();
      invetorySystem.GetPickedItem ().ExchangeItem (clickedItemUI);
     } else {
      // 若裝備槽沒東西,直接把手上的東西放進去
      PutInOneItem();
     }
    }
   } else {
    if (transform.childCount > 0) {
     // 若裝備槽有東西,將裝備拿到手上
     ItemUI clickedItemUI = transform.GetChild (0).GetComponent<itemui> ();
     PickupAllSlotItem(clickedItemUI);
    }
   }
  }
 
  public bool EquipmentTypeIsEqual(Item item){
   return item is Equipment && (item as Equipment).TheEquipmentType == equipmentType;
  }
 }
}
</itemui></itemui></itemui>


接著,請大家複製Knapsack Panel後,製作一個新的Character Panel,如下圖。

排版跟畫面如下圖。

要特別注意的是,每個Slot的Script要改成EquipmentSlot。

修改完後,請大家記得針對不同的Slot,選擇不同的EquipmentType參數。

並將新的EquipmentSlot拉進Project視窗變成Prefab。

 接著,我們在原本的Slot.cs的OnPointerDown方法中,新增按下滑鼠右鍵就能自動穿戴裝備的功能,如下圖。


以下提供新的Slot.cs跟InventorySystem.cs的程式碼:

Slot.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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<inventorysystem> ();
  }
 
  public void StoreItem(Item item){
   // 若Slot底下沒有Item,則Instantiate一個
   if (transform.childCount == 0) {
    GameObject itemObject = Instantiate (itemPrefab, transform);
    itemObject.GetComponentInChildren<itemui> ().SetItem (item);
   } else {
    // 已存在該Item,所以增加數量即可
    transform.GetComponentInChildren<itemui> ().AddAmount ();
   }
  }
 
  public int GetItemID(){
   return transform.GetComponentInChildren<itemui> ().Item.ID;
  }
 
  public bool IsFilled(){
   ItemUI itemUI = transform.GetComponentInChildren<itemui> ();
   return itemUI.Amount >= itemUI.Item.Capacity;
  }
 
  public void OnPointerEnter(PointerEventData eventData){
   if (transform.childCount > 0) {
    Item item = GetComponentInChildren<itemui> ().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<itemui> ();
    invetorySystem.WearingEquipment (clickedItemUI);
   }
 
   if (eventData.button != PointerEventData.InputButton.Left)
    return;
 
   // Slot內是否已經有物體
   if (transform.childCount > 0) {
    // 取得目前滑鼠點擊Slot的ItemUI
    ItemUI clickedItemUI = GetComponentInChildren<itemui> ();
    // 當滑鼠沒有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);
  }
 }
}
</itemui></itemui></itemui></itemui></itemui></itemui></itemui></inventorysystem>

InventorySystem.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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<slot> ();
  }
 
  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<itemui> ().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<canvas> ();
    RectTransformUtility.ScreenPointToLocalPointInRectangle (
     canvas.transform as RectTransform,
     Input.mousePosition,
     null,
     out position);
    pickedItem.SetLocalPosition (position);
   }
  }
 
  private void DiscardPickedItem(){
   // 處理物品丟棄
   // IsPointerOverGameObject(-1)判斷滑鼠左鍵是否有碰到GameObject
   if (pickedItem && Input.GetMouseButtonDown (0)
    && EventSystem.current.IsPointerOverGameObject(-1) == false) {
    isPickedItem = false;
    pickedItem.Hide ();
   }
  }
 
  private bool StoreItemInSameSlot(Item item){
   foreach (Slot slot in slotList) {
    if (slot.transform.childCount >= 1 && slot.GetItemID () == item.ID &&
     slot.IsFilled () == false) {
     // 將新Item與同一個Item放在一起
     slot.StoreItem (item);
     return true;
    }
   }
   // 若背包內不存在相同的Item,則放進空的Slot
   return StoreItemInEmptySlot (item);
  }
 
  private bool StoreItemInEmptySlot(Item item){
   foreach (Slot slot in slotList) {
    if (slot.transform.childCount == 0) {
     // 將Item存進該Slot
     slot.StoreItem (item);
     return true;
    }
   }
   Debug.LogError ("No Empty Slot.");
   return false;
  }
 
  private Item GetItemByID(int id){
   foreach (Item entity in itemList.ConsumableEntityList) {
    if (entity.ID == id) {
     return entity;
    }
   }
   foreach (Item entity in itemList.EquipmentEntityList) {
    if (entity.ID == id) {
     return entity;
    }
   }
   foreach (Item entity in itemList.MaterialEntityList) {
    if (entity.ID == id) {
     return entity;
    }
   }
   return null;
  }
 
  private void ParseItemsJson(){
   // 從Resource資料夾中讀取Items.json
   TextAsset json = Resources.Load<textasset> ("Items");
   // 解析Json格式
   itemList = JsonUtility.FromJson <itemlist>(json.text);
  }
 }
}
</itemlist></textasset></canvas></itemui></slot>

最後,來執行遊戲看看吧,可以穿戴裝備了。





留言