12 第1页 | 共2 页下一页
返回列表 发新帖
查看: 12530|回复: 13

[教程] NGUI中窗体移动区域的限制

[复制链接]

21

主题

7

听众

651

积分

初级设计师

Rank: 3Rank: 3

纳金币
0
精华
3

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2013-5-8 23:54:04 |显示全部楼层
          不管是开发游戏还是做虚拟现实,我想您都不会对NGUI感到陌生,但由于其不是Unity科技自己原生的产品,所以其必定不会做得很完善,至少有很多东西需要自己研究其源代码之后,才能按功能写出自己想要的东西。这次就让我们尝试一下解决一个必须解决的,而且还有点棘手的功能吧!

        打开NGUI的第6个例子,它为我们提供了一个很炫的功能,窗口拖拽,而且还带有加速度和摇摆功能。可是,这里面有一个非常严重的Bug:窗口很容易会跑出屏幕,而且你无法再将其拖到屏幕中。不知道NGUI官方为什么不提供此功能,还是有什么其他原因,但绝不是他们做不到,因为我都做到了,或许是他们想给我们一点点成就感,故意做成这样的吧!好了,我们入正题吧!

        我们可以先尝试一下阅读官方的UIDragObject这个脚本,至少要将拖拽窗口的基本部分给弄清楚。在此基础上,我们就可以利用一些方法来限制窗体拖拽的区域了。下面我将我的代码列出:

using UnityEngine;
using System.Collections;

public class TestDrag : MonoBehaviour {

    public Transform target;//拖拽的窗体的根节点
    public Vector3 scale = Vector3.one;//希望拖拽时窗体移动随着拖拽的方向移动的幅度的一个决定因素
    //public float padding = 0.1f;

    Plane mPlane;//参考平面
    bool mPressed = false;//是否按下
    Vector3 mLastPos = Vector3.zero;//上一次NGUI摄像机沿着屏幕上的鼠标点方向发射射线与参考平面的交点

    void OnPress(bool pressed)
    {
        mPressed = pressed;
        if(pressed)//如果鼠标被按下
        {
            mLastPos = UICamera.lastHit.point;
            Transform trans = UICamera.currentCamera.transform;
            mPlane = new Plane(trans.rotation * Vector3.back,mLastPos);//注意,此平面的方向与NGUI正交摄像机裁剪面平行
        }
    }

    void OnDrag(Vector2 delta)
    {
        Ray ray = UICamera.currentCamera.ScreenPointToRay(UICamera.currentTouch.pos);//发射射线
        float dist = 0f;

        if(mPlane.Raycast(ray,out dist))//与参考平面相交
        {
            Vector3 currentPos = ray.GetPoint(dist);//取得相交点
            Vector3 offset = currentPos - mLastPos;//计算鼠标单次拖拽(OnDrag是由UICamera脚本中的Update函数的某个子程序在恰当的时候利用SendMessage发送过来的)移动的步长分量
            mLastPos = currentPos;//更新mLastPos为当前currentPos,一边下一次的计算

            if (offset.x != 0f || offset.y != 0f)
            {
                offset = target.InverseTransformDirection(offset);//将计算出的鼠标移动步长转为根节点的坐标系下的分量
                offset.Scale(scale);//我们可以在检视面板中修改此scale的值,可以看到不同的效果
                offset = target.TransformDirection(offset);//再转回世界坐标系
            }

            Vector3 pos = target.position;
            pos += offset;//先将target的预期位置给计算出来。下面才是最重要的限制窗体的部分:

            Camera curcam = UICamera.currentCamera;

            Bounds bs = NGUIMath.CalculateAbsoluteWidgetBounds(target);//根据根节点计算窗体在世界坐标系中的大小
/*
首先解释一下我的代码的功能:限制窗体在屏幕里面。又由于此窗体的坐标在其中心,所以我只需让其中心点在一个矩形区域里面就行了,但是:此中心点必须遵循以下规则,否则就会有部分或全部飞出屏幕。下面看图:
1.png


2.png


这两张图显示的两个位置正是此窗口的两个临界位置,即左下角的最远坐标与右上角的最远坐标(好像有些绕口,就暂且先这样叫着吧!)。于是我们得出一个结论:
1.窗口在屏幕上的横坐标必须大于或等于窗体的宽度的二分之一,小于或等于屏幕长度减去窗体宽度的二分之一。
2.窗口在屏幕上的纵坐标坐标必须大于或等于窗体的高度的二分之一,小于或等于屏幕高度减去窗体高度的二分之一。
所以以下代码就顺理成章了!
*/
            Vector3 _lb =new Vector3(target.position.x - bs.size.x/2,target.position.y-bs.size.y/2,0f);//求出窗体的左下角的世界坐标
            Vector3 lb = curcam.WorldToScreenPoint(_lb);//转屏幕坐标
            Vector3 _rt = new Vector3(target.position.x+bs.size.x/2,target.position.y+bs.size.y/2,0f);//求出窗体的右上角世界坐标
            Vector3 rt = curcam.WorldToScreenPoint(_rt);//转屏幕坐标

            float width = rt.x - lb.x;//求出窗体的屏幕坐标系宽度
            float height = rt.y - lb.y;//求出窗体的屏幕坐标系高度

            Vector3 ClampVector1 = new Vector3(width / 2, height / 2, 0f);//窗体的左下角的最远点(相对于屏幕)
            Vector3 ClampVector2 = new Vector3(Screen.width - width / 2, Screen.height - height / 2, 0f);//窗体的右上角的最远点(相对于屏幕)

            Vector3 scrPos = curcam.WorldToScreenPoint(pos);//将pos转为屏幕坐标
            //将pos的屏幕坐标限制在屏幕的两个最远点范围之内
            scrPos.x = Mathf.Clamp(scrPos.x, ClampVector1.x, ClampVector2.x);
            scrPos.y = Mathf.Clamp(scrPos.y, ClampVector1.y, ClampVector2.y);

            然后将限制后的pos屏幕坐标再转为世界坐标,赋值给target。

            target.position = curcam.ScreenToWorldPoint(scrPos);


        }

    }
}


