背包系统在各类游戏里面很场景的,甚至各类商城也可以套用这套背包系统。主要涉及一个是建立背包的表格布局问题,一个是建立背包之后的,游戏物品的拖拽问题。比起原生的UGUI,NGUI对于物体的拖拽支持比较好,做背包系统利用NGUI效率比较高。下面介绍如何用NGUI完成一个背包系统。如下的图所示:
背包本身能自由拖拽。
假设有4个道具,拖入背包能自动贴到背包的格子里面。物品拖出背包,会自动完成背包的自动排序,基本上也就是模拟游戏里面丢弃道具的过程。物品严重遵循排序,不会出现某个格子镂空,逼死强迫症的情况。当然,物体之间是能够自由交换所在格子的。
具体制作过程如下:
一、场景布置
0、首先导入NGUI,新建一个2D UI,这个具体可以参考《【NGUI】Helloworld》(点击打开链接)。
1、如下图所示,在工程内新建2个tag,一个cell,另一个是obj。
2、所布置的东西如下图所示。Sprite1是白色大块的背包背景,Object1~4是4个表情,模拟游戏道具。Cell9,背包的单元格我们只设置好一个就行了,一会儿用预设动态生成9个格子,毕竟在实际中格子的数量绝对不止9个。在Atlas、Sprite和Flip设置好如图所示合适的背景没甚可说。记得给Cell9打上cell这个tag,object1~4打上obj这个tag。原因和《【Unity3D】利用物体碰撞检测、键盘输入处理完成平衡球游戏》(点击打开链接)差不多,一会儿判断碰撞的时候是要用到tag的。
关键是设置合适的大小。这里Object1~4设置为40x40,Cell9为100x100,Sprite1为342x342。
这里Object1~4的大小应该比Cell要小。而Cell单个单元格,一会儿将会成3x3排列,会形成一个300x300的表格,Sprite1的大小应该还要大一点点。
各位请根据自己的需要,按照如上的原理进行大小的设置。
3、给Sprite1、Object1~4和Cell都打上Box Collider的组件,用于碰撞检测等。Box Collider也是在Unity3D实现拖拽的前提,毕竟,要和鼠标点击处发出的射线先检测下是否发生碰撞。Box Collider的大小应该和物体的大小是一样的。换句话说就是碰撞检测区域应该和物体的大小是一样的。默认的Box Collider的大小是1x1x1需要自己调整,请注意。
4、给Sprite1赋予Drag Object组件,让其可以自由被拖动,Drag Effect选择None没这么卡,Keep Visible勾上的话,那它就不能部分被拖到屏幕之外了。同时赋予Grid组件,让其子物体成网格式排列,设置Cell Width、Cell Height为cell9的大小100x100,这也是每个格子的大小。Column Limit为3,每行3个。对齐方式是:Horizontal。意思是从左到右排完3个格子,再开一行。Vertical则是从上到下排完再向右开一列。Pivot选择Center。
Object1~4一会儿我们自己写脚本来完成,先不用管。
5、新建一个预设Cell,并将cell9拖进去,然后隐藏掉cell9。
这就可以开始脚本的写作了。
二、脚本编写
脚本分2步,一个是完成背包单元格创建的CreateBag.cs,一个是完成物体拖动的ItemDrag.cs。
1、新建一个空物体GameObject赋予完成背包单元格创建的脚本CreateBag.cs如下:
using UnityEngine;
using System.Collections;
public class CreateBag : MonoBehaviour
{
public GameObject cell_prefab;//背包格子预设
public GameObject bag;//父物体,在这个父物体下面添加预设
void Start()
{
for (int i = 0; i < 9; i++)//添加并修改预设的过程,创建9个格子
{
GameObject cell = GameObject.Instantiate(cell_prefab, bag.transform.position, bag.transform.rotation) as GameObject;
cell.name = "cell" + i;
cell.transform.SetParent(bag.transform);
cell.transform.localScale = Vector3.one;//设置缩放比例1,1,1,不然默认的比例非常大
}
}
}
并且指明这个cell_prefab为预设Cell,bag为Sprite1(其实应该改个炫酷点的名字的!-_-!)
那么如下的效果则完成,思想同《【Unity3D】表格》(点击打开链接),这里不再赘述了。由于父物体Sprite1有Grid的存在,这9个格子将会安安稳稳的放到里面。
2、接着是分别赋予给Object1~4如下的ItemDrag.cs:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;//用到容器
public class ItemDrag : UIDragDropItem
{
public GameObject UIRoot;
//重写方法当拖拽释放的时候调用
//该方法用于获取拖拽的物体释放拖拽时,该物体所碰撞的对象
//所以我们前面需要给Cell和Obj都添加Box Collider
protected override void OnDragDropRelease(GameObject surface)//surface在这里是拖拽完成时,碰撞的物体
{
base.OnDragDropRelease(surface);
if (surface.tag == "cell" && transform.parent.tag != "cell")//如果是在格子外拖进格子的
{
GameObject[] cells = GameObject.FindGameObjectsWithTag("cell");//获取所有cell
for (int i = 0; i < cells.Length; i++)//遍历所有cell
{
if (cells[i].GetComponentInChildren<Transform>().childCount == 0)//如果Cell没有子物体
{
transform.parent = cells[i].transform;//将Obj放到碰撞的Cell的子列表中
transform.localPosition = Vector3.zero;//设置Obj的相对于Cell的坐标为0
break;//同时打断循环了,不然物体将走到其它为空的物体那里
}
}
}
else if (surface.tag == "cell" && transform.parent.tag == "cell")//如果是在格子内,拖到其它格子
{
transform.parent = transform.parent;//不让拖,放置地点还是原来的那个格子
transform.localPosition = Vector3.zero;
}
else if (surface.tag == "obj" && surface.transform.parent.tag == "cell" && transform.parent.tag == "cell")
{//如果是格子内,拖到其它在格子内的物品
//这两个格子内的物品交换位置
Transform temp = transform.parent;
transform.parent = surface.transform.parent;
surface.transform.parent = temp;
transform.localPosition = Vector3.zero;
surface.transform.localPosition = Vector3.zero;
//注:这一段也就是经典的temp=a;a=b;b=temp;
}
else if (surface.transform == UIRoot.transform)//如果是在格子内拖到格子外的
{
transform.parent = UIRoot.transform;//物体的父物体变回原来的大UI
//……你还可以在这加些销毁物品,丢弃物品确认的代码什么的
/*下面对格子内的物品重新排序,不然会空掉一个格子*/
List<GameObject> objects_in_cells = new List<GameObject>();//新建一个在背包内所有物品的容器
GameObject[] cells = GameObject.FindGameObjectsWithTag("cell");//获取所有cell
for (int i = 0; i < cells.Length; i++)//遍历所有cell
{
foreach (Transform t in cells[i].transform)//如果cell下面有物品
{
objects_in_cells.Add(t.gameObject);//存到容器里面
}
}
for (int i = 0; i < objects_in_cells.Count; i++)//将容器内原在背包的所有物体,从第一个格子开始,重新摆放
{
objects_in_cells[i].transform.parent = cells[i].transform;
objects_in_cells[i].transform.localPosition = Vector3.zero;
}
}
else
{
//再有一种情况,就是如果物品直接从外面拖到有物品的单元格内,请根据需要补充
}
}
}
物体如果被赋予一个直接继承于UIDragDropItem的物体的话是直接可以拖动。
这里的transform是值被赋予脚本的物体的情况,也就是Object1~4,this.transform中前面的this.被省略而已。
主要是考虑到这个物体几种情况。(1)从背包外开始拖的?从背包内开始拖的?(2)碰到格子?还是碰到物体?还是碰到外面的UI?然后就此分别写相应的处理代码。完成整个背包系统的编写。