1、目标

当角色拾取道具时能够显示在库存栏中,同时可以显示拾取道具的数量。

2、TextMeshPro组件

TextMeshPro(TMP)是一个Unity文本组件,TMP为文本样式和纹理提供了强大的灵活性。

TMP与Text的差异:

  • TMP文本样式比Text文本样式更加丰富和灵活
  • TMP对应的Shader支持描边、阴影和颜色渐变等美术效果,而且不会额外增加网格的顶点数量。而Text需要添加Shadow组件、Outline组件等才能支持这些美术效果,且会增加网格的顶点数量。
  • 性能方面,TMP比Text会占用更多的堆内存,消耗更多的渲染开销。
  • TMP是以哦那个SDF技术来渲染字体,而Text使用FreeType库来渲染字体。
  • TMP引用字体有限,如果没找到字体最终是CallBack不到系统字体,会显示一个方框。而Text能CallBack回系统字体。

3、创建插槽预制体

(1)创建UIInventorySlot

点击UIInventoryBar,然后按F键,Scene界面聚焦在UIInventoryBar物体上。

我们需要在里面创建一系列的库存槽,在插槽中操纵图像和文本类型的项目数。

在UIInventoryBar下创建空物体命名为UIInventorySlot。

重置它的位置为Middle Left。

然后给UIInventorySlot添加Image组件,设置属性如下:

Preserve Aspect:当勾选这个选项之后再对图片进行操作时,会保持图片的宽度和高度的比例不发生改变。

然后再添加Canvas Group组件。

(2)创建InventoryHighlight

在UIInventorySlot下创建InventoryHighlight对象,增加这个作用是:后续点击slot时,会有个红色方框突出一下,表明选中了当前slot。

增加Image组件,并设置属性如下:

目前不需要突出显示某个slot,所以需要设置color.alpha=0。

(3)创建TMP文本

在UIInventorySlot下Create -> UI -> Text - TextMeshPro。

接下来创建字体资源,Window -> TextMeshPro -> Font Asset Creator。

在Source Font File中选择Peepo,点击“Generate Font Atlas”,点击"Save"保存到默认文件夹下。

然后点击Text(TMP)对象,设置属性如下:

(4)创建预制体

在Assets -> Prefabs下创建UI目录,并将UIInventorySlot拖入其中。

(5)创建多个Slot

复制多个UIInventorySlot到UIInventoryBar中,通过调整Pos X值使得每个UIInventorySlot可以对号入座。

4、创建UIInventorySlot脚本

在Assets -> Scripts -> UI -> UIInventory下创建UIInventorySlot脚本。

using UnityEngine;
using TMPro;
using UnityEngine.UI;

public class UIInventorySlot : MonoBehaviour
{
    public Image inventorySlotHighlight;
    public Image inventorySlotImage;
    public TextMeshProUGUI textMeshProUGUI;

    [HideInInspector] public ItemDetails itemDetails;
    [HideInInspector] public int itemQuantity;

}

双击UIInventorySlot预制体,添加UIInventorySlot组件,并设置对应的属性信息如下:

5、优化UIInventoryBar脚本

新增两个变量,一个是默认查抄的图案为空图案,另一个是插槽集合需要配置进来。

一般在OnEnable()和OnDisable()方法中监听事件。

新增部分代码如下:

[SerializeField] private Sprite blank16x16sprite = null; // 插槽默认图案
[SerializeField] private UIInventorySlot[] inventorySlot = null; // 所有插槽集合

private void OnDisable()
{
    EventHandler.InventoryUpdatedEvent -= InventoryUpdated;
}

private void InventoryUpdated(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList)
{
    if (inventoryLocation == InventoryLocation.player)
    {
        ClearInventorySlots();

        if (inventorySlot.Length > 0 && inventoryList.Count > 0)
        {
            for (int i = 0; i < inventorySlot.Length; i++)
            {
                if (i < inventoryList.Count)
                {
                    int itemCode = inventoryList[i].itemCode;

                    ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);

                    if (itemDetails != null)
                    {
                        // add images and details to inventory item slot
                        inventorySlot[i].inventorySlotImage.sprite = itemDetails.itemSprite;
                        inventorySlot[i].textMeshProUGUI.text = inventoryList[i].itemQuantity.ToString();
                        inventorySlot[i].itemDetails = itemDetails;
                        inventorySlot[i].itemQuantity = inventoryList[i].itemQuantity;
                    }
                }
                else
                {
                    break;
                }

            }
        }
    }
}

