物品拖拽[u3d_rpg游戏开发之物品管理(五)]

来源:互联网 发布:淘宝开学季什么时候 编辑:程序博客网 时间:2024/05/05 08:42
之前在网上找了好多方法,总是不适合自己,要么就是不能用之类的,我用的是2017的版本,在这里"全面"解析一下.首先创建一个脚本InventoryItem挂在需要拖拽的Image上,这里的Image是另一个Image的子物体,因为子的Imget我用来显示物品的相应物品的图片,而父的Image我用来显示格子的图片,这样就有了格子里面的物品,可以用来拖动,而且,格子的标签我设为"InventoryItemGird",拖拽结束时就可以通过标签判断是不是格子.

1. 继承接口

u3d的ugui有相应的接口来方便我们实现拖拽功能,除了本来就需要继承的MonoBehaviour外,我们写的脚本再继承三个接口,还得先导入头文件 UnityEngine.EventSystems;

using UnityEngine.EventSystems;public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler{}

2. 实现抽象方法

抽象方法同样是三个

using UnityEngine.EventSystems;public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler{    /// <summary>    /// 实现接口的OnBeginDrag方法,处理开始拖拽时要做的事情    /// </summary>    /// <param name="eventData"></param>    public void OnBeginDrag(PointerEventData eventData)    {    }    /// <summary>    /// 实现接口的OnDrag方法,处理拖动中要做的事情    /// </summary>    /// <param name="eventData"></param>    public void OnDrag(PointerEventData eventData)    {    }    /// <summary>    /// 实现接口的OnEndDrag方法,处理结束时的方法    /// </summary>    /// <param name="eventData"></param>    public void OnEndDrag(PointerEventData eventData)    {    }}

3. 去遮挡

拖拽的时候我们会遇到一个问题,就是拖拽物品的父物体是是一个格子,而格子又位于不同的层,如图:

项目来源于siki的黑暗之光

我把格子取名为Inventory-Item-gird-00至Inventory-Item-gird-34共20个格子,这里的第一个格子Inventory-Item-gird-00在其他格子的后面,ui物体越靠下就越显示在摄像机的越靠外层,所以,当Inventory-Item-gird-00下的物品拖拽到Inventory-Item-gird-34格子的位置时,会被其遮挡我们这里就在最下面再创建一个隐形而不能用画布:Canvas,当我们需要拖动物品时就将该物品的父物体设置为Canvas,拖拽物品就会位于最外层,不会被任何ui遮挡,需要注意的是,当拖拽结束我们需要将父物体设置回来,不然会导致原来格子下的Image没了.
    private Transform imageParent;//拖拽物品的父物体    private Canvas imageFather;//获取最外层的画布    private Vector2 drugingRectTransform;//正在被拖拽的图片的原始位置    public void OnBeginDrag(PointerEventData eventData)    {        imageFather = GameObject.Find("Canvas").GetComponent<Canvas>();//获取在最外层的格子        drugingRectTransform = myImage.rectTransform.position;//获取初始位置        imageParent = transform.parent;//获取父物体的transform        transform.SetParent(imageFather.transform);//将物品放在最外层    }    /// <summary>    /// 将一个物体放在另一个物体下    /// </summary>    /// <param name="child">作为子物体</param>    /// <param name="parent">作为父物体</param>    private void SetParentAndPosition(Transform child, Transform parent)//将child放到parent下作为子物体    {        child.SetParent(parent);        child.position = parent.position;    }

4. 实现穿透

当我们拖拽结束的时候,我们需要判断鼠标停留的位置是否是一个格子,如果是格子才有可能吧物品放进去,如果不是就要让物品还原位置.那么问题来了,拖拽物品始终在鼠标位置,也就是说判断鼠标停留位置的时候,射线判断到的永远是我们正在拖拽的物品.所以我们我让我们在拖拽中的物品不被鼠标射线检测到,也就是使物品实现穿透.这个时候我们的物品位于Canvas下,所以我们只需要让Canvas实现穿透就行了,先给其添加一个组件:Canvas Group

CanvasGroup组件

这个组件下的Blocks Raycasts选项就表示所在物体是否会遮挡鼠标射线,这时我们就可以通过代码来控制其开关了.
    /// <summary>    /// 实现接口的OnBeginDrag方法,处理开始拖拽时要做的事情    /// </summary>    /// <param name="eventData"></param>    public void OnBeginDrag(PointerEventData eventData)    {        imageFather = GameObject.Find("Canvas").GetComponent<Canvas>();//获取在最外层的画布        drugingRectTransform = myImage.rectTransform.position;//获取初始位置        imageParent = transform.parent;//获取父物体的transform        transform.SetParent(imageFather.transform);//将物品放在最外层        GameObject.Find("Canvas").GetComponent<CanvasGroup>().blocksRaycasts = false;//射线可以穿透物体    }
至此开始拖拽物品的方法完全实现

5. 物品置入

物品置入前还有一个拖拽方法需要实现:
    /// <summary>    /// 实现接口的OnDrag方法,处理拖动中要做的事情    /// </summary>    /// <param name="eventData"></param>    public void OnDrag(PointerEventData eventData)    {        this.GetComponent<RectTransform>().position=Input.mousePosition;//鼠标左键按住拖拽的时候,物体跟着鼠标移动    }
最后就是拖拽结束的方法了.最后一个交换物品的方法可以不用管,我写给自己看的,所有的思路已经很清晰了.
    /// <summary>    /// 实现接口的OnEndDrag方法,处理结束时的方法    /// </summary>    /// <param name="eventData"></param>    public void OnEndDrag(PointerEventData eventData)    {        SetParentAndPosition(transform, imageParent);//复原父物体        transform.position = drugingRectTransform;//原图归位        ExchangeOfGoods(eventData);//交换物品        GameObject.Find("Canvas").GetComponent<CanvasGroup>().blocksRaycasts = false;//ui事件穿透:置为不能穿透    }    /// <summary>    /// 格子内的物品进行交换    /// </summary>    /// <param name="eventData">参数PointerEventData用于获取鼠标终点位置</param>    private void ExchangeOfGoods(PointerEventData eventData)    {        GameObject go = eventData.pointerCurrentRaycast.gameObject;//获取鼠标下的物体        //如果物体不为空且该物体为物品格子        if (go != null && go.tag.Equals("InventoryItemGird"))        {            InventoryItemGrid fatherOfThis = this.GetComponentInParent<InventoryItemGrid>();//获取起点格子的脚本            InventoryItemGrid fatherOfThat = go.GetComponent<InventoryItemGrid>();//获取终点格子的脚本            //如果格子为空格子            if (fatherOfThat.id == 0)            {                fatherOfThat.SetId(fatherOfThis.id,fatherOfThis.num);//向终点格子储存物品                fatherOfThis.ClearInfo();//清空原来格子信息            }            else//两格子交换物品            {                InventoryItemGrid temp = fatherOfThat;//临时储存终点格子的脚本                fatherOfThat.SetId(fatherOfThis.id, fatherOfThis.num);//向终点格子储存物品                fatherOfThis.SetId(temp.id, temp.num);//向起点格子储存物品            }        }    }
原创粉丝点击