当前位置: 首页 > article >正文

Unity3D仿星露谷物语开发20之道具简介弹出窗

1、目标

当鼠标悬停在栏的某个道具上面时(没有点击道具),会自动弹出道具的简介的说明框。

2、思路

1)制作一个预制件

2)当鼠标悬停在某个slot上时,填充预制件的数据

3、自动布局ContentSizeFitter组件

布局相关的多个组件说明参考:「Unity3D」自动布局LayoutElement、ContentSizeFitter、AspectRatioFitter、GridLayoutGroup_unity layout element-CSDN博客

(1)什么是ContentSizeFitter组件?

它是Unity UGUI中的一个组件,用于自动调整UI元素的大小,以适应其内容的大小变化。

它可以根据内容的大小自动调整UI元素的宽度和高度,确保内容不会被截断或溢出。

(2)工作原理

生效的时候,如果尺寸变动,会根据pivot来改变伸缩方向,即:pivot在中心就是四圈伸展或收缩,pivot在左上就是向右下伸或收缩,以此类推。

4、制作预制件

在Hierarchy -> PersistentScene -> UI -> MainGameUICanvas 下创建新的空物体命名为InventoryTextBox。

(1)添加ContentSizeFitter组件

给这个对象添加ContentSizeFitter组件。

设置Vertical Fit属性为Preferred Size,会根据元素设置这个文本框的垂直大小。

像图像和文本框这样的元素可以设置一个默认的带大小,然后根据内容进行扩展。

(2)添加Vertical Layout Group组件

设置Spacing为-4,使得元素间有轻微的重叠。

Control Child Size的Width和Height都勾选,子元素的Layout Element的才能其作用。

整体效果如下:

(3)添加子元素Image对象

给InventoryTextBox添加Image对象,并且重命名为TextBoxImage1。

Soure Image选择“textBox”。

(4)添加Vertical Layout Group组件

其属性设置如下:

整体效果如下:

(5)添加Text对象

在TextBoxImage1下添加UI -> Text MeshPro对象 并重命名为Text1。

其属性设置如下:

复制Text1得到Text2,其属性设置如下:

复制Text2得到Text3,其属性设置如下:

(6)复制TextBoxImage1得到TextBoxImage2

修改Padding -> Top值为10。

并将其下的Text1~3的Font Size设置为6,Vertex Color设置为black。

整体效果如下:

然后清空所有6个Text对象中的Text Input,默认情况下没有文本输出。

最后将InventoryTextBox对象拖到Assets -> Prefabs -> UI目录下:

然后再删除Hierarchy中的InventoryTextBox对象。

5、创建UIInventoryTextBox脚本

在Assets -> Scripts -> UI -> UIInventory目录下,创建UIInventoryTextBox脚本。

其代码如下:

using TMPro;
using UnityEngine;

public class UIInventoryTextBox : MonoBehaviour
{
    [SerializeField] private TextMeshProUGUI textMeshTop1 = null;
    [SerializeField] private TextMeshProUGUI textMeshTop2 = null;
    [SerializeField] private TextMeshProUGUI textMeshTop3 = null;
    [SerializeField] private TextMeshProUGUI textMeshBottom1 = null;
    [SerializeField] private TextMeshProUGUI textMeshBottom2 = null;
    [SerializeField] private TextMeshProUGUI textMeshBottom3 = null;


    public void SetTextboxText(string textTop1, string textTop2, string textTop3, string textBottom1, string textBottom2, string textBottom3)
    {
        textMeshTop1.text = textTop1;
        textMeshTop2.text = textTop2;
        textMeshTop3.text = textTop3;
        textMeshBottom1.text = textBottom1;
        textMeshBottom2.text = textBottom2;
        textMeshBottom3.text = textBottom3;
    }
}

打开InventoryTextBox的预制体,然后添加UIInventoryTextBox组件。

同时填充6个属性值。

6、优化Settings.cs脚本

设置工具的description描述信息。

添加如下代码:

  // Tools 
  public const string HoeingTool = "Hoe";
  public const string ChoppingTool = "Axe";
  public const string BreakingTool = "Pickaxe";
  public const string ReapingTool = "Scythe";
  public const string WateringTool = "Watering Can";
  public const string CollectingTool = "Basket";

7、优化InventoryManager.cs脚本

增加获取道具类型描述的功能。

