【NGUI】背包

背包系统在各类游戏里面很场景的,甚至各类商城也可以套用这套背包系统。主要涉及一个是建立背包的表格布局问题,一个是建立背包之后的,游戏物品的拖拽问题。比起原生的UGUI,NGUI对于物体的拖拽支持比较好,做背包系统利用NGUI效率比较高。下面介绍如何用NGUI完成一个背包系统。如下的图所示:

【NGUI】背包_第1张图片

背包本身能自由拖拽。

假设有4个道具,拖入背包能自动贴到背包的格子里面。物品拖出背包,会自动完成背包的自动排序,基本上也就是模拟游戏里面丢弃道具的过程。物品严重遵循排序,不会出现某个格子镂空,逼死强迫症的情况。当然,物体之间是能够自由交换所在格子的。

具体制作过程如下:

一、场景布置

0、首先导入NGUI,新建一个2D UI,这个具体可以参考《【NGUI】Helloworld》(点击打开链接)。

1、如下图所示,在工程内新建2个tag,一个cell,另一个是obj。

【NGUI】背包_第2张图片

2、所布置的东西如下图所示。Sprite1是白色大块的背包背景,Object1~4是4个表情,模拟游戏道具。Cell9,背包的单元格我们只设置好一个就行了,一会儿用预设动态生成9个格子,毕竟在实际中格子的数量绝对不止9个。在Atlas、Sprite和Flip设置好如图所示合适的背景没甚可说。记得给Cell9打上cell这个tag,object1~4打上obj这个tag。原因和《【Unity3D】利用物体碰撞检测、键盘输入处理完成平衡球游戏》(点击打开链接)差不多,一会儿判断碰撞的时候是要用到tag的。

【NGUI】背包_第3张图片

关键是设置合适的大小。这里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需要自己调整,请注意。

【NGUI】背包_第4张图片

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。

【NGUI】背包_第5张图片

Object1~4一会儿我们自己写脚本来完成,先不用管。

5、新建一个预设Cell,并将cell9拖进去,然后隐藏掉cell9。

【NGUI】背包_第6张图片

这就可以开始脚本的写作了。

二、脚本编写

脚本分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(其实应该改个炫酷点的名字的!-_-!)

【NGUI】背包_第7张图片

那么如下的效果则完成,思想同《【Unity3D】表格》(点击打开链接),这里不再赘述了。由于父物体Sprite1有Grid的存在,这9个格子将会安安稳稳的放到里面。

【NGUI】背包_第8张图片

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().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 objects_in_cells = new List();//新建一个在背包内所有物品的容器
            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?

然后就此分别写相应的处理代码。完成整个背包系统的编写。

你可能感兴趣的:(Unity3D)