之前完成了一個(gè)背包以及添加物品,但是物品并不具備“物品”所該有的功能,除開每個(gè)游戲所需的特定的效果之外,也缺乏基本的移動(dòng)拖拽功能等,這篇我們完善一下背包系統(tǒng)。先回顧一下之前的代碼,這里我直接貼出來。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GridManager : MonoBehaviour {
public List<GameObject> grid;//格子列表,用來存儲(chǔ)所有個(gè)物品格子
public GameObject item;//物品對(duì)象,把prefab添加到這里
void Update () {
if(Input.GetKeyDown(KeyCode.K)){
AddItem(item);//調(diào)用添加物品的方法
}
}
public void AddItem(GameObject _item){
//查找每個(gè)格子,尋找空格子的物體。
for(int i=0;i<grid.Count;i++){
//這里我們通過查找名字來尋找物體,判斷格子內(nèi)是否有物品存在
//但這樣就只能查找命名為該名字的物體,實(shí)際中我們可以通過tag或者其他屬性來判斷
bool isHaving= grid[i].transform.FindChild("Sword(Clone)");
//如果當(dāng)前格子有物品存在
if(isHaving)
continue;
else if(!isHaving){
//當(dāng)前的空格子
Transform _i=grid[i].gameObject.GetComponent<RectTransform>();
//創(chuàng)建一個(gè)物品對(duì)象
GameObject go=(GameObject)Instantiate(_item);
//把創(chuàng)建的物品對(duì)象添加到格子下
go.transform.SetParent(_i);
//調(diào)整物品的位置位于格子中間
go.transform.localPosition=Vector3.zero;
break;
}
}
}
這段代碼實(shí)現(xiàn)了根據(jù)按順序給每個(gè)格子添加物品,如果我們的格子里面有物品,則會(huì)跳過,查看下一個(gè)格子是否能存放物品。注意,我們暫時(shí)沒有寫背包格子滿載的情況,也沒有判斷物品是否能夠疊加的功能。
那么,當(dāng)一個(gè)背包里的東西雜亂無章的時(shí)候,我們就需要對(duì)其進(jìn)行整理。這里我們先添加一個(gè)函數(shù)。
//在開頭添加一個(gè)存儲(chǔ)列表
public List<GameObject> c_grid;//備用列表,用于存儲(chǔ)需要整理位置的物品
//整理物品函數(shù)
public void ClearUpGrid(){
for (int i = 0; i < grid.Count; i++)
{
bool isHaving= grid[i].transform.FindChild("Sword(Clone)");
if(isHaving){
GameObject go=grid[i].transform.FindChild("Sword(Clone)").gameObject;
c_grid.Add(go);//將當(dāng)前格子內(nèi)的物品存儲(chǔ)到后備列表里面
if(i==grid.Count-1)//當(dāng)所有格子內(nèi)的物品都存放到備用列表中后
grid.Clear();//清空當(dāng)前的背包
}
}
for (int j = 0; j < c_grid.Count; j++)
{
//對(duì)于每個(gè)存儲(chǔ)到后備列表中的物品,我們依次添加到格子列表中
GameObject go=c_grid[j];
go.transform.SetParent(grid[j].transform);
//調(diào)整物品的位置位于格子中間
go.transform.localPosition=Vector3.zero;
//如果所有的物品都添加到了物品格子列表中,則清空后備列表
if(j==c_grid.Count)
c_grid.Clear();
}
}
我們?cè)俳oUpdate函數(shù)添加一句來調(diào)用整理背包的函數(shù)
if(Input.GetKeyDown(KeyCode.Space)){
ClearUpGrid();
}
分析一下
1、查詢每個(gè)背包格子,如果有物品存在,則將該物品復(fù)制一份到備用列表中。
2、當(dāng)查詢到最后一個(gè)背包格子的時(shí)候,則清空當(dāng)前背包格子內(nèi)的物品
3、查詢每個(gè)后備列表中的物品,依次添加到背包格子列表中。
4、當(dāng)后備列表中所有物品都添加完成了,則清空后備列表。
這里也需要注意一下!!我們發(fā)現(xiàn)代碼中再一次對(duì)整個(gè)背包列表進(jìn)行了查詢,與之前添加物品函數(shù)一樣,總共我們就查詢了兩次,其實(shí)我們也可以當(dāng)背包格子中有物品存在的時(shí)候,就將其添加到后備列表中存儲(chǔ),整理的時(shí)候直接清空格子再添加。但是當(dāng)我們背包過大,物品過多的時(shí)候,這樣子不一定好,因?yàn)檫@樣每個(gè)物品就占了2個(gè)資源,如果我們按照這里的函數(shù)來執(zhí)行的話,就只會(huì)在整理的時(shí)候占用資源。我們?cè)谧霰嘲臅r(shí)候,需要考慮如何安排這樣的代碼。這里提供這樣的一個(gè)方法來參考。
另外,我們這里所有的物品都是一個(gè),所以不存在類別關(guān)系,如果要對(duì)物品進(jìn)行分類優(yōu)先排序,我們需要對(duì)每個(gè)物品添加類別屬性,通過類別屬性來自定義優(yōu)先級(jí),在排序的時(shí)候進(jìn)行優(yōu)先排序。
背包整理的功能有了,接下來是對(duì)每個(gè)物品的處理。
首先,我們確定幾項(xiàng)要點(diǎn):
1、物品能夠拖動(dòng)
2、物品拖動(dòng)后的層級(jí)關(guān)系
3、物品拖放的位置
4、拖動(dòng)失敗的處理
其實(shí)這是一個(gè)很簡單的問題,我們事先給gridList添加了一個(gè)空物體GridManager作為父物體,先給我們的物品添加一個(gè)腳本(該腳本偷懶用的別人的)
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class ItemDrag : MonoBehaviour, IPointerDownHandler,IPointerUpHandler,IDragHandler {
// 鼠標(biāo)起點(diǎn)
private Vector2 originalLocalPointerPosition;
// 面板起點(diǎn)
private Vector3 originalPanelLocalPosition;
// 當(dāng)前面板
private RectTransform panelRectTransform;
// 父節(jié)點(diǎn),這個(gè)最好是UI父節(jié)點(diǎn),因?yàn)樗木匦未笮偤檬瞧聊淮笮?
public RectTransform parentRectTransform;
//格子列表
private GameObject gridManager;//在gridlist之上添加的一個(gè)新的空物體作為父節(jié)點(diǎn)
private GameObject originalGrid;//記錄物品拖動(dòng)前的位置
private static int siblingIndex = 0;
void Awake () {
panelRectTransform = transform as RectTransform;
parentRectTransform = GameObject.FindGameObjectWithTag("ScrollRect").GetComponent<RectTransform>() as RectTransform;
gridManager=GameObject.Find("GridManager");
}
// 鼠標(biāo)按下
public void OnPointerDown (PointerEventData data) {
//記錄物品當(dāng)前所在的格子信息
originalGrid=panelRectTransform.parent.gameObject;
//將物品放置在gridManager下,并設(shè)置層級(jí),保證物品顯示在整個(gè)背包層面之上
panelRectTransform.SetParent(gridManager.transform);
siblingIndex++; //層級(jí)管理
panelRectTransform.transform.SetSiblingIndex(siblingIndex);
// 記錄當(dāng)前面板起點(diǎn)
originalPanelLocalPosition = panelRectTransform.localPosition;
// 通過屏幕中的鼠標(biāo)點(diǎn),獲取在父節(jié)點(diǎn)中的鼠標(biāo)點(diǎn)
// parentRectTransform:父節(jié)點(diǎn)
// data.position:當(dāng)前鼠標(biāo)位置
// data.pressEventCamera:當(dāng)前事件的攝像機(jī)
// originalLocalPointerPosition:獲取當(dāng)前鼠標(biāo)起點(diǎn)
RectTransformUtility.ScreenPointToLocalPointInRectangle (parentRectTransform, data.position, data.pressEventCamera, out originalLocalPointerPosition);
}
public void OnPointerUp(PointerEventData data){
// transform.SetParent(gridlist.transform);
RaycastHit2D hit = Physics2D.Raycast(Input.mousePosition,-Vector2.up);
if (hit.collider != null) { //如果射線檢測到的gameobject為grid,就把當(dāng)前物品放在grid節(jié)點(diǎn)下
if(hit.collider.gameObject.tag=="Grid"&&hit.collider.gameObject.transform.FindChild("Sword(Clone)")==null)
transform.parent=hit.transform;
else
{
//如果不是格子或沒有檢測到物體,則將物品放回到原來的格子內(nèi)
transform.parent=originalGrid.transform;
}
}
else
{
transform.parent=originalGrid.transform;
}
//重置物品位置
transform.localPosition=Vector3.zero;
}
// 拖動(dòng)
public void OnDrag (PointerEventData data) {
if (panelRectTransform == null || parentRectTransform == null){
return;
}
Vector2 localPointerPosition;
// 獲取本地鼠標(biāo)位置
if (RectTransformUtility.ScreenPointToLocalPointInRectangle (parentRectTransform, data.position, data.pressEventCamera, out localPointerPosition)) {
// 移動(dòng)位置 = 本地鼠標(biāo)當(dāng)前位置 - 本地鼠標(biāo)起點(diǎn)位置
Vector3 offsetToOriginal = localPointerPosition - originalLocalPointerPosition;
// 當(dāng)前物品位置 = 物品起點(diǎn) + 移動(dòng)位置
panelRectTransform.localPosition = originalPanelLocalPosition + offsetToOriginal;
}
// ClampToWindow ();
}
本段代碼是借用他人的代碼做修改來使用的。我們注意一下代碼。
首先,在腳本開頭我們添加了這樣一句
using UnityEngine.EventSystems;
這是引用ugui的響應(yīng)事件,同時(shí)也在MonoBehaviour后添加了三個(gè)接口。
同時(shí)提一下,因?yàn)镃#不支持多繼承,但我們可以通過實(shí)現(xiàn)接口的方法來做類似多繼承的效果。
public class ItemDrag : MonoBehaviour, IPointerDownHandler,
IPointerUpHandler,IDragHandler
添加后會(huì)發(fā)現(xiàn)報(bào)錯(cuò),提示我們沒有實(shí)現(xiàn)接口。這三個(gè)接口名分別對(duì)應(yīng)以下三個(gè)函數(shù),分別當(dāng)鼠標(biāo)按下時(shí),鼠標(biāo)松開時(shí),以及物品拖動(dòng)時(shí)。
public void OnPointerDown (PointerEventData data) { }
public void OnPointerUp (PointerEventData data) { }
public void OnDrag(PointerEventData data) {
詳細(xì)的功能在代碼里注釋有了。
另外關(guān)于RectTransformUtility.ScreenPointToLocalPointInRectangle,雨松的博客里也有講過,是將一個(gè)坐標(biāo)轉(zhuǎn)換的問題,將鼠標(biāo)的坐標(biāo)轉(zhuǎn)換為UI的坐標(biāo)。這段代碼呢可以去官網(wǎng)查詢一下api了解一下,不僅是背包,包括血條名字的顯示,飆血字效果等都會(huì)用到的相關(guān)方法。
再次注意,光是這樣,我們是檢測不到碰撞的,通過射線碰撞需要對(duì)每個(gè)物品格子添加Collider來檢測,選中物品列表中的每個(gè)grid,添加BoxCollider2D組件,在Scene下調(diào)節(jié)Collider的大小。現(xiàn)在可以檢測了。
別忘了給格子添加Tag為Grid。
運(yùn)行一下,已經(jīng)完成對(duì)背包的整理和對(duì)物品的拖放了。
如果對(duì)本篇內(nèi)容有什么問題或者意見或不對(duì)的地方,請(qǐng)及時(shí)指出,歡迎交流~