5-14 Discard Item And Control Panel Display
今天要來教大家製作『丟棄物品』和『控制介面顯示』的功能,將物品用滑鼠移動到背包外面並點擊滑鼠左鍵,該物品就會被丟棄,以及使用快捷鍵和介面右上角的叉叉按鈕,就可以開關介面。
首先,在InventorySystem.cs的Update中,額外增加下列的程式碼,使用EventSystem的IsPointerOverGameObject方法,可以判斷滑鼠左鍵有沒有點擊到GameObject,若沒有,則將PickedItem隱藏,並將isPickedItem設為false。下次滑鼠點擊背包的其他物體時,PickedItem資訊會被重整,相當於上一個狀態的物體被丟棄。
InventorySystem.cs的完整程式碼如下:
新增一個Script名為Panel.cs,這個腳本要用來控制介面的隱藏和顯示。
撰寫完成後,請在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:
執行遊戲看看吧,我用滑鼠點擊了背包內的能量瓶。
然後朝著血量瓶的位置點擊下去,能量瓶就會放下去,然後滑鼠上抓著血量瓶。
首先,在InventorySystem.cs的Update中,額外增加下列的程式碼,使用EventSystem的IsPointerOverGameObject方法,可以判斷滑鼠左鍵有沒有點擊到GameObject,若沒有,則將PickedItem隱藏,並將isPickedItem設為false。下次滑鼠點擊背包的其他物體時,PickedItem資訊會被重整,相當於上一個狀態的物體被丟棄。
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(){ // 測試程式碼,手動生成物品 if (Input.GetKeyDown (KeyCode.G)) { StoreItem (Random.Range(1,3)); } if (isPickedItem) { // 將滑鼠座標轉換成Canvas上的座標 Vector2 position; Canvas canvas = GetComponentInParent
新增一個Script名為Panel.cs,這個腳本要用來控制介面的隱藏和顯示。
Panel.cs的完整程式碼如下:
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(); } 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 (); } } }
撰寫完成後,請在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:
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().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; GetComponentInParent ().ShowToolTip (item.GetToolTipText ()); } } public void OnPointerExit(PointerEventData eventData){ GetComponentInParent ().HideToolTip (); } public void OnPointerDown(PointerEventData eventData){ InventorySystem invetorySystem = GetComponentInParent (); // Slot內是否已經有物體 if (transform.childCount > 0) { // 取得目前滑鼠點擊Slot的ItemUI ItemUI clickedItemUI = GetComponentInChildren (); // 當滑鼠沒有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); } } }
執行遊戲看看吧,我用滑鼠點擊了背包內的能量瓶。
然後朝著血量瓶的位置點擊下去,能量瓶就會放下去,然後滑鼠上抓著血量瓶。
留言
張貼留言