最近学习了Unity背包系统设计的基本设计。学习资料主要参考了B站up主:M_Studio的教学视频。学习完后,在Assets Store上重新下载了一套GUI对学习到的知识进行了一次复盘。现在,对复盘的过程进行一次记录。这个背包系统主要实现了基本的背包功能,Player(玩家)在场景中拾取得到物品,然后物品能够在背包上出现,背包中的物品也能够进行移动以及交换位置。
使用的资源都是在Asset Store上下载得到的免费资源,本次使用的UI主要是GUI_Parts、Simple Fantasy GUI、Tiny RPG Forest。
1. 设置UI- 在场景中按照以下父子关系的顺序,创建UI。
- 然后,对UI组件对象进行重新命名。
-
把Slot设置为预制体,Slot表示一个装备栏,装备栏里有对应的游戏物品,这个装备栏显示的是物品的图片和物品的数量。
-
设置好Slot预制体后,可以在游戏场景中删除Slot的UI对象。
- UI的图片可以按照自己的喜好设置,这里需要注意的是Grid对象需要添加一个Grid Layout Group组件,使Grid做成网格布局。
- 通过上述的UI组件设置,其在场景中生成的效果如下图所示。
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- itemList = new List
- ();//设置背包里面物品的列表
}
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 slots = new List();//物品栏中的物品对象
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().slotID = j;
instance.slots[j].GetComponent().SetupSlot(instance.myBag.itemList[j]);
}
}
}
- 场景中的Bag物体对象,按以下组件设置。
-
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(); } public void OnDrag(PointerEventData eventData) { currentRect.anchoredPosition += eventData.delta; } }
-
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预制体中组件的设置如下
-
在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().slotID; transform.SetParent(transform.parent.parent);//把物体从父节点中分离,使其能够在最上层显示 transform.position = eventData.position; GetComponent ().blocksRaycasts = false; } //拖拽 public void OnDrag(PointerEventData eventData) { //transform.position = eventData.position; Vector3 pos; m_RT = gameObject.GetComponent (); 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 ().slotID]; myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent ().slotID] = temp; eventData.pointerCurrentRaycast.gameObject.transform.parent.position = orignalParent.position; eventData.pointerCurrentRaycast.gameObject.transform.parent.SetParent(orignalParent); GetComponent ().blocksRaycasts = true; return; } //如果放开位置没有物品,则直接放下。 transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform); transform.position = eventData.pointerCurrentRaycast.gameObject.transform.position; myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent ().slotID] = myBag.itemList[currentItemID]; myBag.itemList[currentItemID] = null; GetComponent ().blocksRaycasts = true; } } -
Item物体的组件设置情况如下。
-
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(); coll = GetComponent (); anim = GetComponent(); } 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); } } }
- 背包初始状态
- 物品转移到背包里面
- 交换物品
- 拖拽物品