首先我先声明一下,如果您对世界坐标与摄像机坐标还有局部坐标系与世界坐标系的概念不清楚的话,先补补这方面的知识吧,以免打击自己的信心。其实这里面没用到很高深的数学知识,那些引擎都封装好了,我们直接调用就行了。我的代码注释已经写得很详细了,如果大家有什么疑问的话,请给我留言。运行一下,功能以大功告成,我就不附上截图了。不早了,下次见!














回复

使用道具 举报

9903

主题

126

听众

7万

积分

首席设计师

Rank: 8Rank: 8

纳金币
53448
精华
316

最佳新人 热心会员 灌水之王 活跃会员 突出贡献 荣誉管理 论坛元老

发表于 2013-5-9 01:02:41 |显示全部楼层
谢谢楼主的NGUI教程,值得好好研究一下!
回复

使用道具 举报

1

主题

5

听众

2194

积分

中级设计师

Rank: 5Rank: 5

纳金币
7
精华
0

活跃会员

发表于 2013-5-9 10:11:34 |显示全部楼层
非常感谢!正需要。主要是觉得楼主的代码注释是我看到过最详细完美的,阅读起来非常舒服。由衷的佩服!希望能交个朋友。
回复

使用道具 举报

6

主题

1

听众

1473

积分

助理设计师

Rank: 4

纳金币
126
精华
1
发表于 2013-5-9 10:13:20 |显示全部楼层
楼主费心了!~~真心感谢!~~~
回复

使用道具 举报

ku 智囊团   

89

主题

2

听众

5万

积分

首席设计师

Rank: 8Rank: 8

纳金币
25
精华
1

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2013-5-9 16:08:03 |显示全部楼层
真是不错的教程,谢谢
回复

使用道具 举报

0

主题

2

听众

6150

积分

高级设计师

Rank: 6Rank: 6

纳金币
62
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2013-5-10 20:18:05 |显示全部楼层
谢谢楼主的NGUI教程,值得好好研究一下!
回复

使用道具 举报

2

主题

0

听众

337

积分

设计实习生

Rank: 2

纳金币
3
精华
0

最佳新人

发表于 2013-5-12 18:06:48 |显示全部楼层
正在研究NGUI ,谢谢
回复

使用道具 举报

2317

主题

54

听众

2万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
20645
精华
62

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2013-5-12 18:52:45 |显示全部楼层
楼主对NGUI比较了解,请多多分享这方面的教程,谢谢
回复

使用道具 举报

1

主题

1

听众

731

积分

初级设计师

Rank: 3Rank: 3

纳金币
7
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2013-5-14 14:10:06 |显示全部楼层
不错这个可以学习一下
回复

使用道具 举报

0

主题

5

听众

701

积分

初级设计师

Rank: 3Rank: 3

纳金币
45
精华
0

最佳新人

发表于 2013-6-3 08:59:18 |显示全部楼层
感谢分享!
回复

使用道具 举报

12 第1页 | 共2 页下一页
返回列表 发新帖
您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|纳金网 ( 闽ICP备08008928号

GMT+8, 2024-3-29 03:58 , Processed in 0.101806 second(s), 35 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部