Related Posts Plugin for WordPress, Blogger...

5-14 Discard Item And Control Panel Display

今天要來教大家製作『丟棄物品』和『控制介面顯示』的功能,將物品用滑鼠移動到背包外面並點擊滑鼠左鍵,該物品就會被丟棄,以及使用快捷鍵和介面右上角的叉叉按鈕,就可以開關介面。

首先,在InventorySystem.cs的Update中,額外增加下列的程式碼,使用EventSystem的IsPointerOverGameObject方法,可以判斷滑鼠左鍵有沒有點擊到GameObject,若沒有,則將PickedItem隱藏,並將isPickedItem設為false。下次滑鼠點擊背包的其他物體時,PickedItem資訊會被重整,相當於上一個狀態的物體被丟棄。

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
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(){
   // 測試程式碼,手動生成物品
   if (Input.GetKeyDown (KeyCode.G)) {
    StoreItem (Random.Range(1,3));
   }
 
   if (isPickedItem) {
    // 將滑鼠座標轉換成Canvas上的座標
    Vector2 position;
    Canvas canvas = GetComponentInParent<canvas> ();
    RectTransformUtility.ScreenPointToLocalPointInRectangle (
     canvas.transform as RectTransform,
     Input.mousePosition,
     null,
     out position);
    pickedItem.SetLocalPosition (position);
   }
 
   // 處理物品丟棄
   // IsPointerOverGameObject(-1)判斷滑鼠左鍵是否有碰到GameObject
   if (pickedItem && Input.GetMouseButtonDown (0)
    && EventSystem.current.IsPointerOverGameObject(-1) == false) {
    isPickedItem = false;
    pickedItem.Hide ();
   }
  }
 
  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 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 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);
   // 使用foreach迴圈讀取List中的Item資料
   // TODO 讀取Json有Bug
  }
 }
}
</itemlist></textasset></canvas></slot>

新增一個Script名為Panel.cs,這個腳本要用來控制介面的隱藏和顯示。

Panel.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;
 
public class Panel : MonoBehaviour {
 
 [SerializeField] float panelAlpha = 1;
 [SerializeField] float smoothAlphaMultiplier = 4;
 [SerializeField] KeyCode hotKey;
 
 CanvasGroup canvasGroup;
 
 void Start () {
  canvasGroup = GetComponent<canvasgroup> ();
 }
  
 void Update () {
  if (Input.GetKeyDown (hotKey)) {
   DisplaySwitch ();
  }
 
  if (canvasGroup.alpha != panelAlpha) {
   // 用Lerp函數控制Alpha的漸變
   canvasGroup.alpha = Mathf.Lerp (canvasGroup.alpha, panelAlpha, Time.deltaTime * smoothAlphaMultiplier);
   if (Mathf.Abs (canvasGroup.alpha - panelAlpha) < 0.01f) {
    canvasGroup.alpha = panelAlpha;
   }
  }
 }
 
 public void Show(){
  canvasGroup.blocksRaycasts = true;
  panelAlpha = 1;
 }
 
 public void Hide(){
  canvasGroup.blocksRaycasts = false;
  panelAlpha = 0;
 }
 
 public void DisplaySwitch(){
  if (panelAlpha == 1) {
   Hide ();
  } else {
   Show ();
  }
 }
}
 
</canvasgroup>


撰寫完成後,請在Chest Panel的Inspector視窗新增Canvas Group組件。

並新增Panel.cs,Hot Key的部分請大家選擇自己喜歡的快捷鍵,此處我選擇 C。

同理,在Knapsack Panel中也要新增Canvas Group組件和Panel.cs,此處的HotKey我選擇K。

接著,選擇Knapsack Panel底下的Close Btn。

在Inspector視窗中應該能看見之前新增的Button組件,在On Click()的地方應會顯示List is Empty,請大家按下『+』按鈕。

然後將Knapsack Panel拖曳到小框框中,如下圖。接著在No Function的地方選擇Panel/Hide()。

同理,在Chest Panel底下的Close Btn也要新增事件,只不過請注意這邊要拖曳的是Chest Panel,No Function的部分也選擇Panel/Hide()。

然後,執行遊戲看看,按下右上角的叉叉按鈕就可以關閉介面了。

