NGUI学习笔记(五): 背包拖拽效果

来源:互联网 发布:ps cs6 for mac破解版 编辑:程序博客网 时间:2024/06/08 06:11

关于拖拽以及交换物品,NGUI有很贴心的UIDragDropItem以及UIContainer两个内置脚本.基本功能已经实现,使用者需要做的就是根据自己的需求Override相应的虚函数.

先展示一下今天要实现的目标效果:
目标效果

文字说明:
1.我希望被交换的物品在改变自己的位置时,有一段朝目标格子移动的位移动画,而不是很死板的瞬间改变position.
2.如图中所示,当前物品拖拽到了哪个格子上,则对应的格子会有相应的图形状态变化来提示用户.

场景截图:

场景截图

下面上脚本:

using UnityEngine;using System.Collections;using System.Collections.Generic;public class ExchangableItem : UIDragDropItem {    /// <summary>    /// 当前拖拽悬停在的那个Container;    /// </summary>    Transform dragOverContainer;    protected override void Start ()    {        base.Start ();    }    protected override void OnDragStart ()    {        base.OnDragStart ();        //每次开始拖拽时都将正在悬停的容器格子置为自身父物体,保证有一个正确的初始值        dragOverContainer = transform.parent;    }    protected override void OnPress (bool isPressed)    {        base.OnPress (isPressed);        //当对某个Item按下时则其UIWidget(包括所有子物体)的深度都+2,为什么不是+1呢?因为被交换的Item会采用+1,这里要比被交换Item的深度高1.        //反之当按压结束时还原深度        List<UIWidget> widgets = new List<UIWidget>(transform.GetComponentsInChildren<UIWidget>());        if (isPressed) {            transform.GetComponent<UIWidget> ().depth+=2;            foreach (UIWidget widget in widgets) {                widget.depth+=2;            }        } else {            transform.GetComponent<UIWidget> ().depth-=2;            foreach (UIWidget widget in widgets) {                widget.depth-=2;            }        }    }    protected override void OnDragDropMove (Vector2 delta)    {        base.OnDragDropMove (delta);        Ray ray = UICamera.mainCamera.ScreenPointToRay (Input.mousePosition);        RaycastHit hit;        //当拖拽时,用射线检测Layer为Container的Collider,以此来获得被拖拽物在哪个容器的上方.        if (Physics.Raycast (ray, out hit, Mathf.Infinity, (1 << LayerMask.NameToLayer ("Container")))) {            if (hit.collider.transform != dragOverContainer) {                UIPlayTween playTw = dragOverContainer.gameObject.GetComponent<UIPlayTween> ();                playTw.resetOnPlay = true;                //这里要吐槽一下NGUI内置的Tween功能,我本来想让处于拖拽物下方的容器做循环的颜色变换动画,                //但是发现这样根本没法用UIPlayTween将其停止,                //这也算是UIPlayTween的一大缺陷,                //相比于DoTween简直弱爆了!                //这里的反向播放是为了还原到原有状态.                playTw.Play (false);                print (dragOverContainer.name);                dragOverContainer = hit.collider.transform;                print (dragOverContainer.name);                playTw = dragOverContainer.gameObject.GetComponent<UIPlayTween> ();                playTw.resetOnPlay = true;                playTw.Play (true);            }        }    }    protected override void OnDragEnd ()    {        base.OnDragEnd ();        //拖拽结束时也要动画进行反向播放来达到还原状态的目的        UIPlayTween playTw = dragOverContainer.gameObject.GetComponent<UIPlayTween> ();        playTw.resetOnPlay = true;        playTw.Play (false);    }    protected override void OnDragDropRelease (GameObject surface)    {        if (!cloneOnDrag)        {            // Re-enable the collider            if (mButton != null) mButton.isEnabled = true;            else if (mCollider != null) mCollider.enabled = true;            else if (mCollider2D != null) mCollider2D.enabled = true;            // Is there a droppable container?            UIDragDropContainer container = surface ? NGUITools.FindInParents<UIDragDropContainer>(surface) : null;            if (container != null)            {                   //当这个容器是自身所在容器时直接还原                if (container.transform == mParent) {                    transform.localPosition = Vector3.zero;                    mParent.localScale = Vector3.one;                    return;                }                // Container found -- parent this object to the container                Transform containerTrans =  (container.reparentTarget != null) ? container.reparentTarget : container.transform;                if (containerTrans.childCount > 0)                 {                    Transform parent = mParent;                    Transform child = containerTrans.GetChild (0);                    List<UIWidget> widgets = new List<UIWidget>(child.GetComponentsInChildren<UIWidget>());                    child.GetComponent<UIWidget> ().depth++;                    foreach (UIWidget widget in widgets) {                        widget.depth++;                    }                    TweenPosition twpos = child.gameObject.AddComponent<TweenPosition> ();                    //将mParent中心点坐标转换为世界坐标,其实可以直接用mParent的世界坐标                    Vector3 worldPos = mParent.TransformPoint (Vector3.zero);                    //将mParent的世界坐标转换到child的本地坐标系中                    Vector3 localPos = child.InverseTransformPoint (worldPos);                    //TweenPosition动画是对物体的本地坐标进行改变,这一点很重要,很重要,很重要!!!                    twpos.to.Set (localPos.x, localPos.y, localPos.z);                    twpos.duration = 0.2f;                    //这里要再次吐槽NGUI自带的Tween,连在代码内指定一个动画曲线都这么困难!还要自己指定关键帧                    //DoTween里直接封装好了非常多且使用的动画曲线,你需要通过枚举类型指定就可以了                    //twpos.animationCurve = new AnimationCurve(new Keyframe[]());                    twpos.Play ();                    twpos.AddOnFinished                    (                        //新建一个代理                        new EventDelegate (                            //Lambada表达式申明匿名函数                            () => {                                             child.GetComponent<UIWidget> ().depth--;                                foreach (UIWidget widget in widgets) {                                    widget.depth--;                                }                                //当动画播放完毕时改变父物体为拖拽物之前的父物体                                //注意一定提前记录被拽物之前的父物体,因为这里相当于协同程序结束时的回调                                child.parent = parent;                                child.localPosition = Vector3.zero;                                child.localScale = Vector3.one;                            }                        )                    );                    mTrans.parent = containerTrans;                    mTrans.localPosition = Vector3.zero;                    mTrans.localScale = Vector3.one;                }            }            else            {                // No valid container under the mouse -- revert the item's parent                mTrans.parent = mParent;                mTrans.localPosition = Vector3.zero;                mTrans.localScale = Vector3.one;            }            // Update the grid and table references            mParent = mTrans.parent;            mGrid = NGUITools.FindInParents<UIGrid>(mParent);            mTable = NGUITools.FindInParents<UITable>(mParent);            // Re-enable the drag scroll view script            if (mDragScrollView != null)                StartCoroutine(EnableDragScrollView());            // Notify the widgets that the parent has changed            NGUITools.MarkParentAsChanged(gameObject);            if (mTable != null) mTable.repositionNow = true;            if (mGrid != null) mGrid.repositionNow = true;            // We're now done            OnDragDropEnd();        }        else NGUITools.Destroy(gameObject);    }}

最后我要补充一下当我做射线检测时,发现只要运行我原本设置好的Layer就会自动发生改变,这是为什么?
经过几次尝试终于发现了原因:
原因
英文的Log信息已经给出了答案,这小细节还挺麻烦的,哈!

原创粉丝点击