Unity2021 Inventory(背包)系统学习记录
最近学习了Unity背包系统设计的基本设计。学习资料主要参考了B站up主:M_Studio的教学视频。学习完后,在Assets Store上重新下载了一套GUI对学习到的知识进行了一次复盘。现在,对复盘的过程进行一次记录。这个背包系统主要实现了基本的背包功能,Player(玩家)在场景中拾取得到物品,然后物品能够在背包上出现,背包中的物品也能够进行移动以及交换位置。
? 使用的资源都是在Asset Store上下载得到的免费资源,本次使用的UI主要是GUI_Parts、Simple Fantasy GUI、Tiny RPG Forest。
1. 设置UI
- UI的图片可以按照自己的喜好设置,这里需要注意的是Grid对象需要添加一个Grid Layout Group组件,使Grid做成网格布局。
- 通过上述的UI组件设置,其在场景中生成的效果如下图所示。
2.设置ScriptableObject
ScriptableObject可以把数据存储在资源文件中,当退出运行时也一样会保持修改。这里把物品和背包设置成ScriptableObject对象。
-
新建一个C#脚本Item.cs,用于创建物体的ScriptableObject对象。 using UnityEngine;
[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/New Item")]//右键菜单快速创建
public class Item : ScriptableObject
{
public string itemName;//设置物品的名称
public Sprite itemImage;//设置物品的图片
public int itemHeld;//设置物品的数量
[TextArea]
public string itemInfo;//设置物品的描述
public bool equip;//设置物品可否装备
}
- 新建一个C#脚本Inventory.cs,用于创建背包的ScriptableObject对象。
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory/New Inventory")]
public class Inventory : ScriptableObject
{
public List<Item> itemList = new List<Item>();//设置背包里面物品的列表
}
3.在背包UI对象添加一个InventoryManger.cs脚本,用于控制物品栏里面的物品显示。
sing System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class InventoryManger : MonoBehaviour
{
private static InventoryManger instance;
public Inventory myBag; // 获取背包的信息
public GameObject slotGrid;//获取Grid网格对象
//public Slot SlotPrefab;
public GameObject emptySlot;//slot预制体
public Text itemInformation;//物品描述
public List<GameObject> slots = new List<GameObject>();//物品栏中的物品对象
void Awake()//单例模式,保证只有一个实例
{
if (instance!=null)
{
Destroy(this);
}
instance = this;
}
private void OnEnable()
{
RefreshItem();//每次启动程序时,刷新背包里的物品
instance.itemInformation.text = "";//物品描述设置为空
}
public static void UpdateItemInfo(string itemDes)
{
instance.itemInformation.text = itemDes;//设置物品描述信息
}
public static void RefreshItem()
{
//先删除物品栏中的所有游戏对象,遍历Grid挂载的子对象
for (int i = 0; i < instance.slotGrid.transform.childCount; i++)
{
if (instance.slotGrid.transform.childCount==0)//如果Grid没有挂载子对象
{
break;
}
//每遍历一个就删除一个子对象
Destroy(instance.slotGrid.transform.GetChild(i).gameObject);
instance.slots.Clear();
}
//重新加载Invetory中的物品对象。
for (int j = 0; j < instance.myBag.itemList.Count; j++)
{
instance.slots.Add(Instantiate(instance.emptySlot));
instance.slots[j].transform.SetParent(instance.slotGrid.transform, false);
instance.slots[j].GetComponent<Slot>().slotID = j;
instance.slots[j].GetComponent<Slot>().SetupSlot(instance.myBag.itemList[j]);
}
}
}
-
Bag组件中的MoveBag.cs脚本主要时控制背包窗口的拖拽。 using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class MoveBag : MonoBehaviour,IDragHandler
{
public Canvas canvas;
private RectTransform currentRect;
// Start is called before the first frame update
void Awake()
{
currentRect = GetComponent<RectTransform>();
}
public void OnDrag(PointerEventData eventData)
{
currentRect.anchoredPosition += eventData.delta;
}
}
4.Slot预制体的设置
-
Slot预制体挂载一个Slot.cs脚本,这个脚本主要时控制物品的图片和物品的数量。 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Slot : MonoBehaviour
{
public int slotID;
public Item SlotItem;
public Image SlotImage;//物品图片
public Text slotNum;//物品数量
public GameObject itemInslot;
public string slotInfo;
public void ItemOnClick()
{
InventoryManger.UpdateItemInfo(slotInfo);//点击该物品时,物品描述将会更新
}
public void SetupSlot(Item item)
{
if (item==null)//如果物品不存在,物品栏并不会激活
{
itemInslot.SetActive(false);
return;
}
SlotImage.sprite = item.itemImage;
slotNum.text = item.itemHeld.ToString();
slotInfo = item.itemInfo;
}
}
-
Slot预制体中组件的设置如下
5.物品的拖拽
-
在Slot预制体中的Item物体中挂载ItemOnDrag.cs组件 using UnityEngine;
using UnityEngine.EventSystems;
public class ItemOnDrag : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler
{
public Transform orignalParent;
private RectTransform m_RT;
public Inventory myBag;
private int currentItemID;
//开始拖拽
public void OnBeginDrag(PointerEventData eventData)
{
orignalParent = transform.parent;//记录物体原先的位置
currentItemID = orignalParent.GetComponent<Slot>().slotID;
transform.SetParent(transform.parent.parent);//把物体从父节点中分离,使其能够在最上层显示
transform.position = eventData.position;
GetComponent<CanvasGroup>().blocksRaycasts = false;
}
//拖拽
public void OnDrag(PointerEventData eventData)
{
//transform.position = eventData.position;
Vector3 pos;
m_RT = gameObject.GetComponent<RectTransform>();
RectTransformUtility.ScreenPointToWorldPointInRectangle(m_RT,eventData.position,eventData.enterEventCamera,out pos);
m_RT.position = pos;
//Debug.Log(eventData.pointerCurrentRaycast.gameObject.name);
}
//放开拖拽
public void OnEndDrag(PointerEventData eventData)
{
//使用射线来检测物体下方是不是有物品,如果有物品则交换位置
if (eventData.pointerCurrentRaycast.gameObject.name=="Item Image")
{
transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform.parent.parent);
transform.position = eventData.pointerCurrentRaycast.gameObject.transform.parent.parent.position;
//itemList的物品存储位置改变。
var temp = myBag.itemList[currentItemID];
myBag.itemList[currentItemID] =
myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotID];
myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotID] = temp;
eventData.pointerCurrentRaycast.gameObject.transform.parent.position = orignalParent.position;
eventData.pointerCurrentRaycast.gameObject.transform.parent.SetParent(orignalParent);
GetComponent<CanvasGroup>().blocksRaycasts = true;
return;
}
//如果放开位置没有物品,则直接放下。
transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform);
transform.position = eventData.pointerCurrentRaycast.gameObject.transform.position;
myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotID] = myBag.itemList[currentItemID];
myBag.itemList[currentItemID] = null;
GetComponent<CanvasGroup>().blocksRaycasts = true;
}
}
-
Item物体的组件设置情况如下。
6.Player(玩家)组件的设置
-
Player上挂载一个PlayerMovement.cs脚本,用于控制Player的移动,以及控制背包的打开。 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Rigidbody2D rb;
Collider2D coll;
Animator anim;
public GameObject myBag;
private bool isOpen;
public float speed;
Vector2 movement;
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
coll = GetComponent<Collider2D>();
anim = GetComponent<Animator>();
}
private void Update()
{
Movement();
SwitchAnim();
OpenMyBag();
}
void Movement()//移动
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
rb.MovePosition(rb.position + movement * speed * Time.deltaTime);
}
void SwitchAnim()//切换动画
{
if (movement != Vector2.zero)//保证Horizontal归0时,保留movment的值来切换idle动画的blend tree
{
anim.SetFloat("horizontal", movement.x);
anim.SetFloat("vertical", movement.y);
}
anim.SetFloat("speed", movement.magnitude);//magnitude 也可以用 sqrMagnitude 具体可以参考Api 默认返回值永远>=0
}
void OpenMyBag()
{
if (Input.GetKeyDown(KeyCode.O))
{
myBag.SetActive(!myBag.activeSelf);
}
}
}
7.效果演示
- 拖拽物品
|