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
最後,來執行遊戲看看吧,可以穿戴裝備了。
留言
張貼留言