Unity遊戲開發實現背包系統的示例詳解

引言

背包是遊戲中經常使用的一個組件,它負責管理玩傢在遊戲中所獲得的道具。一個完整的背包系統應當具有將物品放置進背包、對背包內物品進行管理和使用背包內物品等功能。而往往一個背包系統的邏輯關系較為復雜,如果把所有功能都放在一個腳本中實現會使代碼顯得十分冗雜且缺乏邏輯。所以在背包系統的設計過程中,我們常將其分解為數據、邏輯和UI三部分分別來進行完成。

一、UI設計

以Cotton Puzzle中的背包設計為例,我們需要有物品展示欄、物品切換按鍵和物品提示信息等部分。

在Canvas中創建ItemHolder,在ItemHolder中創建LeftButton和RightButton控制物品的左右切換、Slot來控制物品的顯示以及ItemToolTip來顯示物品的提示信息。

二、UI

==創建InventoryUI腳本並掛在ItemHolder上以對整個背包UI進行管理。== 首先,確定InventoryUI可以控制的組件並定義當前所顯示的物品序號,由於ItemToolTip的顯示與否和Slot直接相關聯,所以可以通過控制Slot間接控制ItemToolTip,在InventoryUI中便不再單獨對其進行控制。

//InventoryUI.cs
    public Button leftButton;
    public Button rightButton;
    public SlotUI slotUI;
    public int currentItemIndex;//當前物品序號

2.1 Slot中的物品顯示

//InventoryUI.cs
    /// <summary>
    /// 更新Slot中物品圖片
    /// </summary>
    /// <param name="itemDetails"></param>
    /// <param name="index"></param>
    private void OnUpdateUIEvent(ItemDetails itemDetails, int index)
    {
        if (itemDetails == null)//當前物品為空
        {
            slotUI.SetEmpty();
            currentItemIndex = -1;
            leftButton.interactable = false;
            rightButton.interactable=false;
        }
        else
        {
            currentItemIndex = index;
            slotUI.SetItem(itemDetails);
            if (index > 0)
                leftButton.interactable = true;
            if (currentItemIndex == -1)
            {
                leftButton.interactable = false;
                rightButton.interactable = false;
            }
        }
    }

定義UpdateUIEvent事件來實時管理Slot中顯示的物品(通過調用SlotUI中實現的SetEmpty()和SetItem(itemDetails)方法實現)和左右按鈕的可使用情況。

//InventoryUI.cs
    private void OnEnable()
    {
        EventHandler.UpdateUIEvent += OnUpdateUIEvent;
    }
    private void OnDisable()
    {
        EventHandler.UpdateUIEvent -= OnUpdateUIEvent;
    }

2.2 物品切換

//InventoryUI.cs
    /// <summary>
    /// 左右按鈕使用事件
    /// </summary>
    /// <param name="amount"></param>
    public void SwitchItem(int amount)
    {
        var index = currentItemIndex + amount;
        if(index< currentItemIndex)
        {
            leftButton.interactable = false;
            rightButton.interactable = true;
        }
        else if(index> currentItemIndex)
        {
            leftButton.interactable = true;
            rightButton.interactable = false;
        }
        else
        {
            leftButton.interactable = true;
            rightButton.interactable = true;
        }
        EventHandler.CallChangeItemEvent(index);
    }

2.3 SlotUI的實現

在SlotUI中需要實現將Slot圖片設置為指定物品(包含物品為空的情況)、鼠標點擊反饋、鼠標移入移出反饋(這裡主要要實現當鼠標移入顯示物品文字描述信息,移出取消顯示的功能)。

//SlotUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class SlotUI : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
    public Image itemImage;
    public ItemDetails currentItem;
    public ItemToolTip toolTip;
    private bool isClicked;
    /// <summary>
    /// 設置Slot圖片為指定item
    /// </summary>
    /// <param name="itemDetails"></param>
    public void SetItem(ItemDetails itemDetails)
    {
        currentItem = itemDetails;
        this.gameObject.SetActive(true);
        itemImage.sprite = itemDetails.itemSprite;
        itemImage.SetNativeSize();
    }
    /// <summary>
    /// 設置Slot圖片為空
    /// </summary>
    public void SetEmpty()
    {
        this.gameObject.SetActive(false);
    }
    public void OnPointerClick(PointerEventData eventData)
    {
        isClicked = !isClicked;
        EventHandler.CallItemSelectedEvent(currentItem, isClicked);
    }
    public void OnPointerEnter(PointerEventData eventData)
    {
        if (this.gameObject.activeInHierarchy)
        {
            toolTip.gameObject.SetActive(true);
            toolTip.UpdateItemName(currentItem.itemName);
        }
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        toolTip.gameObject.SetActive(false);
    }
}

2.4 物品描述信息的展示

首先我們需要定義一個變量來記錄物品的信息。這裡我們在ItemToolTip下創建一個Text來記錄文本信息。

//ItemToolTip.cs
    public Text itemNameText;

接下來定義方法根據目前Slot所展示的物品名字來修改Text的內容。