/// <summary>
/// Get the item type description for an item type - returns the item type description as a string for a given ItemType
/// </summary>
/// <param name="itemType"></param>
/// <returns></returns>
public string GetItemTypeDescription(ItemType itemType)
{
    string itemTypeDescription;
    switch (itemType)
    {
        case ItemType.Breaking_tool:
            itemTypeDescription = Settings.BreakingTool;
            break;
        case ItemType.Chopping_tool:
            itemTypeDescription = Settings.ChoppingTool;
            break;
        case ItemType.Hoeing_tool:
            itemTypeDescription = Settings.HoeingTool;
            break;
        case ItemType.Reaping_tool:
            itemTypeDescription = Settings.ReapingTool;
            break;
        case ItemType.Watering_tool:
            itemTypeDescription = Settings.WateringTool;
            break;
        case ItemType.Collecting_tool:
            itemTypeDescription = Settings.CollectingTool;
            break;

        default:
            itemTypeDescription = itemType.ToString();
            break;
    }

    return itemTypeDescription;
}

8、优化UIInventoryBar.cs脚本

增加属性,用于记录Bar对应的弹出框。

[HideInInspector] public GameObject inventoryTextBoxGameobject;

其完整代码如下:

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; // 所有插槽集合
    public GameObject inventoryBarDraggedItem;
    [HideInInspector] public GameObject inventoryTextBoxGameobject;


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

9、优化UIInventorySlot.cs脚本

添加属性并进行初始化:

private Canvas parentCanvas;
[SerializeField] private GameObject inventoryTextBoxPrefab = null;

 private void Awake()
 {
     parentCanvas = GetComponentInParent<Canvas>();
 }

给UIInventorySlot类继承IPointerEnterHandler, IPointerExitHandler的接口,并实现这两个接口:

public void OnPointerEnter(PointerEventData eventData)
{
    // Populate text box with item details
    if(itemQuantity != 0)
    {
        // Instantiate inventory text box
        inventoryBar.inventoryTextBoxGameobject = Instantiate(inventoryTextBoxPrefab, transform.position, Quaternion.identity);
        inventoryBar.inventoryTextBoxGameobject.transform.SetParent(parentCanvas.transform, false);

        UIInventoryTextBox inventoryTextBox = inventoryBar.inventoryTextBoxGameobject.GetComponent<UIInventoryTextBox>();

        // Set item type description
        string itemTypeDescription = InventoryManager.Instance.GetItemTypeDescription(itemDetails.itemType);

        // Populate text box
        inventoryTextBox.SetTextboxText(itemDetails.itemDescription, itemTypeDescription, "", itemDetails.itemLongDescription, "", "");

        // Set text box position according to inventory bar position
        if (inventoryBar.IsInventoryBarPositionBottom)
        {
            inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 0f);
            inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y + 50f, transform.position.z);
        }
        else
        {
            inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 1f);
            inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y - 50f, transform.position.z);
        }
    }
}

public void OnPointerExit(PointerEventData eventData)
{
    DestroyInventoryTextBox();
}

private void DestroyInventoryTextBox()
{
    if (inventoryBar.inventoryTextBoxGameobject != null) 
    {
        Destroy(inventoryBar.inventoryTextBoxGameobject);
    }
}

当在库存栏中交互两个道具的位置时,此时鼠标会经过Slot,此时我们不希望显示描述窗口。

if (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null) 
{


    // Destroy inventory text box
    DestroyInventoryTextBox();
}

完整代码如下:

using UnityEngine;
using TMPro;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;

