NGUI源码分析(四) 关于Anchor

 老版NGUI的Example 1里面提供演示NGUI中使用UIAnchor组件实现GUI对齐功能,新版本的NGUI的Example例子貌似不再使用UIAnchor了,而是演示Widget内置的Anchor的新功能,我学Uinty3d的时候,NGUI已经是比较新的版本了,所以这里分析一下新Anchor的功能.Anchor其实并不复杂,Anchor的意思就是锚点或抛锚,只要设置了UIWidget的Anchor属性,就可以挂到任意节点上面,随着抛锚的位置变化而变化。

  下面是新版Anchor用到的类的介绍:

 一.AnchorPoint 锚点类,UIRect的内部的嵌套类   

    public Transform target;//抛锚目标点
    public Camera targetCam;//目标相机
    public UIRect rect;//目标的UIRect组件

    public float relative = 0f; //相对距离比例
    public int absolute = 0;//锚点跟目标的边距离

    GetSides(Transform relativeTo)) // 获得目标变相对于当前的局部矩形范围

二.UIRect 抽象类,Widget的父类,所有UI组件的祖宗 UIRect里面有4跟Achor相关的关键属性:
   

     public AnchorPoint leftAnchor = new AnchorPoint();// 左边锚点
     public AnchorPoint rightAnchor = new AnchorPoint(1f);// 右边锚点
     public AnchorPoint bottomAnchor = new AnchorPoint();// 底部锚点
     public AnchorPoint topAnchor = new AnchorPoint(1f);// 顶部锚点

     //锚点更新类型,决定什么时候执行锚点逻辑OnUpdate表示每帧执行,OnEnable表示脚本激活的时候执行
     public AnchorUpdate updateAnchors = AnchorUpdate.OnUpdate;

Update () 方法:  

 //如果锚点的更新类型是OnUpdate 
if (updateAnchors == AnchorUpdate.OnUpdate || mUpdateAnchors)
 {
        mUpdateFrame = frame;
        mUpdateAnchors = false; 

        bool anchored = false;
        //如果锚点目标存在 则执行目标的Update方法
        if (leftAnchor.target)
        {
             anchored = true;
             if (leftAnchor.rect != null && leftAnchor.rect.mUpdateFrame != frame)
             leftAnchor.rect.Update();

        }

        if (bottomAnchor.target)
        {
             anchored = true;
             if (bottomAnchor.rect != null && bottomAnchor.rect.mUpdateFrame != frame)
             bottomAnchor.rect.Update();
        }

        ......中间省略

        // 更新锚点对齐逻辑
        if (anchored) OnAnchor();

   }

  从上面的方法可以看出,当锚点更新类型是OnUpdate时,会先调用目标的Update方法,如果自顶向下都有锚点且都是OnUpdate类型,就会一直递归至顶层开始顺序调用OnAnchor,而且每帧都会调用,这会很消耗cpu,非常蛋疼,所以在AnchorUpdate类型最好不要设置为OnUpdate ,应该设置为OnEnable,如下图:

 如果锚点的目标也有锚点的话,比如A的锚点目标是B,B的锚点是C,如果存在这种多层锚点的情况,设置为OnEnable可能会有问题,所以尽量不要出现多层的情况.

 

三.UIWidget类 实现锚点的具体逻辑

  OnAnchor()方法 更新锚点相关逻辑

  部分代码: 

    //如果4个顶点绑定到同一个对象上
    if (leftAnchor.target == bottomAnchor.target &&
    leftAnchor.target == rightAnchor.target &&
    leftAnchor.target == topAnchor.target)
    {
        //获得父对象相对于当前对象的4条边的位置,顺序是左,上,右,下
        Vector3[] sides = leftAnchor.GetSides(parent);
        if (sides != null)
        {
            // 左边位置 = 父对象左边位置 + 左边边距  (leftAnchor的relative为0,lerp运算得出结果是sides[0].x)
            lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute;

            // 右边位置 = 父对象右边位置  + 右边边距  (rightAnchor的relative为1,Lerp运算得出结果是sides[2].x)
            rt = NGUIMath.Lerp(sides[0].x, sides[2].x, rightAnchor.relative) + rightAnchor.absolute;

            // 底部位置 = 父对象底部位置  + 底部边距  (bottomAnchor的relative为0,Lerp运算得出结果是sides[3].y)
            bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute;

               // 顶部位置 = 父对象顶部位置  + 顶部边距  (topAnchor的relative为1,Lerp运算得出结果是sides[1].y)
             tt = NGUIMath.Lerp(sides[3].y, sides[1].y, topAnchor.relative) + topAnchor.absolute;
        }
    }

  //relative并不一定是固定的,如果如果目标的锚点位置设置为center的话,relative会设置为0.5



  ......



  //计算新的位置,和大小  

   Vector3 newPos = new Vector3(Mathf.Lerp(lt, rt, pvt.x), Mathf.Lerp(bt, tt, pvt.y), pos.z);

   int w = Mathf.FloorToInt(rt - lt + 0.5f);

   int h = Mathf.FloorToInt(tt - bt + 0.5f);

   cachedTransform.localPosition = newPos;

   mWidth = w;

   mHeight = h;

 

  总结:当设置了锚点目标的时候,锚点对象就的空间属性就不仅由自己决定了,对象会通过父节点的位置和大小变化情况,而更新自己的位置和大小。Anchor类型最好设置为OnEnable,尽量不要出现多层锚点.

  原文地址:http://www.cnblogs.com/rocky300/articles/4681193.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值