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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值