当前位置:   article > 正文

Unity游戏开发:背包系统的实现_unity背包系统

unity背包系统

背包是游戏中经常使用的一个组件,它负责管理玩家在游戏中所获得的道具。一个完整的背包系统应当具有将物品放置进背包、对背包内物品进行管理和使用背包内物品等功能。而往往一个背包系统的逻辑关系较为复杂,如果把所有功能都放在一个脚本中实现会使代码显得十分冗杂且缺乏逻辑。所以在背包系统的设计过程中,我们常将其分解为数据、逻辑和UI三部分分别来进行完成。

一、UI设计

Alt
以Cotton Puzzle中的背包设计为例,我们需要有物品展示栏、物品切换按键和物品提示信息等部分。
Alt

在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;//当前物品序号
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

定义UpdateUIEvent事件来实时管理Slot中显示的物品(通过调用SlotUI中实现的SetEmpty()和SetItem(itemDetails)方法实现)和左右按钮的可使用情况。

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

    private void OnDisable()
    {
        EventHandler.UpdateUIEvent -= OnUpdateUIEvent;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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);

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

2.4 物品描述信息的展示

首先我们需要定义一个变量来记录物品的信息。这里我们在ItemToolTip下创建一个Text来记录文本信息。

//ItemToolTip.cs
    public Text itemNameText;
  • 1
  • 2

接下来定义方法根据目前Slot所展示的物品名字来修改Text的内容。

//ItemToolTip.cs
    public void UpdateItemName(ItemName itemName)
    {
        itemNameText.text = itemName switch
        {
            ItemName.Key => "信箱钥匙",
            ItemName.Ticket => "一张船票",
            _ => ""
        };
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

为了方便使用我们将物品名写成了枚举变量,定义在Enums脚本中,方便在其他脚本中也可以使用。

//Enums.cs
/// <summary>
/// 物品名
/// </summary>
public enum ItemName
{
    None,
    Key,
    Ticket,
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

三、数据

首先为了我们能够在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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在unity中可以直接新建一个ItemDataList的数据对象,创建方式为:右键 -> Create ->Inventory -> ItemDataList
unity界面显示
定义GetItemDetails根据物品名获取ItemDetails 对象。

//ItemDataList.cs

    public List<ItemDetails> itemDetailsList = new List<ItemDetails>();
    public ItemDetails GetItemDetails(ItemName itemName)
    {
        return itemDetailsList.Find(i => i.itemName.Equals(itemName));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

四、逻辑

对于场景中的物体,在鼠标点击后需要能够被添加到背包内并在原有位置消失。可通过ItemClicked()函数来实现。

//Item.cs
    public ItemName itemName;

    public void ItemClicked()
    {
        //隐藏鼠标点击物体
        this.gameObject.SetActive(false);
        //将物品添加到背包
        InventoryManager.Instance.AddItem(itemName);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

最后,需要定义一个脚本InventoryManager对背包系统的UI、数据进行一个统一的管理

//InventoryManager.cs
    [SerializeField] private List<ItemName> itemList = new List<ItemName>();

    public ItemDataList itemData;//通过物品名在物品列表中查找相应物品
  • 1
  • 2
  • 3
  • 4

(1)在新加载游戏的时候需要清空物品列表

    private void OnStartNewGameEvent(int gameWeek)
    {
        itemList.Clear();
    }
  • 1
  • 2
  • 3
  • 4

(2)当物品被使用时需要将物品移除背包并更新UI

    private void OnItemUsedEvent(ItemName itemName)
    {
        var index = GetItemIndex(itemName);
        itemList.RemoveAt(index);

        if (itemList.Count == 0)
            EventHandler.CallUpdateUIEvent(null, -1);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(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);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(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("越界!");
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

注册这些事件:

    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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

另外有一些工具函数需要实现:

    public void AddItem(ItemName itemName)
    {
        if(!itemList.Contains(itemName))//列表中不包含物品则添加到列表中
        { 
            itemList.Add(itemName);
        }
        //UI显示
        EventHandler.CallUpdateUIEvent(itemData.GetItemDetails(itemName), itemList.Count - 1);
    }

    /// <summary>
    /// 获取物品在列表中的序号
    /// </summary>
    /// <param name="itemName"></param>
    /// <returns></returns>
    private int GetItemIndex(ItemName itemName)
    {
        for(int i=0;i<itemList.Count;i++)
        {
            if(itemName.Equals(itemList[i]))
                return i;
        }
        return -1;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/235887
推荐阅读
相关标签
  

闽ICP备14008679号