Related Posts Plugin for WordPress, Blogger...

5-10 Click To Pickup Item

本篇要介紹用滑鼠點擊背包內的物體後,會將物體選取起來,如果按住Z鍵再選擇物體的話,只會選擇一半的物體。選取起來的物體,會跟著滑鼠的鼠標移動。因為這個功能同時修改的部分比較多,所以會分多篇文章介紹,本篇先介紹選取,後續篇再介紹放下。

首先在Canvas底下創造一個PickedItem,這個物件是前面章節所介紹名為的Item的Prefab,如果不知道這個Prefab怎麼製作,請先看看以前的文章:
5-6 Knapsack User Interface 

 
製作完成後,應該會在場景中看見如下圖的PickedItem物件,這是要用在滑鼠點擊物體後,會依照物體的資訊顯示在PickedItem上面,並將原本點擊的Slot上的Item移除。

PickedItem預設要不啟用,所以請大家在Inspector視窗中將勾勾取消。

首先簡單介紹一下Slot修改後的程式碼,主要是繼承IPointerDownHandler,並覆寫OnPointerDown方法,該方法可以偵測滑鼠按下的事件。


然後實作內容中,先用transform.childCount確認Slot內是否有Item,再用InvntorySystem類中的bool屬性isPickedItem判斷目前是否處於『Picked』狀態,如果為非,則可以將Slot內的物體取出來。其他行則有撰寫註解,請大家自己閱讀。


在InventorySystem類中,則主要新增了PickupItem方法,此方法用來將滑鼠點中的Item屬性設定到PickedItem上面。


以下提供本次修改的完整程式碼。
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 ();
   if (transform.childCount > 0) {
    // 取得目前滑鼠點擊Slot的ItemUI
    ItemUI currentItemUI = GetComponentInChildren ();
    if (invetorySystem.isPickedItem == false) {
     if (Input.GetKey (KeyCode.Z)) {
      // 壓住Z鍵時,只會取出一半的數量
      int amountToPick = (currentItemUI.Amount+1) / 2;
      int amountRemained = currentItemUI.Amount - amountToPick;
      // 將PickedItem的ItemUI設置為目前滑鼠點中的ItemUI
      invetorySystem.PickupItem (currentItemUI.Item, amountToPick);
      if (amountRemained == 0) {
       Destroy (currentItemUI.gameObject);
      } else {
       currentItemUI.SetAmount (amountRemained);
      }
     } else {
      // 將PickedItem的ItemUI設置為目前滑鼠點中的ItemUI
      invetorySystem.PickupItem(currentItemUI.Item, currentItemUI.Amount);
      // 因為已經選中物體了,要將Slot內的ItemUI刪除
      Destroy (currentItemUI.gameObject);
     }
    }
   }
  }
 }
}

InventorySystem.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;

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 ();
   // 測試程式碼,手動生成物品
   StoreItem (1);
   StoreItem (2);
   StoreItem (1);
   StoreItem (3);
   StoreItem (4);
   StoreItem (5);
   StoreItem (6);
   StoreItem (7);
   StoreItem (8);
   StoreItem (9);
   StoreItem (10);
   StoreItem (11);
   StoreItem (12);
   StoreItem (7);
   StoreItem (8);
   StoreItem (9);
   StoreItem (13);
   StoreItem (14);
   StoreItem (15);
   StoreItem (16);
   StoreItem (17);
  }

  void Update(){
   if (isPickedItem) {
    // 將滑鼠座標轉換成Canvas上的座標
    Vector2 position;
    Canvas canvas = GetComponentInParent ();
    RectTransformUtility.ScreenPointToLocalPointInRectangle (
     canvas.transform as RectTransform,
     Input.mousePosition,
     null,
     out position);
    pickedItem.SetLocalPosition (position);
   }
  }

  public void PickupItem(Item item, int amount){
   pickedItem.SetItem (item, amount);
   pickedItem.Show ();
   toolTip.Hide ();
   isPickedItem = true;
  }

  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 ("Items");
   // 解析Json格式
   itemList = JsonUtility.FromJson (json.text);
   // 使用foreach迴圈讀取List中的Item資料
   // TODO 讀取Json有Bug
  }
 }
}


ItemUI.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace RPG.Inventory{
 public class ItemUI : MonoBehaviour{
  public Item Item { get; set; }
  public int Amount { get; set; }

  public void SetItem(Item item, int amount = 1){
   this.Item = item;
   this.Amount = amount;
   GetComponentInChildren ().sprite = Resources.Load (item.Sprite);
   GetComponentInChildren ().text = Amount.ToString();
  }

  public void AddAmount(int amount = 1){
   this.Amount += amount;
   GetComponentInChildren ().text = Amount.ToString();
  }

  public void SetAmount(int amount){
   this.Amount = amount;
   GetComponentInChildren ().text = Amount.ToString();
  }

  public void Show(){
   gameObject.SetActive (true);
  }

  public void Hide(){
   gameObject.SetActive (false);
  }

  public void SetLocalPosition(Vector3 localPosition){
   transform.localPosition = localPosition;
  }
 }
}


程式碼修改完成後,於KanpsackPanel的InventorySystem中,我們必須將剛剛新增到Canvas底下的PickedItem拉進參數,如下圖。

接著,讓我們執行遊戲看看吧。用滑鼠點擊腰帶後,腰帶被從背包上取下來了。

移動滑鼠時,這個腰帶也會跟著滑鼠移動。

如果是按住Z鍵再點擊血量瓶的話,會發現我們只取下一半的血量瓶。

留言