正文
Unity3D — — UGUI之简易背包
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
Uinity版本:2017.3
最近在学Siki老师的《黑暗之光RPG》教程,由于教程内用的是NGUI实现,而笔者本人用的是UGUI,所以在这里稍微写一下自己的实现思路(大致上和NGUI一样)
一、成品
先展现实现后的效果,如下:
功能简介:
物品的添加功能暂时通过摁下X来模拟(在Update()方法中实现)
实现的功能如图所示主要有以下几个
根据相应的物品ID添加到背包中 / 如果已有物品则数量+1
物品间的拖放交换
摁住物品1秒后显示详细信息
二、代码
代码分为两部分,背包整体Canvas(InventoryManger)和物品本身的Prefab(InventoryItem)
物品本身的代码是在之前一篇博文的基础上又添加了点东西:
Unity — — UGUI之背包物品拖放 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; public class InventoryManger : MonoBehaviour { public static InventoryManger _instance;
/// <summary>
///无法获取还未实例化的物体(目前不知道具体方法)
/// 因此用public来获取物体的Prefab
/// </summary>
public InventoryItem item;
public List<GameObject> inventorySlotList = new List<GameObject>();
public Text coinText; private Canvas inventory; //获取到背包,实现隐藏/显示功能
private int coinNum = ;
private int count;
bool isFull = false; //判断背包是否满 public void Awake()
{
//获取到背包的Canvas组件来控制背包开关
//如果整个Gamobject设为UnActive则当背包关闭时无法设置物品信息(#Null# InventoryItem._instance.SetItemInfo(id))
inventory = this.GetComponent<Canvas>();
inventory.enabled = false;
_instance = this; } private void Start()
{
coinText.text = coinNum.ToString();
} //模拟拾取
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.X))
{
int randomID = Random.Range(, );
GetItemID(randomID);
}
} /// <summary>
/// 通过ID号实例化Item
/// </summary>
/// <param name="id"> Item的ID号 </param>
public void GetItemID(int id)
{
//思路
//1.先查找所有格子找是否有物体
//1.1 如果有且ID相等 -> 数量+1,hasFoundItem设为true,跳出循环
//2.如果没找到相应物体,再次遍历所有格子
//2.1如果有空格子,实例化新物体
//2.2否则,背包满了 bool hasFoundItem = false;
foreach (GameObject temp in inventorySlotList)
{
InventoryItem slotChild = temp.GetComponentInChildren<InventoryItem>();
if (slotChild != null && slotChild.IdItem == id)
{
slotChild.ItemNumPlus();
isFull = false;
hasFoundItem = true;
Debug.Log(">>>> Add Num");
break;
}
} if(hasFoundItem==false)
{
foreach (GameObject temp in inventorySlotList)
{
InventoryItem slotChild = temp.GetComponentInChildren<InventoryItem>();
if (slotChild == null && count < inventorySlotList.Count)
{
Instantiate(item, temp.transform);
InventoryItem._instance.SetItemInfo(id);
count++;
Debug.Log(">>>> Instantiate");
isFull = false;
break;
}
else //(slotChild!=null && slotChild.IdItem != id && && count < inventorySlotList.Count)
{
isFull = true;
Debug.Log("This slot has item : " + "slotChild.id = " + slotChild.IdItem + " slot = " + temp.name);
Debug.Log("New ID = " + id);
Debug.Log(">>> next loop");
}
}
}
//背包满了
if (isFull)
{
Debug.Log("bag full");
} /// <summary>
/// 当没有交换物品功能时的实现方法
/// 按照顺序查找
/// </summary>
//foreach (GameObject temp in inventorySlotList)
//{
// InventoryItem slotChild = temp.GetComponentInChildren<InventoryItem>(); // //逻辑
// //if :1.当前格子无物体且背包未满 — — 实例化新的物品,full暂时设为false,跳出循环
// //else if :2.当前格子有物体且id相等 — — 物品数 + 1,full暂时设为false,跳出循环
// //else :3.当前格子有物体且id不等时 — — full暂时设为true,继续循环
// if (slotChild == null && count < inventorySlotList.Count)
// {
// Instantiate(item, temp.transform);
// InventoryItem._instance.SetItemInfo(id);
// count++;
// Debug.Log(">>>> Instantiate");
// isFull = false;
// break;
// }
// else if (slotChild != null && slotChild.IdItem == id)
// {
// slotChild.ItemNumPlus();
// isFull = false;
// Debug.Log(">>>> Add Num");
// break;
// }
// else //(slotChild!=null && slotChild.IdItem != id)
// {
// isFull = true;
// Debug.Log("This slot has item : " + "slotChild.id = " + slotChild.IdItem + " slot = " + temp.name);
// Debug.Log(">>> next loop");
// }
//}
} //显示背包
public void ShowInventory()
{
inventory.enabled = true;
} //隐藏背包
public void HideInventory()
{
inventory.enabled = false;
}
}
InventoryManger
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.EventSystems;
using UnityEngine.UI; public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerEnterHandler,IPointerDownHandler,IPointerUpHandler
{
public static InventoryItem _instance; private float counter = 0f; //计算摁住时间
private bool isPress = false; //是否摁住鼠标
private int numItem = ; //Item数量
private int NumItem
{
get
{
return numItem;
}
set
{
numItem = value;
}
}
//必须设为public,否则当未激活时获取不到ID
private int idItem = ; //Item的ID
public int IdItem
{
get
{
return idItem;
}
set
{
idItem = value;
}
} private Text numText; // Item 数量的text
private Image itemImage; //Item UI 图片
private Transform originalSlot;
private GameObject parent;
private GameObject item;
private float x_item;
private float y_item;
private Vector2 itemSize;
private bool isDragging = false; //默认设为false,否则OnPointerEnter每帧都会调用,会有bug /// <summary>
/// 添加CanvasGroup组件,在物品拖动时blocksRaycasts设置为false;
/// 让鼠标的Pointer射线穿过Item物体检测到UI下层的物体信息
/// </summary>
private CanvasGroup itemCanvasGroup; public void Awake()
{
//由于在实例化初始阶段就要获取,因此放在Awake里
//在Start()中会报空指针
itemImage = this.GetComponent<Image>();
numText = this.GetComponentInChildren<Text>(); _instance = this;
}
private void Start()
{
itemCanvasGroup = this.GetComponent<CanvasGroup>();
item = this.transform.gameObject;
x_item = item.GetComponent<Image>().GetPixelAdjustedRect().width; //Image的初始长宽
y_item = item.GetComponent<Image>().GetPixelAdjustedRect().height; parent = GameObject.FindGameObjectWithTag("SlotGrid");
} //增加物品数量并更新text显示
public void ItemNumPlus(int num = )
{
numItem+=num;
numText.text = numItem.ToString();
} //设置物品具体信息
//1.根据ID得到物品信息(GetObjectInfoByID(id)方法)
//2.设置相应的ID号 、图片 、 数量text
public void SetItemInfo(int id)
{
ObjectInfo itemInfo = ObjectsInfo._instance.GetObjectInfoByID(id);
this.idItem = id; //设置id
itemImage.sprite = Resources.Load<Sprite>(itemInfo.icon_name);
numText.text = numItem.ToString();
} public void OnPointerEnter(PointerEventData eventData)
{
//当鼠标在最外层时(移出背包,Canvas外)
//让物品回到原位
if (eventData.pointerCurrentRaycast.depth== && isDragging==true)
{
SetOriginalPos(this.gameObject);
return;
} //Debug.Log(eventData.pointerCurrentRaycast.depth);
string objectTag = eventData.pointerCurrentRaycast.gameObject.tag;
Debug.Log("Raycast = "+objectTag); //Item的拖放逻辑
if(objectTag!=null && isDragging==true)
{ if (objectTag == Tags.InventorySlot) //如果是空格子,则放置Item
{
SetCurrentSlot(eventData);
}
else if (objectTag == Tags.InventoryItem) //交换物品
{
SwapItem(eventData);
}
else //如果都不是则返回原位
{
SetOriginalPos(this.gameObject);
}
}
} //把Item回归到原来位置
public void SetOriginalPos(GameObject gameobject)
{
gameobject.transform.SetParent(originalSlot);
gameobject.GetComponent<RectTransform>().anchoredPosition = originalSlot.GetComponent<RectTransform>().anchoredPosition;
itemCanvasGroup.blocksRaycasts = true;
} //交换两个物体
//由于拖放中,正被拖放的物体没有Block RayCast
//具体思路:
//1.记录当前射线照射到的物体(Item2)
//2.获取Item2的parent的位置信息,并把item1放过去
//3.把Item2放到Item1所在的位置
public void SwapItem(PointerEventData eventData)
{
GameObject targetItem = eventData.pointerCurrentRaycast.gameObject; //下面这两个方法不可颠倒,否则执行顺序不一样会出bug
//BUG:先把Item2放到了Item1的位置,此时Item1得到的位置信息是传递后的Item2的(原本Item1的位置)
//因此会把Item1也放到Item2下,变成都在原本Item1的Slot内
SetCurrentSlot(eventData);
SetOriginalPos(targetItem);
} //设置Item到当前鼠标所在的Slot
public void SetCurrentSlot(PointerEventData eventData)
{
//如果Slot为空
if (eventData.pointerCurrentRaycast.gameObject.tag==Tags.InventorySlot)
{
Transform currentSlot= eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(currentSlot);
//如果只是transform position,图片会默认在左上角顶点处的Anchor
//因此这里用anchoredPosition让Item图片填充满Slot
this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
}
else if(eventData.pointerCurrentRaycast.gameObject.tag == Tags.InventoryItem)
{
Transform currentSlot = eventData.pointerCurrentRaycast.gameObject.transform.parent;
this.transform.SetParent(currentSlot);
this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
}
} public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log(eventData.pointerPress);
originalSlot = this.GetComponent<Transform>().parent; //每次拖拽开始前记录初始位置
isDragging = true;
itemCanvasGroup.blocksRaycasts = true;
item.transform.SetParent(parent.transform, false); // 将item设置到当前UI层级的最下面(最表面,防止被同一层级的UI覆盖)
item.transform.SetAsLastSibling(); //Item的UI图片自适应改变大小
item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, x_item);
item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, y_item);
} public void OnDrag(PointerEventData eventData)
{ itemCanvasGroup.blocksRaycasts = false;
DragPos(eventData);
Debug.Log(eventData.pointerPress);
} public void OnEndDrag(PointerEventData eventData)
{
OnPointerEnter(eventData);
itemCanvasGroup.blocksRaycasts = true;
isDragging = false;
Debug.Log(eventData.pointerPress);
} //获取鼠标当前位置,并赋给item
private void DragPos(PointerEventData eventData)
{
RectTransform RectItem = item.GetComponent<RectTransform>();
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(item.transform as RectTransform, eventData.position, eventData.pressEventCamera, out globalMousePos))
{
RectItem.position = globalMousePos;
}
} //摁住1秒后显示文本信息
private void Update()
{
//Image的长宽需要在update里更新一下
//防止游戏中改变窗口大小后,拖拽时物品Image出现bug,比例失调
x_item = item.GetComponent<Image>().GetPixelAdjustedRect().width;
y_item = item.GetComponent<Image>().GetPixelAdjustedRect().height; if (Input.GetMouseButton()&&isPress)
{
counter += Time.deltaTime;
Debug.Log(counter);
if(counter>=)
{
ShowItemDetail._instance.ShowDetail(idItem);
}
}
} public void OnPointerDown(PointerEventData eventData)
{
isPress = true;
Debug.Log("Down");
}
//释放鼠标后隐藏文本
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Up");
counter = ;
isPress = false;
ShowItemDetail._instance.HideDetail();
}
}
InventoryItem
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; public class ShowItemDetail : MonoBehaviour { public static ShowItemDetail _instance;
private Image ItemDetailImage;
private Text ItemDetailText;
private bool isShow = false;
private void Awake()
{
_instance = this;
ItemDetailImage = this.GetComponent<Image>();
ItemDetailText = this.GetComponentInChildren<Text>();
ItemDetailImage.enabled = false;
ItemDetailText.enabled = false;
} //显示物体文本信息
public void ShowDetail(int id)
{
//文本信息位置在物品右上角
transform.position = new Vector3(Input.mousePosition.x+InventoryItem._instance.GetComponent<RectTransform>().rect.width, Input.mousePosition.y+ InventoryItem._instance.GetComponent<RectTransform>().rect.height, ); ItemDetailImage.enabled = true;
ItemDetailText.enabled = true;
ObjectInfo info = ObjectsInfo._instance.GetObjectInfoByID(id);
string content="";
switch(info.type)
{
case ObjectType.Drug:
content = GetDrugDetail(info);
break;
case ObjectType.Equip:
content = GetEquipDetail(info);
break;
}
ItemDetailText.text = content;
}
//隐藏文本信息
public void HideDetail()
{
ItemDetailImage.enabled = false;
ItemDetailText.enabled = false;
}
//设置Drug的信息
public string GetDrugDetail(ObjectInfo info)
{
string str="";
str += "名称 :" + info.name + "\n";
str += "恢复HP :" + info.hp + "\n";
str += "恢复MP :" + info.mp + "\n";
str += "购买价 :" + info.price_buy + "\n";
str += "出售价 :" + info.price_sell + "\n"; return str;
}
//设置装备信息
public string GetEquipDetail(ObjectInfo info)
{
string str = "";
str += "名称 :" + info.name + "\n";
switch(info.equipType)
{
case EquipType.Headgear:
str += "穿戴类型 : 头盔\n";
break;
case EquipType.Armor:
str += "穿戴类型 : 盔甲\n";
break;
case EquipType.L_Hand:
str += "穿戴类型 : 左手\n";
break;
case EquipType.R_Hand:
str += "穿戴类型 : 右手\n";
break;
case EquipType.Shoe:
str += "穿戴类型 : 鞋子\n";
break;
case EquipType.Accessory:
str += "穿戴类型 : 饰品\n";
break;
}
switch(info.careerType)
{
case CareerType.Swordman:
str += "适用职业 : 剑士\n";
break;
case CareerType.Magician:
str += "适用职业 : 法师\n";
break;
case CareerType.Common:
str += "适用职业 : 通用\n";
break;
}
str += "攻击 : " + info.atk + "\n";
str += "防御 : " + info.def + "\n";
str += "速度 : " + info.spd + "\n";
str += "购买价 :" + info.price_buy + "\n";
str += "出售价 :" + info.price_sell + "\n";
return str;
}
}
ShowItemDetail
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; public class InventoryManger : MonoBehaviour { public static InventoryManger _instance;
/// <summary>
///无法获取还未实例化的物体(目前不知道具体方法)
/// 因此用public来获取物体的Prefab
/// </summary>
public InventoryItem item;
public List<GameObject> inventorySlotList = new List<GameObject>();
public Text coinText; private Canvas inventory; //获取到背包,实现隐藏/显示功能
private int coinNum = ;
private int count;
bool isFull = false; //判断背包是否满 public void Awake()
{
//获取到背包的Canvas组件来控制背包开关
//如果整个Gamobject设为UnActive则当背包关闭时无法设置物品信息(#Null# InventoryItem._instance.SetItemInfo(id))
inventory = this.GetComponent<Canvas>();
inventory.enabled = false;
_instance = this; } private void Start()
{
coinText.text = coinNum.ToString();
} //模拟拾取
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.X))
{
int randomID = Random.Range(, );
GetItemID(randomID);
}
} /// <summary>
/// 通过ID号实例化Item
/// </summary>
/// <param name="id"> Item的ID号 </param>
public void GetItemID(int id)
{
//思路
//1.先查找所有格子找是否有物体
//1.1 如果有且ID相等 -> 数量+1,hasFoundItem设为true,跳出循环
//2.如果没找到相应物体,再次遍历所有格子
//2.1如果有空格子,实例化新物体
//2.2否则,背包满了 bool hasFoundItem = false;
foreach (GameObject temp in inventorySlotList)
{
InventoryItem slotChild = temp.GetComponentInChildren<InventoryItem>();
if (slotChild != null && slotChild.IdItem == id)
{
slotChild.ItemNumPlus();
isFull = false;
hasFoundItem = true;
Debug.Log(">>>> Add Num");
break;
}
} if(hasFoundItem==false)
{
foreach (GameObject temp in inventorySlotList)
{
InventoryItem slotChild = temp.GetComponentInChildren<InventoryItem>();
if (slotChild == null && count < inventorySlotList.Count)
{
Instantiate(item, temp.transform);
InventoryItem._instance.SetItemInfo(id);
count++;
Debug.Log(">>>> Instantiate");
isFull = false;
break;
}
else //(slotChild!=null && slotChild.IdItem != id && && count < inventorySlotList.Count)
{
isFull = true;
Debug.Log("This slot has item : " + "slotChild.id = " + slotChild.IdItem + " slot = " + temp.name);
Debug.Log("New ID = " + id);
Debug.Log(">>> next loop");
}
}
}
//背包满了
if (isFull)
{
Debug.Log("bag full");
} /// <summary>
/// 当没有交换物品功能时的实现方法
/// 按照顺序查找
/// </summary>
//foreach (GameObject temp in inventorySlotList)
//{
// InventoryItem slotChild = temp.GetComponentInChildren<InventoryItem>(); // //逻辑
// //if :1.当前格子无物体且背包未满 — — 实例化新的物品,full暂时设为false,跳出循环
// //else if :2.当前格子有物体且id相等 — — 物品数 + 1,full暂时设为false,跳出循环
// //else :3.当前格子有物体且id不等时 — — full暂时设为true,继续循环
// if (slotChild == null && count < inventorySlotList.Count)
// {
// Instantiate(item, temp.transform);
// InventoryItem._instance.SetItemInfo(id);
// count++;
// Debug.Log(">>>> Instantiate");
// isFull = false;
// break;
// }
// else if (slotChild != null && slotChild.IdItem == id)
// {
// slotChild.ItemNumPlus();
// isFull = false;
// Debug.Log(">>>> Add Num");
// break;
// }
// else //(slotChild!=null && slotChild.IdItem != id)
// {
// isFull = true;
// Debug.Log("This slot has item : " + "slotChild.id = " + slotChild.IdItem + " slot = " + temp.name);
// Debug.Log(">>> next loop");
// }
//}
} //显示背包
public void ShowInventory()
{
inventory.enabled = true;
} //隐藏背包
public void HideInventory()
{
inventory.enabled = false;
}
}
InventoryManger
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.EventSystems;
using UnityEngine.UI; public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerEnterHandler,IPointerDownHandler,IPointerUpHandler
{
public static InventoryItem _instance; private float counter = 0f; //计算摁住时间
private bool isPress = false; //是否摁住鼠标
private int numItem = ; //Item数量
private int NumItem
{
get
{
return numItem;
}
set
{
numItem = value;
}
}
//必须设为public,否则当未激活时获取不到ID
private int idItem = ; //Item的ID
public int IdItem
{
get
{
return idItem;
}
set
{
idItem = value;
}
} private Text numText; // Item 数量的text
private Image itemImage; //Item UI 图片
private Transform originalSlot;
private GameObject parent;
private GameObject item;
private float x_item;
private float y_item;
private Vector2 itemSize;
private bool isDragging = false; //默认设为false,否则OnPointerEnter每帧都会调用,会有bug /// <summary>
/// 添加CanvasGroup组件,在物品拖动时blocksRaycasts设置为false;
/// 让鼠标的Pointer射线穿过Item物体检测到UI下层的物体信息
/// </summary>
private CanvasGroup itemCanvasGroup; public void Awake()
{
//由于在实例化初始阶段就要获取,因此放在Awake里
//在Start()中会报空指针
itemImage = this.GetComponent<Image>();
numText = this.GetComponentInChildren<Text>(); _instance = this;
}
private void Start()
{
itemCanvasGroup = this.GetComponent<CanvasGroup>();
item = this.transform.gameObject;
x_item = item.GetComponent<Image>().GetPixelAdjustedRect().width; //Image的初始长宽
y_item = item.GetComponent<Image>().GetPixelAdjustedRect().height; parent = GameObject.FindGameObjectWithTag("SlotGrid");
} //增加物品数量并更新text显示
public void ItemNumPlus(int num = )
{
numItem+=num;
numText.text = numItem.ToString();
} //设置物品具体信息
//1.根据ID得到物品信息(GetObjectInfoByID(id)方法)
//2.设置相应的ID号 、图片 、 数量text
public void SetItemInfo(int id)
{
ObjectInfo itemInfo = ObjectsInfo._instance.GetObjectInfoByID(id);
this.idItem = id; //设置id
itemImage.sprite = Resources.Load<Sprite>(itemInfo.icon_name);
numText.text = numItem.ToString();
} public void OnPointerEnter(PointerEventData eventData)
{
//当鼠标在最外层时(移出背包,Canvas外)
//让物品回到原位
if (eventData.pointerCurrentRaycast.depth== && isDragging==true)
{
SetOriginalPos(this.gameObject);
return;
} //Debug.Log(eventData.pointerCurrentRaycast.depth);
string objectTag = eventData.pointerCurrentRaycast.gameObject.tag;
Debug.Log("Raycast = "+objectTag); //Item的拖放逻辑
if(objectTag!=null && isDragging==true)
{ if (objectTag == Tags.InventorySlot) //如果是空格子,则放置Item
{
SetCurrentSlot(eventData);
}
else if (objectTag == Tags.InventoryItem) //交换物品
{
SwapItem(eventData);
}
else //如果都不是则返回原位
{
SetOriginalPos(this.gameObject);
}
}
} //把Item回归到原来位置
public void SetOriginalPos(GameObject gameobject)
{
gameobject.transform.SetParent(originalSlot);
gameobject.GetComponent<RectTransform>().anchoredPosition = originalSlot.GetComponent<RectTransform>().anchoredPosition;
itemCanvasGroup.blocksRaycasts = true;
} //交换两个物体
//由于拖放中,正被拖放的物体没有Block RayCast
//具体思路:
//1.记录当前射线照射到的物体(Item2)
//2.获取Item2的parent的位置信息,并把item1放过去
//3.把Item2放到Item1所在的位置
public void SwapItem(PointerEventData eventData)
{
GameObject targetItem = eventData.pointerCurrentRaycast.gameObject; //下面这两个方法不可颠倒,否则执行顺序不一样会出bug
//BUG:先把Item2放到了Item1的位置,此时Item1得到的位置信息是传递后的Item2的(原本Item1的位置)
//因此会把Item1也放到Item2下,变成都在原本Item1的Slot内
SetCurrentSlot(eventData);
SetOriginalPos(targetItem);
} //设置Item到当前鼠标所在的Slot
public void SetCurrentSlot(PointerEventData eventData)
{
//如果Slot为空
if (eventData.pointerCurrentRaycast.gameObject.tag==Tags.InventorySlot)
{
Transform currentSlot= eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(currentSlot);
//如果只是transform position,图片会默认在左上角顶点处的Anchor
//因此这里用anchoredPosition让Item图片填充满Slot
this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
}
else if(eventData.pointerCurrentRaycast.gameObject.tag == Tags.InventoryItem)
{
Transform currentSlot = eventData.pointerCurrentRaycast.gameObject.transform.parent;
this.transform.SetParent(currentSlot);
this.GetComponent<RectTransform>().anchoredPosition = currentSlot.GetComponent<RectTransform>().anchoredPosition;
}
} public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log(eventData.pointerPress);
originalSlot = this.GetComponent<Transform>().parent; //每次拖拽开始前记录初始位置
isDragging = true;
itemCanvasGroup.blocksRaycasts = true;
item.transform.SetParent(parent.transform, false); // 将item设置到当前UI层级的最下面(最表面,防止被同一层级的UI覆盖)
item.transform.SetAsLastSibling(); //Item的UI图片自适应改变大小
item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, x_item);
item.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, y_item);
} public void OnDrag(PointerEventData eventData)
{ itemCanvasGroup.blocksRaycasts = false;
DragPos(eventData);
Debug.Log(eventData.pointerPress);
} public void OnEndDrag(PointerEventData eventData)
{
OnPointerEnter(eventData);
itemCanvasGroup.blocksRaycasts = true;
isDragging = false;
Debug.Log(eventData.pointerPress);
} //获取鼠标当前位置,并赋给item
private void DragPos(PointerEventData eventData)
{
RectTransform RectItem = item.GetComponent<RectTransform>();
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(item.transform as RectTransform, eventData.position, eventData.pressEventCamera, out globalMousePos))
{
RectItem.position = globalMousePos;
}
} //摁住1秒后显示文本信息
private void Update()
{
//Image的长宽需要在update里更新一下
//防止游戏中改变窗口大小后,拖拽时物品Image出现bug,比例失调
x_item = item.GetComponent<Image>().GetPixelAdjustedRect().width;
y_item = item.GetComponent<Image>().GetPixelAdjustedRect().height; if (Input.GetMouseButton()&&isPress)
{
counter += Time.deltaTime;
Debug.Log(counter);
if(counter>=)
{
ShowItemDetail._instance.ShowDetail(idItem);
}
}
} public void OnPointerDown(PointerEventData eventData)
{
isPress = true;
Debug.Log("Down");
}
//释放鼠标后隐藏文本
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Up");
counter = ;
isPress = false;
ShowItemDetail._instance.HideDetail();
}
}
InventoryItem
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; public class ShowItemDetail : MonoBehaviour { public static ShowItemDetail _instance;
private Image ItemDetailImage;
private Text ItemDetailText;
private bool isShow = false;
private void Awake()
{
_instance = this;
ItemDetailImage = this.GetComponent<Image>();
ItemDetailText = this.GetComponentInChildren<Text>();
ItemDetailImage.enabled = false;
ItemDetailText.enabled = false;
} //显示物体文本信息
public void ShowDetail(int id)
{
//文本信息位置在物品右上角
transform.position = new Vector3(Input.mousePosition.x+InventoryItem._instance.GetComponent<RectTransform>().rect.width, Input.mousePosition.y+ InventoryItem._instance.GetComponent<RectTransform>().rect.height, ); ItemDetailImage.enabled = true;
ItemDetailText.enabled = true;
ObjectInfo info = ObjectsInfo._instance.GetObjectInfoByID(id);
string content="";
switch(info.type)
{
case ObjectType.Drug:
content = GetDrugDetail(info);
break;
case ObjectType.Equip:
content = GetEquipDetail(info);
break;
}
ItemDetailText.text = content;
}
//隐藏文本信息
public void HideDetail()
{
ItemDetailImage.enabled = false;
ItemDetailText.enabled = false;
}
//设置Drug的信息
public string GetDrugDetail(ObjectInfo info)
{
string str="";
str += "名称 :" + info.name + "\n";
str += "恢复HP :" + info.hp + "\n";
str += "恢复MP :" + info.mp + "\n";
str += "购买价 :" + info.price_buy + "\n";
str += "出售价 :" + info.price_sell + "\n"; return str;
}
//设置装备信息
public string GetEquipDetail(ObjectInfo info)
{
string str = "";
str += "名称 :" + info.name + "\n";
switch(info.equipType)
{
case EquipType.Headgear:
str += "穿戴类型 : 头盔\n";
break;
case EquipType.Armor:
str += "穿戴类型 : 盔甲\n";
break;
case EquipType.L_Hand:
str += "穿戴类型 : 左手\n";
break;
case EquipType.R_Hand:
str += "穿戴类型 : 右手\n";
break;
case EquipType.Shoe:
str += "穿戴类型 : 鞋子\n";
break;
case EquipType.Accessory:
str += "穿戴类型 : 饰品\n";
break;
}
switch(info.careerType)
{
case CareerType.Swordman:
str += "适用职业 : 剑士\n";
break;
case CareerType.Magician:
str += "适用职业 : 法师\n";
break;
case CareerType.Common:
str += "适用职业 : 通用\n";
break;
}
str += "攻击 : " + info.atk + "\n";
str += "防御 : " + info.def + "\n";
str += "速度 : " + info.spd + "\n";
str += "购买价 :" + info.price_buy + "\n";
str += "出售价 :" + info.price_sell + "\n";
return str;
}
}
ShowItemDetail
写代码时由于笔者考虑不周,这里简单说下途中遇到的几个问题以及解决方法:
(1)背包的开启/关闭问题:把整个背包的勾选关掉后,是无法获取到未激活的物体,因此只能通过背包下的的Canvas组件来控制开关显示
(2)物品的添加:由于物品间有拖放功能,因此在添加物品时要先遍历整个背包格子来判断是否已有物体
(3)物品UI大小的自适应:物品在拖放过程中改变窗口大小图片会出错,因此在InventoryItem的Update()里实时获取UI大小
(4)物品信息的显示:和图中看到的一样,显示的位置还是有些尴尬,目前设置是显示在鼠标点击位置的右上角
三、实现思路
在这里笔者浅谈下自己对这个背包功能的理解
首先背包大致分为三个层级,依次为:背包整体 -> 格子 -> 物品
背包:实现物品的添加功能,通过他获取其下各个子物体的信息(如Prefab等)
格子:存储物品的位置信息,不做具体的实现功能
物品:记录物品的图片,数量以及ID号等独立信息,不过这里笔者把拖放功能也在Item放里实现,通过当前item所在位置获取到格子的位置信息并设置相应的parent
大致设置如下:
格子和物品都是Prefab
其中有具体实现的只有InventoryCanvas和InventoryItem两个物体
然后通过InventoryCanvas获取到子物体的信息
接下来是在Item上添加脚本和CanvasGroup组件(在拖拽时blocks RayCasts设为false来获取到ItemUI层后的Slot层信息)
最后,这是在生成以及拖拽时Hierarchy层的变化
1.实例化ItemPrefab在相应格子内
2.在拖拽中把物品设置到到当前UI层最表面(最下方)
3.结束后设置成相应Slot的子物体