public class UIInventorySlot : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
    private Camera mainCamera;
    private Transform parentItem; // 场景中的物体父类
    private GameObject draggedItem; // 被拖动的物体
    private Canvas parentCanvas;

    public Image inventorySlotHighlight;
    public Image inventorySlotImage;
    public TextMeshProUGUI textMeshProUGUI;

    [SerializeField] private UIInventoryBar inventoryBar = null;
    [SerializeField] private GameObject itemPrefab = null;
    [SerializeField] private int slotNumber = 0; // 插槽的序列号
    [SerializeField] private GameObject inventoryTextBoxPrefab = null;

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

    private void Awake()
    {
        parentCanvas = GetComponentInParent<Canvas>();
    }

    private void Start()
    {
        mainCamera = Camera.main;
        parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if(itemDetails != null) 
        {
            // Disable keyboard input
            Player.Instance.DisablePlayerInputAndResetMovement();

            // Instatiate gameobject as dragged item
            draggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);

            // Get image for dragged item
            Image draggedItemImage = draggedItem.GetComponentInChildren<Image>();
            draggedItemImage.sprite = inventorySlotImage.sprite;
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        // move game object as dragged item
        if(!draggedItem != null)
        {
            draggedItem.transform.position = Input.mousePosition;
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        // Destroy game object as dragged item
        if (draggedItem != null) 
        {
            Destroy(draggedItem);

            // if drag ends over inventory bar, get item drag is over and swap then
            if (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null) 
            {
                // get the slot number where the drag ended
                int toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;

                // Swap inventory items in inventory list
                InventoryManager.Instance.SwapInventoryItems(InventoryLocation.player, slotNumber, toSlotNumber);


                // Destroy inventory text box
                DestroyInventoryTextBox();
            }
            else
            {
                // else attemp to drop the item if it can be dropped
                if (itemDetails.canBeDropped)
                {
                    DropSelectedItemAtMousePosition();
                }
            }

            // Enable player input
            Player.Instance.EnablePlayerInput();
        }
    }

    /// <summary>
    /// Drops the item(if selected) at the current mouse position. called by the DropItem event
    /// </summary>
    private void DropSelectedItemAtMousePosition()
    {
        if(itemDetails != null)
        {
            Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));

            // Create item from prefab at mouse position
            GameObject itemGameObject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, parentItem);
            Item item = itemGameObject.GetComponent<Item>();
            item.ItemCode = itemDetails.itemCode;

            // Remove item from player's inventory
            InventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);
        }
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        // Populate text box with item details
        if(itemQuantity != 0)
        {
            // Instantiate inventory text box
            inventoryBar.inventoryTextBoxGameobject = Instantiate(inventoryTextBoxPrefab, transform.position, Quaternion.identity);
            inventoryBar.inventoryTextBoxGameobject.transform.SetParent(parentCanvas.transform, false);

            UIInventoryTextBox inventoryTextBox = inventoryBar.inventoryTextBoxGameobject.GetComponent<UIInventoryTextBox>();

            // Set item type description
            string itemTypeDescription = InventoryManager.Instance.GetItemTypeDescription(itemDetails.itemType);

            // Populate text box
            inventoryTextBox.SetTextboxText(itemDetails.itemDescription, itemTypeDescription, "", itemDetails.itemLongDescription, "", "");

            // Set text box position according to inventory bar position
            if (inventoryBar.IsInventoryBarPositionBottom)
            {
                inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 0f);
                inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y + 50f, transform.position.z);
            }
            else
            {
                inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 1f);
                inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y - 50f, transform.position.z);
            }
        }
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        DestroyInventoryTextBox();
    }

    private void DestroyInventoryTextBox()
    {
        if (inventoryBar.inventoryTextBoxGameobject != null) 
        {
            Destroy(inventoryBar.inventoryTextBoxGameobject);
        }
    }
}

10、UIInventorySlot0~11添加InventoryTextBox属性

最终效果如下:


http://www.kler.cn/a/501016.html

相关文章:

  • HAMi + prometheus-k8s + grafana实现vgpu虚拟化监控
  • 【论文阅读+复现】High-fidelity Person-centric Subject-to-Image Synthesis
  • seo泛目录(seo泛目录程序)
  • 给DevOps加点料:融入安全性的DevSecOps
  • 亚远景-ASPICE评估:汽车软件项目的过程能力评价
  • 【Oracle篇】深入了解执行计划中的访问路径(含表级别、B树索引、位图索引、簇表四大类访问路径)
  • Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。
  • 【数据结构】航班查询系统:链表的实际运用
  • 大数据学习(34)-mapreduce详解
  • 指令的修饰符
  • STM32F103ZET6战舰版单片机开发板PCB文件 电路原理图
  • 基类指针指向派生类对象,基类指针的首地址永远指向子类从基类继承的基类首地址
  • 【Hystrix-2】使用 Hystrix 实现服务容错与降级:Java 案例代码详解
  • 30_Redis哨兵模式
  • 未来十年:科技重塑生活的全景展望
  • 如何实现图片选择功能
  • 【Rust自学】11.10. 集成测试
  • js逆向说明
  • Python中定位包含特定文本信息的元素
  • 网络安全 | DevSecOps:将安全融入DevOps开发生命周期
  • 5. DL深度学习(Deep Learning)
  • 【2025 Rust学习 --- 15 迭代器的消耗】
  • [创业之路-242]:《华为双向指挥系统》-1-组织再造-企业普遍采用的5种组织结构形式
  • 苍穹外卖07——来单提醒和客户催单(涉及SpringTask、WebSocket协议、苍穹外卖跳过微信支付同时保证可以收到订单功能)
  • 排序算法(归并排序、快速排序)
  • Type-C双屏显示器方案