Unity3D仿星露谷物语开发53之库存管理页面
1、目标
添加一个完整的库存管理页面。
2、库存页取消选中功能
在主界面的Slot中选中一个item,然后按Esc键进入库存管理页面,发现item还是处于选中状态。
需求:当我们进入这个库存页面时,任何当前被选中的东西都不应该被选中,因为我们可能会在这个新的库存菜单中操纵一些库存位置。
(1)修改UIManager.cs脚本
添加一行代码:
修改EnablePauseMenu方法,当菜单页启用时,我们想销毁任何当前拖动的项目,我们也想清除任何当前选定的项目,添加2行代码如下:
(2)修改UIInventoryBar.cs脚本
添加第一个方法:
public void DestroyCurrentlyDraggedItems()
{for(int i = 0; i < inventorySlot.Length; i++){if (inventorySlot[i].draggedItem != null){Destroy(inventorySlot[i].draggedItem);}}
}
当前UIInventorySlot类(inventorySlot变量)的draggedItem属性是private,需要修改为public。
进入UIInventorySlot.cs脚本,修改一行代码,private -> public。
添加第二个方法:
public void ClearCurrentlySelectedItems()
{for(int i = 0; i < inventorySlot.Length; i++){inventorySlot[i].ClearSelectedItem();}
}
进入UIInventorySlot.cs脚本,修改一行代码,private -> public。
(3)配置UI信息
重新运行游戏,我们看到拖拽Slot中的Item到库存页后(按Esc键后弹出的页面),拖拽的商品自动消失了。
3、构建库存页对象结构
操作Tab0InventoryManagement对象。
添加Image组件,配置信息如下:
接下来就是创建库存管理槽。
在Tab0InventoryManagement下创建子物体命名为InventoryManagementSlot,添加Image组件,设置如下参数信息:
创建灰度图。在InventoryManagementSlot下创建子物体命名为GreyedOutImage,添加Image组件,配置如下:
创建文本。在InventoryManagementSlot下创建子物体命名为Text,添加组件TextMeshPro-Text(UI),配置如下:
预制体化:将InventoryManagementSlot放到Assets -> Prefabs -> UI下。
将Hierarchy下的InventoryManagementSlot重命名为InventoryManagementSlot0,然后复制11份再进行重命名InventoryManagementSlot1 ~ InventoryManagementSlot11如下:
然后分别调整InventoryManagementSlot1 ~ InventoryManagementSlot11的Pos X的值。每个slot+21,比如InventoryManagementSlot1的pos X值为14+21=35。调整完毕后效果如下:
然后构建第二排的slot。
复制InventoryManagementSlot0 ~ InventoryManagementSlot11,得到新的12个slot对象,分别重命名为InventoryManagementSlot12 ~ InventoryManagementSlot23,同时设置Pos Y值为6,效果如下:
然后构建第三排的slot,继续采用复制的方式,并修改对象的名称为InventoryManagementSlot24 ~ InventoryManagementSlot35,同时设置Pos Y值为-15。
然后构建第四排的slot,继续采用复制的方式,并修改对象的名称为InventoryManagementSlot36 ~ InventoryManagementSlot47,同时设置Pos Y值为-36。
处理完之后效果如下:
4、编写脚本代码
在Assets/Scripts/UI下创建目录命名为UIPauseMenu。
(1)编写PauseMenuInventoryManagementSlot.cs脚本
在UIPauseMenu下新建脚本命名为PauseMenuInventoryManagementSlot.cs。
using UnityEngine;
using TMPro;
using UnityEngine.EventSystems;
using UnityEngine.UI;public class PauseMenuInventoryManagementSlot : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{public Image inventoryManagementSlotImage; // 库存管理槽public TextMeshProUGUI textMeshProUGUI;public GameObject greyedOutImageGO;[SerializeField] private PauseMenuInventoryManagement inventoryManagement = null;[SerializeField] private GameObject inventoryTextBoxPrefab = null; // 显示项目的库存文件框预制体[HideInInspector] public ItemDetails itemDetails; // 槽位中物品详情[HideInInspector] public int itemQuantity; // 槽位中物品数量[SerializeField] private int slotNumber = 0; // 槽位的序列值0~47private Vector3 startingPosition;public GameObject draggedItem;private Canvas parentCanvas; // 父对象画布的引用private void Awake(){parentCanvas = GetComponentInParent<Canvas>();}public void OnBeginDrag(PointerEventData eventData){if(itemQuantity != 0){// Instatiate gameobject as dragged itemdraggedItem = Instantiate(inventoryManagement.inventoryManagementDraggedItemPrefab, inventoryManagement.transform);// Get image for dragged itemImage draggedItemImage = draggedItem.GetComponentInChildren<Image>(); draggedItemImage.sprite = inventoryManagementSlotImage.sprite;}}public void OnDrag(PointerEventData eventData){// move game object as dragged itemif(draggedItem != null){draggedItem.transform.position = Input.mousePosition;}}public void OnEndDrag(PointerEventData eventData){// Destory game object as dragged itemif(draggedItem != null){Destroy(draggedItem);// Get object drag is overif(eventData.pointerCurrentRaycast.gameObject != null&& eventData.pointerCurrentRaycast.gameObject.GetComponent<PauseMenuInventoryManagementSlot>() != null){// get the slot number where drag endedint toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<PauseMenuInventoryManagementSlot>().slotNumber; // Swap inventory items in inventory listInventoryManager.Instance.SwapInventoryItems(InventoryLocation.player, slotNumber, toSlotNumber);// Destory inventory text boxinventoryManagement.DestroyInventoryTextBoxGameobject();}}}public void OnPointerEnter(PointerEventData eventData){// Populate text box with item detailsif(itemQuantity != 0){// Instantiate inventory text boxinventoryManagement.inventoryTextBoxGameobject = Instantiate(inventoryTextBoxPrefab, transform.position, Quaternion.identity); inventoryManagement.inventoryTextBoxGameobject.transform.SetParent(parentCanvas.transform, false);UIInventoryTextBox inventoryTextBox = inventoryManagement.inventoryTextBoxGameobject.GetComponent<UIInventoryTextBox>();// Set item type descriptionstring itemTypeDescription = InventoryManager.Instance.GetItemTypeDescription(itemDetails.itemType);// Populate text boxinventoryTextBox.SetTextboxText(itemDetails.itemDescription, itemTypeDescription, "", itemDetails.itemLongDescription, "", "");// Set text box positionif(slotNumber > 23){inventoryManagement.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 0.5f);inventoryManagement.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y + 50f, transform.position.z);}else{inventoryManagement.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 1f);inventoryManagement.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y - 50f, transform.position.z);}}}public void OnPointerExit(PointerEventData eventData){inventoryManagement.DestroyInventoryTextBoxGameobject();}}
(2)编写PauseMenuInventoryManagement.cs脚本
在UIPauseMenu下新建脚本命名为PauseMenuInventoryManagement.cs。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PauseMenuInventoryManagement : MonoBehaviour
{[SerializeField] private PauseMenuInventoryManagementSlot[] inventoryManagementSlot = null;public GameObject inventoryManagementDraggedItemPrefab;[SerializeField] private Sprite transparent16x16 = null;[HideInInspector] public GameObject inventoryTextBoxGameobject; // 鼠标悬停在一个库存物品上时弹出的游戏对象private void OnEnable(){EventHandler.InventoryUpdatedEvent += PopulatePlayerInventory;// Populate player inventoryif(InventoryManager.Instance != null){PopulatePlayerInventory(InventoryLocation.player, InventoryManager.Instance.inventoryLists[(int)InventoryLocation.player]);}}private void OnDisable(){EventHandler.InventoryUpdatedEvent -= PopulatePlayerInventory;DestroyInventoryTextBoxGameobject();}public void DestroyInventoryTextBoxGameobject(){// Destory inventory text box if createdif(inventoryTextBoxGameobject != null){Destroy(inventoryTextBoxGameobject);}}// 退出菜单(Esc)时销毁拖拽的对象public void DestroyCurrentlyDraggedItems(){// loop through all player inventory itemsfor(int i = 0; i < InventoryManager.Instance.inventoryLists[(int)InventoryLocation.player].Count; i++){if (inventoryManagementSlot[i].draggedItem != null){Destroy(inventoryManagementSlot[i].draggedItem);}}}private void PopulatePlayerInventory(InventoryLocation inventoryLocation, List<InventoryItem> playerInventoryList){if(inventoryLocation == InventoryLocation.player){InitialiseInventoryManagementSlots();// loop through all player inventory itemsfor(int i = 0; i < InventoryManager.Instance.inventoryLists[(int)InventoryLocation.player].Count; i++){// Get inventory item detailsinventoryManagementSlot[i].itemDetails = InventoryManager.Instance.GetItemDetails(playerInventoryList[i].itemCode);inventoryManagementSlot[i].itemQuantity = playerInventoryList[i].itemQuantity;if (inventoryManagementSlot[i].itemDetails != null){// update inventory management slot with image and quantityinventoryManagementSlot[i].inventoryManagementSlotImage.sprite = inventoryManagementSlot[i].itemDetails.itemSprite;inventoryManagementSlot[i].textMeshProUGUI.text = inventoryManagementSlot[i].itemQuantity.ToString();}}}}private void InitialiseInventoryManagementSlots(){// Clear inventory slotsfor(int i = 0; i < Settings.playerMaximumInventoryCapacity; i++){inventoryManagementSlot[i].greyedOutImageGO.SetActive(false);inventoryManagementSlot[i].itemDetails = null;inventoryManagementSlot[i].itemQuantity = 0;inventoryManagementSlot[i].inventoryManagementSlotImage.sprite = transparent16x16;inventoryManagementSlot[i].textMeshProUGUI.text = "";}// Grey out unavailable slotsfor(int i = InventoryManager.Instance.inventoryListCapacityIntArray[(int)InventoryLocation.player]; i < Settings.playerMaximumInventoryCapacity; i++){inventoryManagementSlot[i].greyedOutImageGO.SetActive(true);}}}
6、配置槽位对象
点击Tab0InventoryManagement,添加PauseMenuInventoryManagement组件。
双击Assets/Prefabs/UI/InventoryManagementSlot.prefab。
添加PauseMenuInventoryManagementSlot组件,配置如下:
返回主界面,同时选中InventoryManagementSlot0~InventoryManagementSlot47,然后将Tab0InventoryManagement拖到InventoryManagement配置中。
依次配置InventoryManagementSlot0~InventoryManagementSlot47中的SlotNumber的值。
点击Tab0InventoryManagement对象,将48个Slot对象拖到InventoryManagementSlot配置中。
将Assets/Prefabs/UI/InventoryDraggedItem.prefab拖到inventoryManagementDraggedItemPrefab配置中。
再选择transparent16x16对应的图片。
7、修改UIManager.cs脚本
添加一行代码:
在DisablePauseMenu函数中添加一行代码:
8、配置UI对象
9、运行游戏
有几个测试点:
1)当inventory库中有超过12个item时,按esc键进入库存管理页面可以看到所有的item
2)库存管理页面中交互item的效果,第2排和第1排交互item,按esc退出后的库存中仍然正常显示
3)刚好在拖拽,按esc键后拖拽取消
4)库存管理页面中第25个开始是置灰的