按下快捷鍵也同樣可以關閉介面,再按一次快捷鍵則會打開介面。

接著調整一個小問題,我們在之前的文章中,沒有處理『當滑鼠抓著的物體與Slot中的物體不是同一個時,要將抓著的物體與Slot的交換』。所以,我們在Slot.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
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;
 
  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;
    GetComponentInParent<inventorysystem> ().ShowToolTip (item.GetToolTipText ());
   }
  }
 
  public void OnPointerExit(PointerEventData eventData){
   GetComponentInParent<inventorysystem> ().HideToolTip ();
  }
 
  public void OnPointerDown(PointerEventData eventData){
   InventorySystem invetorySystem = GetComponentInParent<inventorysystem> ();
   // Slot內是否已經有物體
   if (transform.childCount > 0) {
    // 取得目前滑鼠點擊Slot的ItemUI
    ItemUI clickedItemUI = GetComponentInChildren<itemui> ();
    // 當滑鼠沒有Item時
    if (invetorySystem.isPickedItem == false) {
     if (Input.GetKey (KeyCode.Z)) {
      // 壓住Z鍵時,只會取出一半的數量
      PickupHalfOfSlotItem(clickedItemUI, invetorySystem);
     } else {
      // 取出全部
      PickupAllSlotItem (clickedItemUI, invetorySystem);
     }
    // 當滑鼠有Item時
    } else if (invetorySystem.isPickedItem == true) {
     // 當放入Item的是同一個時
     if (clickedItemUI.Item.ID == invetorySystem.GetPickedItem ().Item.ID) {
      if (Input.GetKey (KeyCode.Z)) {
       // 壓住Z鍵時,只會一個一個的放入物體
       PutInOneItem (invetorySystem);
      } else {
       // 放入所有物體
       PutInAllItem (clickedItemUI, invetorySystem);
      }
     } else {
      // 當放入的Item不是同一個時,將Slot與PickedItem交換
      ChangeSlotAndPickedItem(clickedItemUI, invetorySystem);
     }
    }
   } else {
    if (invetorySystem.isPickedItem == true) {
     if (Input.GetKey (KeyCode.Z)) {
      // 壓住Z鍵時,只會一個一個的放入物體
      PutInOneItem (invetorySystem);
     } else {
      // 放入所有物體
      PutInAllItemToEmptySlot (invetorySystem);
     }
    }
   }
  }
 
  private void PickupHalfOfSlotItem(ItemUI clickedItemUI, InventorySystem invetorySystem){
   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);
   }
  }
 
  private void PickupAllSlotItem(ItemUI clickedItemUI, InventorySystem invetorySystem){
   // 將PickedItem的ItemUI設置為目前滑鼠點中的ItemUI
   invetorySystem.PickupItem (clickedItemUI.Item, clickedItemUI.Amount);
   // 因為已經選中物體了,要將Slot內的ItemUI刪除
   Destroy (clickedItemUI.gameObject);
  }
 
  private void PutInOneItem(InventorySystem invetorySystem){
   this.StoreItem (invetorySystem.GetPickedItem ().Item);
   invetorySystem.ReducePickedItem (1);
  }
 
  private void PutInAllItem(ItemUI clickedItemUI, InventorySystem invetorySystem){
   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(InventorySystem invetorySystem){
   for (int i = 0; i < invetorySystem.GetPickedItem ().Amount; i++) {
    this.StoreItem (invetorySystem.GetPickedItem().Item);
   }
   invetorySystem.ReducePickedItem (invetorySystem.GetPickedItem().Amount);
  }
 
  private void ChangeSlotAndPickedItem(ItemUI clickedItemUI, InventorySystem invetorySystem){
   Item item = clickedItemUI.Item;
   int amount = clickedItemUI.Amount;
   clickedItemUI.SetItem(invetorySystem.GetPickedItem().Item, invetorySystem.GetPickedItem().Amount);
   invetorySystem.GetPickedItem ().SetItem (item, amount);
  }
 }
}
</itemui></inventorysystem></inventorysystem></inventorysystem></itemui></itemui></itemui></itemui></itemui>

執行遊戲看看吧,我用滑鼠點擊了背包內的能量瓶。

然後朝著血量瓶的位置點擊下去,能量瓶就會放下去,然後滑鼠上抓著血量瓶。

留言