private void ClearInventorySlots()
{
    if(inventorySlot.Length > 0)
    {
        // loop through inventory slots and update with blank sprite
        for(int i = 0; i < inventorySlot.Length; i++)
        {
            inventorySlot[i].inventorySlotImage.sprite = blank16x16sprite;
            inventorySlot[i].textMeshProUGUI.text = "";
            inventorySlot[i].itemDetails = null;
            inventorySlot[i].itemQuantity = 0;
        }
    }
}

private void OnEnable()
{
    EventHandler.InventoryUpdatedEvent += InventoryUpdated;
}

完整代码如下:

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

public class UIInventoryBar : MonoBehaviour
{
    [SerializeField] private Sprite blank16x16sprite = null; // 插槽默认图案
    [SerializeField] private UIInventorySlot[] inventorySlot = null; // 所有插槽集合

    private RectTransform rectTransform;

    private bool _isInventoryBarPositionBottom = true;

    public bool IsInventoryBarPositionBottom { get { return _isInventoryBarPositionBottom;} set { _isInventoryBarPositionBottom = value; } }

    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void OnDisable()
    {
        EventHandler.InventoryUpdatedEvent -= InventoryUpdated;
    }

    private void InventoryUpdated(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList)
    {
        if (inventoryLocation == InventoryLocation.player)
        {
            ClearInventorySlots();

            if (inventorySlot.Length > 0 && inventoryList.Count > 0)
            {
                for (int i = 0; i < inventorySlot.Length; i++)
                {
                    if (i < inventoryList.Count)
                    {
                        int itemCode = inventoryList[i].itemCode;

                        ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);

                        if (itemDetails != null)
                        {
                            // add images and details to inventory item slot
                            inventorySlot[i].inventorySlotImage.sprite = itemDetails.itemSprite;
                            inventorySlot[i].textMeshProUGUI.text = inventoryList[i].itemQuantity.ToString();
                            inventorySlot[i].itemDetails = itemDetails;
                            inventorySlot[i].itemQuantity = inventoryList[i].itemQuantity;
                        }
                    }
                    else
                    {
                        break;
                    }

                }
            }
        }
    }

    private void ClearInventorySlots()
    {
        if(inventorySlot.Length > 0)
        {
            // loop through inventory slots and update with blank sprite
            for(int i = 0; i < inventorySlot.Length; i++)
            {
                inventorySlot[i].inventorySlotImage.sprite = blank16x16sprite;
                inventorySlot[i].textMeshProUGUI.text = "";
                inventorySlot[i].itemDetails = null;
                inventorySlot[i].itemQuantity = 0;
            }
        }
    }

    private void OnEnable()
    {
        EventHandler.InventoryUpdatedEvent += InventoryUpdated;
    }

    private void Update()
    {
        // Switch inventory bar position depending on player position
        SwitchInventoryBarPosition();
    }

    private void SwitchInventoryBarPosition()
    {
        Vector3 playerViewportPosition = Player.Instance.GetPlayerViewportPosition();

        if (playerViewportPosition.y > 0.3f && IsInventoryBarPositionBottom == false)
        {
            rectTransform.pivot = new Vector2(0.5f, 0f);
            rectTransform.anchorMin = new Vector2(0.5f, 0f);
            rectTransform.anchorMax = new Vector2(0.5f, 0f);
            rectTransform.anchoredPosition = new Vector2(0f, 2.5f);

            IsInventoryBarPositionBottom = true;
        }
        else if (playerViewportPosition.y <= 0.3f && IsInventoryBarPositionBottom == true) 
        {
            rectTransform.pivot = new Vector2(0.5f, 1f);
            rectTransform.anchorMin = new Vector2(0.5f, 1f);
            rectTransform.anchorMax = new Vector2(0.5f, 1f);
            rectTransform.anchoredPosition = new Vector2(0f, -2.5f);

            IsInventoryBarPositionBottom = false;
        }
    }
}

点击UIInventoryBar对象,

1)配置Blank 16x16sprite对象如下。

2)点击右上角的锁按钮,然后把12个slot拖到Inventory Slot变量中。

6、优化InventoryManager脚本

注释所有调用DebugPrintInventoryList的地方(共2个)。

执行程序,效果如下:

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