5-24 Automatically Put On Equipment
本章要來製作玩家碰到Weapon Pickup Point之後,不但要自動穿上裝備,背包系統內的裝備欄也必須對應到新裝備,而舊裝備必須自動放回背包內。如果背包已經滿了,則不能撿起裝備。
上一章已經教大家製作自動穿上Weapon Pickup Point的裝備的方法了。
5-23 Throw Out Weapon Pickup Point When Enemy Died
https://rpgcorecombat.blogspot.tw/2018/03/5-23-throw-out-weapon-pickup-point-when.html
首先修改Slot.cs內的StoreItem方法,多增加一個回傳值ItemUI,後面會用到。
接著在InventorySystem.cs中再新增一個方法WearingEquipment,透過指定ID讓玩家穿上裝備。先用GetItemByID取得Equipment物件,接著從EquipmentSlotList中找出符合EquipmentType的Slot。比方說這個Equipment的類型為MainHand,則找出專門放置MainHand的EquipmentSlot。
若EquipmentSlot的ChildCound大於0,代表裝備欄內已經有裝備了,呼叫TakeOffAndWearNewEquipment方法。若裝備欄沒有裝備,呼叫StoreItem方法便能直接放置裝備。
接著說明TakeOffAndWearNewEquipment方法,先將新的裝備放到knapsackSlotList,如果放不進去代表背包滿了,回傳false。如未滿,則呼叫裝備欄的ExchangeItem方法與背包內的新裝備進行交換。
一開始不是有修改Slot.cs的StoreItem方法嗎?讓他回傳ItemUI,這是因為這邊的ExchangeItem方法必須傳入ItemUI的關係,所以必須要取得放在背包內的新裝備的ItemUI。
然後,我們就可以在WeaponPickPoint.cs中使用InventorySystem的WearingEquipment方法,指定WeaponConfig的ID,玩家的裝備欄便能自動放置裝備了。
以下提供本次修改的完整程式碼:
Slot.cs:
InventorySystem.cs:
WeaponPickupPoint.cs:
接著來測試遊戲看看吧,現在玩家裝備的是一把矛。
然後我把敵人殺掉之後,他掉落了一把劍,走過去撿起來吧。
撿起來以後,玩家會自動裝備這把劍,而且原本的矛也會放到背包中。
上一章已經教大家製作自動穿上Weapon Pickup Point的裝備的方法了。
5-23 Throw Out Weapon Pickup Point When Enemy Died
https://rpgcorecombat.blogspot.tw/2018/03/5-23-throw-out-weapon-pickup-point-when.html
首先修改Slot.cs內的StoreItem方法,多增加一個回傳值ItemUI,後面會用到。
接著在InventorySystem.cs中再新增一個方法WearingEquipment,透過指定ID讓玩家穿上裝備。先用GetItemByID取得Equipment物件,接著從EquipmentSlotList中找出符合EquipmentType的Slot。比方說這個Equipment的類型為MainHand,則找出專門放置MainHand的EquipmentSlot。
若EquipmentSlot的ChildCound大於0,代表裝備欄內已經有裝備了,呼叫TakeOffAndWearNewEquipment方法。若裝備欄沒有裝備,呼叫StoreItem方法便能直接放置裝備。
接著說明TakeOffAndWearNewEquipment方法,先將新的裝備放到knapsackSlotList,如果放不進去代表背包滿了,回傳false。如未滿,則呼叫裝備欄的ExchangeItem方法與背包內的新裝備進行交換。
一開始不是有修改Slot.cs的StoreItem方法嗎?讓他回傳ItemUI,這是因為這邊的ExchangeItem方法必須傳入ItemUI的關係,所以必須要取得放在背包內的新裝備的ItemUI。
然後,我們就可以在WeaponPickPoint.cs中使用InventorySystem的WearingEquipment方法,指定WeaponConfig的ID,玩家的裝備欄便能自動放置裝備了。
以下提供本次修改的完整程式碼:
Slot.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; namespace RPG.Inventory{ public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler { [SerializeField] GameObject itemUIPrefab; protected InventorySystem inventorySystem; void Start(){ inventorySystem = GetComponentInParent(); } public ItemUI StoreItem(Item item){ // 若Slot底下沒有Item,則Instantiate一個 if (transform.childCount == 0) { GameObject itemObject = Instantiate (itemUIPrefab, transform); itemObject.GetComponentInChildren ().SetItem (item); } else { // 已存在該Item,所以增加數量即可 transform.GetComponentInChildren ().AddAmount (); } return transform.GetComponentInChildren (); } 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; inventorySystem.ShowToolTip (item.GetToolTipText ()); } } public void OnPointerExit(PointerEventData eventData){ inventorySystem.HideToolTip (); } public virtual void OnBeginDrag(PointerEventData eventData){ // Slot內是否已經有物體 if (transform.childCount > 0) { // 取得目前滑鼠點擊Slot的ItemUI ItemUI clickedItemUI = GetComponentInChildren (); // 當滑鼠沒有Item時 if (inventorySystem.isPickedItem == false) { // 取出全部 PickupAllSlotItem (clickedItemUI); } } } public void OnDrag(PointerEventData eventData){ } public virtual void OnDrop(PointerEventData eventData){ // Slot內是否已經有物體 if (transform.childCount > 0) { // 取得目前滑鼠點擊Slot的ItemUI ItemUI clickedItemUI = GetComponentInChildren (); // 當滑鼠有Item時 if (inventorySystem.isPickedItem == true) { // 當放入Item的是同一個時 if (clickedItemUI.Item.ID == inventorySystem.GetPickedItem ().Item.ID) { // 放入所有物體 PutInAllItem (clickedItemUI); } else { // 當放入的Item不是同一個時,將Slot與PickedItem交換 inventorySystem.GetPickedItem().ExchangeItem(clickedItemUI); } } } else { if (inventorySystem.isPickedItem == true) { // 放入所有物體 PutInAllItemToEmptySlot (); } } } public void OnEndDrag(PointerEventData eventData){ } //IPointerDownHandler public virtual void OnPointerDown(PointerEventData eventData){ // 使用滑鼠右鍵自動穿戴裝備 if (eventData.button == PointerEventData.InputButton.Right && inventorySystem.isPickedItem == false && transform.childCount > 0) { ItemUI clickedItemUI = GetComponentInChildren (); inventorySystem.WearingEquipment (clickedItemUI); } if (eventData.button != PointerEventData.InputButton.Left) { return; } // Slot內是否已經有物體 if (transform.childCount > 0) { // 取得目前滑鼠點擊Slot的ItemUI ItemUI clickedItemUI = GetComponentInChildren (); // 當滑鼠沒有Item時 if (inventorySystem.isPickedItem == false) { if (Input.GetKey (KeyCode.Z)) { // 壓住Z鍵時,只會取出一半的數量 PickupHalfOfSlotItem(clickedItemUI); } else { // 取出全部 PickupAllSlotItem (clickedItemUI); } // 當滑鼠有Item時 } else if (inventorySystem.isPickedItem == true) { // 當放入Item的是同一個時 if (clickedItemUI.Item.ID == inventorySystem.GetPickedItem ().Item.ID) { if (Input.GetKey (KeyCode.Z)) { // 壓住Z鍵時,只會一個一個的放入物體 PutInOneItem (); } else { // 放入所有物體 PutInAllItem (clickedItemUI); } } else { // 當放入的Item不是同一個時,將Slot與PickedItem交換 inventorySystem.GetPickedItem().ExchangeItem(clickedItemUI); } } } else { if (inventorySystem.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 inventorySystem.PickupItem (clickedItemUI.Item, amountToPick); if (amountRemained == 0) { Destroy (clickedItemUI.gameObject); } else { clickedItemUI.SetAmount (amountRemained); } } protected void PickupAllSlotItem(ItemUI clickedItemUI){ // 將PickedItem的ItemUI設置為目前滑鼠點中的ItemUI inventorySystem.PickupItem (clickedItemUI.Item, clickedItemUI.Amount); // 因為已經選中物體了,要將Slot內的ItemUI刪除 Destroy (clickedItemUI.gameObject); } protected void PutInOneItem(){ this.StoreItem (inventorySystem.GetPickedItem ().Item); inventorySystem.ReducePickedItem (1); } private void PutInAllItem(ItemUI clickedItemUI){ if (clickedItemUI.Item.Capacity > clickedItemUI.Amount) { int slotRemained = clickedItemUI.Item.Capacity - clickedItemUI.Amount; if (slotRemained >= inventorySystem.GetPickedItem ().Amount) { // 若Slot內的空間足以放下滑鼠拿著的全部物體 clickedItemUI.SetAmount (clickedItemUI.Amount + inventorySystem.GetPickedItem ().Amount); inventorySystem.ReducePickedItem (inventorySystem.GetPickedItem ().Amount); } else { // Slot內的空間不夠放,只能放下部分滑鼠拿著的物體 clickedItemUI.SetAmount (clickedItemUI.Amount + slotRemained); inventorySystem.ReducePickedItem (slotRemained); } } } private void PutInAllItemToEmptySlot(){ for (int i = 0; i < inventorySystem.GetPickedItem ().Amount; i++) { this.StoreItem (inventorySystem.GetPickedItem().Item); } inventorySystem.ReducePickedItem (inventorySystem.GetPickedItem().Amount); } } }
InventorySystem.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Text; using RPG.Character; using RPG.Weapons; using RPG.Core; namespace RPG.Inventory{ public class InventorySystem : MonoBehaviour { [SerializeField] ToolTip toolTip; [SerializeField] ItemUI pickedItem; [SerializeField] CharacterProperty characterProperty; public bool isPickedItem = false; ItemList itemList; ForgeFormulaList forgeFormulaList; Slot[] allSlotList; Slot[] knapsackSlotList; EquipmentSlot[] equipmentSlotList; VendorSlot[] vendorSlotList; ForgeSlot[] forgeSlotList; int coinAmount = 1000; Text coinText; void Start () { ParseItemsJson (); ParseForgeFormulaJson (); InitialSlotList (); coinText = GameObject.Find ("Coin").GetComponentInChildren(); coinText.text = coinAmount.ToString(); InitialVendorItem (); } void Update(){ PressGToAddItem (); MovePickedItemByMousePosition (); DiscardPickedItem (); } void InitialSlotList(){ allSlotList = GetComponentsInChildren (); equipmentSlotList = GetComponentsInChildren (); vendorSlotList = GetComponentsInChildren (); forgeSlotList = GetComponentsInChildren (); knapsackSlotList = GetComponentsInChildren (); knapsackSlotList = System.Array.FindAll (knapsackSlotList, CheckIfKnapsackSlot); } bool CheckIfKnapsackSlot(Slot slot){ if (slot is EquipmentSlot) { return false; } else if (slot is VendorSlot) { return false; } else if (slot is ForgeSlot) { return false; } else { return true; } } public void SaveInventory(){ StringBuilder saveData = new StringBuilder (); foreach (Slot slot in allSlotList) { if (slot is VendorSlot) { // 如果是商店類型的Slot則不需要儲存狀態,所以儲存為沒有物品的0 saveData.Append ("0-"); } else if (slot.transform.childCount > 0) { // Slot內有物品,儲存ID跟數量 ItemUI itemUI = slot.GetComponentInChildren (); saveData.Append (itemUI.Item.ID + "," + itemUI.Amount + "-"); } else { // Slot內沒有物品,儲存0 saveData.Append ("0-"); } } PlayerPrefs.SetString (this.gameObject.name, saveData.ToString()); PlayerPrefs.SetInt ("CoinAmount", coinAmount); } public void LoadInventory(){ if (PlayerPrefs.HasKey (this.gameObject.name)) { string savaData = PlayerPrefs.GetString (this.gameObject.name); string[] itemArray = savaData.Split ('-'); // Length - 1是因為最後一個沒有資料 for (int i = 0; i < itemArray.Length - 1; i++) { if (itemArray [i] != "0") { string[] itemInfo = itemArray [i].Split (','); // 取得物品的ID跟數量 int id = int.Parse (itemInfo[0]); int amount = int.Parse (itemInfo[1]); Item item = GetItemByID (id); // 將物品生成在指定的Slot for (int j = 0; j < amount; j++) { allSlotList [i].StoreItem (item); } } } } if (PlayerPrefs.HasKey ("CoinAmount")) { coinAmount = PlayerPrefs.GetInt ("CoinAmount"); coinText.text = coinAmount.ToString (); } } private bool ConsumeCoin(int amount){ if (coinAmount > 0) { coinAmount -= amount; coinText.text = coinAmount.ToString (); return true; } return false; } private void EarnCoin(int amount){ coinAmount += amount; coinText.text = coinAmount.ToString (); } public void BuyItem(Item item){ bool isSuccess = ConsumeCoin (item.BuyPrice); if (isSuccess) { StoreItem (item); } } public void SellItem(int sellAmount){ int sellPrice = pickedItem.Item.SellPrice * sellAmount; ReducePickedItem (sellAmount); EarnCoin (sellPrice); } 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(EquipmentSlot equipmentSlot in equipmentSlotList){ if(equipmentSlot.EquipmentTypeIsEqual (equipmentToWear.Item)){ // 換裝備的時候更換Player的武器 Weapon weaponConfig = Resources.Load ((equipmentToWear.Item as Equipment).EquipmentAssetsPath); if (weaponConfig != null) { Game.Instance.GetPlayerWeaponSystem ().PutWeaponInHand (weaponConfig); } if (equipmentSlot.transform.childCount > 0) { // 人物已配戴裝備了,將裝備與背包的裝備交換 equipmentSlot.GetComponentInChildren ().ExchangeItem(equipmentToWear); } else { if (isPickedItem == true) { // 若使用Drag-Drop的方式穿上裝備,要記得再將pickedItem設定為false isPickedItem = false; pickedItem.Hide (); } // 人物未配戴裝備,直接穿上 equipmentSlot.StoreItem (equipmentToWear.Item); } break; } } UpdatePropertyText (); } // 使用時機:於背包系統外直接給ID,讓玩家穿上裝備 public bool WearingEquipment(int id){ Equipment equipmentToWear = (GetItemByID (id) as Equipment); foreach (EquipmentSlot equipmentSlot in equipmentSlotList) { if (equipmentSlot.EquipmentTypeIsEqual (equipmentToWear)) { if (equipmentSlot.transform.childCount > 0) { // 人物已配戴裝備了,脫下舊裝備穿上新裝備 return TakeOffAndWearNewEquipment(equipmentToWear, equipmentSlot); } else { // 人物未配戴裝備,直接穿上 equipmentSlot.StoreItem (equipmentToWear); } return true; } } return false; } bool TakeOffAndWearNewEquipment(Equipment equipment, EquipmentSlot equipmentSlot){ ItemUI equipmentToWear = null; // 先確認背包內是否有空位,有的話就放入新裝備 foreach (Slot slot in knapsackSlotList) { if (slot.transform.childCount == 0) { equipmentToWear = slot.StoreItem (equipment); break; } } if (equipmentToWear == null) { Debug.LogError ("No Empty Slot."); return false; } // 並自動將背包內的新裝備與舊裝備進行更換 equipmentSlot.GetComponentInChildren ().ExchangeItem(equipmentToWear); return true; } public ItemUI GetPickedItem(){ return pickedItem; } public Slot[] GetSlotList(){ return allSlotList; } public void ShowToolTip(string content){ toolTip.Show (content); } public void HideToolTip(){ toolTip.Hide (); } public void UpdatePropertyText(){ characterProperty.UpdatePropertyText (); } 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 knapsackSlotList) { 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 knapsackSlotList) { if (slot.transform.childCount == 0) { // 將Item存進該Slot slot.StoreItem (item); return true; } } Debug.LogError ("No Empty Slot."); return false; } public void ForgeItem(){ List materialsIDList = new List (); List forgeSlotList = new List (); // 取得Forge Slot內的Item foreach (ForgeSlot forgeSlot in forgeSlotList) { if (forgeSlot.transform.childCount > 0) { ItemUI currentItemUI = forgeSlot.transform.GetComponentInChildren (); for (int i = 0; i < currentItemUI.Amount; i++) { materialsIDList.Add (currentItemUI.Item.ID); forgeSlotList.Add (forgeSlot); } } } // 檢查item組合是否匹配任一鍛造公式 foreach (ForgeFormula formula in forgeFormulaList.ForgeFormulaEntityList) { if (formula.MatchFormula (materialsIDList)) { // 符合公式,存入鍛造物品到背包內 StoreItem (formula.ResultID); // 消耗材料 ConsumeMaterials(formula, forgeSlotList); break; } } } private void ConsumeMaterials(ForgeFormula formula, List forgeSlotList){ List requireList = formula.GetRequireList(); foreach (int ID in requireList) { foreach (ForgeSlot forgeSlot in forgeSlotList) { if (forgeSlot.transform.childCount > 0 && forgeSlot.GetItemID() == ID) { ItemUI itemUI = forgeSlot.GetComponentInChildren (); itemUI.ReduceAmount (1); if (itemUI.Amount <= 0) { DestroyImmediate (itemUI.gameObject); } break; } } } } private void PressGToAddItem(){ // 測試程式碼,手動生成物品 if (Input.GetKeyDown (KeyCode.G)) { StoreItem (Random.Range(1,18)); } } private void InitialVendorItem(){ Item[] vendorItemList = new Item[8]; // 手動初始化要放入商店的物品 vendorItemList [0] = GetItemByID (1); vendorItemList [1] = GetItemByID (2); vendorItemList [2] = GetItemByID (3); vendorItemList [3] = GetItemByID (4); vendorItemList [4] = GetItemByID (5); vendorItemList [5] = GetItemByID (6); vendorItemList [6] = GetItemByID (7); vendorItemList [7] = GetItemByID (8); foreach (Item item in vendorItemList) { foreach (VendorSlot vendorSlot in vendorSlotList) { // 如果同類型物品疊加在一起 if (vendorSlot.transform.childCount >= 1 && vendorSlot.GetItemID () == item.ID && vendorSlot.IsFilled () == false) { vendorSlot.StoreItem (item); break; } // 如果沒有同類物品,則放入空Slot else if (vendorSlot.transform.childCount == 0) { vendorSlot.StoreItem (item); break; } } } } private void MovePickedItemByMousePosition(){ if (isPickedItem) { // 將滑鼠座標轉換成Canvas上的座標 Vector2 position; Canvas canvas = GetComponentInParent
WeaponPickupPoint.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; using RPG.Character; using RPG.Core; namespace RPG.Weapons{ // 在編輯模式執行程式碼 [ExecuteInEditMode] public class WeaponPickupPoint : MonoBehaviour { [SerializeField] Weapon weaponConfig; [SerializeField] AudioClip pickupSFX; [Header("Rotation")] [SerializeField] float xRotationsPerMinute = 1f; [SerializeField] float yRotationsPerMinute = 1f; [SerializeField] float zRotationsPerMinute = 1f; [Header("Box Collider")] [SerializeField] Vector3 colliderCenter = new Vector3(0, 0.7f, 0); [SerializeField] Vector3 colliderSize = new Vector3(1, 1, 1); AudioSource audioSource; BoxCollider myCollider; void Start () { if (Application.isPlaying) { audioSource = gameObject.AddComponent(); myCollider = gameObject.AddComponent (); myCollider.center = colliderCenter; myCollider.size = colliderSize; } } void Update () { // 判斷要在編輯模式下才執行程式 if (!Application.isPlaying) { DestroyChildren (); InstantiateWeapon (); } RotateTransform (); } void DestroyChildren(){ Transform simulation = transform.GetChild (0); foreach (Transform child in simulation.transform) { DestroyImmediate (child.gameObject); } } // 產生預覽用的武器物件於場景中 void InstantiateWeapon(){ Transform simulation = transform.GetChild (0); GameObject weapon = weaponConfig.GetWeaponPrefab(); weapon.transform.position = Vector3.zero; Instantiate (weapon, simulation); } // 觸發時更換武器 void OnCollisionEnter(Collision collision){ PutEquipmentIntoInventory (collision); PutWeaponInHand(collision); } void PutEquipmentIntoInventory(Collision collision){ PlayerMovement player = collision.gameObject.GetComponent (); if (player) { // 若碰觸者為玩家,則將武器放進裝備欄 bool isSuccess = Game.Instance.inventorySystem.WearingEquipment(weaponConfig.GetID()); if (!isSuccess) { return; } } } void PutWeaponInHand(Collision collision){ WeaponSystem weaponSystem = collision.gameObject.GetComponent (); if (weaponSystem) { weaponSystem.PutWeaponInHand (weaponConfig); audioSource.PlayOneShot (pickupSFX); Destroy (gameObject); } } void RotateTransform(){ Transform simulation = transform.GetChild (0); //xRotationsPerMinute為1代表轉1圈,1圈有360度,將圈數乘以360度後再除以60秒,便得出1秒轉幾度 //Time.deltaTime為Update每次執行Frame經過了幾秒的基本時間單位,故將此單位乘以1秒所需旋轉的度數 //故能得出每個Frame要旋轉幾度 float xDegreesPerFrame = (xRotationsPerMinute * 360) / 60 * Time.deltaTime; simulation.RotateAround (simulation.position, simulation.right, xDegreesPerFrame); float yDegreesPerFrame = (yRotationsPerMinute * 360) / 60 * Time.deltaTime; simulation.RotateAround (simulation.position, simulation.up, yDegreesPerFrame); float zDegreesPerFrame = (zRotationsPerMinute * 360) / 60 * Time.deltaTime; simulation.RotateAround (simulation.position, simulation.forward, zDegreesPerFrame); } } }
接著來測試遊戲看看吧,現在玩家裝備的是一把矛。
然後我把敵人殺掉之後,他掉落了一把劍,走過去撿起來吧。
撿起來以後,玩家會自動裝備這把劍,而且原本的矛也會放到背包中。
留言
張貼留言