//ItemToolTip.cs
    public void UpdateItemName(ItemName itemName)
    {
        itemNameText.text = itemName switch
        {
            ItemName.Key => "信箱鑰匙",
            ItemName.Ticket => "一張船票",
            _ => ""
        };
    }

為瞭方便使用我們將物品名寫成瞭枚舉變量,定義在Enums腳本中,方便在其他腳本中也可以使用。

//Enums.cs
/// <summary>
/// 物品名
/// </summary>
public enum ItemName
{
    None,
    Key,
    Ticket,
}

三、數據

首先為瞭我們能夠在unity中更加直觀地對數據進行修改,利用以下代碼在unity的菜單下創建數據子菜單,並序列化對象。每個ItemDetails包含物品名(itemName)和物品貼圖(itemSprite)兩個屬性。

//ItemDataList.cs
[CreateAssetMenu(fileName ="ItemDataList",menuName = "Inventory/ItemDataList")]
/// <summary>
/// 對象序列化
/// </summary>
[System.Serializable]
public class ItemDetails
{
    public ItemName itemName;
    public Sprite itemSprite;
}

在unity中可以直接新建一個ItemDataList的數據對象,創建方式為:右鍵 -> Create ->Inventory -> ItemDataList

定義GetItemDetails根據物品名獲取ItemDetails 對象。

//ItemDataList.cs
    public List<ItemDetails> itemDetailsList = new List<ItemDetails>();
    public ItemDetails GetItemDetails(ItemName itemName)
    {
        return itemDetailsList.Find(i => i.itemName.Equals(itemName));
    }

四、邏輯

對於場景中的物體,在鼠標點擊後需要能夠被添加到背包內並在原有位置消失。可通過ItemClicked()函數來實現。

//Item.cs
    public ItemName itemName;
    public void ItemClicked()
    {
        //隱藏鼠標點擊物體
        this.gameObject.SetActive(false);
        //將物品添加到背包
        InventoryManager.Instance.AddItem(itemName);
    }

最後,需要定義一個腳本InventoryManager對背包系統的UI、數據進行一個==統一的管理==。

//InventoryManager.cs
    [SerializeField] private List<ItemName> itemList = new List<ItemName>();
    public ItemDataList itemData;//通過物品名在物品列表中查找相應物品

(1)在新加載遊戲的時候需要清空物品列表

    private void OnStartNewGameEvent(int gameWeek)
    {
        itemList.Clear();
    }

(2)當物品被使用時需要將物品移除背包並更新UI

    private void OnItemUsedEvent(ItemName itemName)
    {
        var index = GetItemIndex(itemName);
        itemList.RemoveAt(index);
        if (itemList.Count == 0)
            EventHandler.CallUpdateUIEvent(null, -1);
    }

(3)當場景加載之後,需要將目前背包中有的物品進行更新

    private void OnAfterSceneLoadedEvent()
    {
        if (itemList.Count == 0)
            EventHandler.CallUpdateUIEvent(null, -1);
        else
        {
            for (int i = 0; i < itemList.Count; i++)
            {
                EventHandler.CallUpdateUIEvent(itemData.GetItemDetails(itemList[i]), i);
            }
        }
    }

(4)當使用Button在背包中左右切換物品時,需要實時更改物品的圖片並判斷是否越界

    private void OnChangeItemEvent(int index)
    {
        if (index >= 0 && index < itemList.Count)
        {
            ItemDetails item = itemData.GetItemDetails(itemList[index]);
            EventHandler.CallUpdateUIEvent(item, index);
        }
        else
        {
            Debug.Log("越界!");
        }
    }

註冊這些事件:

    private void OnEnable()
    {
        EventHandler.ChangeItemEvent += OnChangeItemEvent;
        EventHandler.AfterSceneLoadedEvent += OnAfterSceneLoadedEvent;
        EventHandler.ItemUsedEvent += OnItemUsedEvent;
        EventHandler.StartNewGameEvent += OnStartNewGameEvent;
    }
    private void OnDisable()
    {
        EventHandler.ChangeItemEvent -= OnChangeItemEvent;
        EventHandler.AfterSceneLoadedEvent -= OnAfterSceneLoadedEvent;
        EventHandler.ItemUsedEvent -= OnItemUsedEvent;
        EventHandler.StartNewGameEvent -= OnStartNewGameEvent;
    }

另外有一些工具函數需要實現:

    public void AddItem(ItemName itemName)
    {
        if(!itemList.Contains(itemName))//列表中不包含物品則添加到列表中
        { 
            itemList.Add(itemName);
        }
        //UI顯示
        EventHandler.CallUpdateUIEvent(itemData.GetItemDetails(itemName), itemList.Count - 1);
    }
    /// &lt;summary&gt;
    /// 獲取物品在列表中的序號
    /// &lt;/summary&gt;
    /// &lt;param name="itemName"&gt;&lt;/param&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    private int GetItemIndex(ItemName itemName)
    {
        for(int i=0;i&lt;itemList.Count;i++)
        {
            if(itemName.Equals(itemList[i]))
                return i;
        }
        return -1;
    }

以上就是Unity遊戲開發實現背包系統的示例詳解的詳細內容,更多關於Unity遊戲開發背包系統的資料請關註WalkonNet其它相關文章!

推薦閱讀: