NGUI锚定系统:UIAnchor\UIRect

一、UIAnchor:非UIRect对象的坐标适配。
NGUI的Anchor锚点:
public enum Side
{
   BottomLeft,
   Left,
   TopLeft,
   Top,
   TopRight,
   Right,
   BottomRight,
   Bottom,
   Center,
}
锚定坐标的计算:mRect{x = xMin, y = yMin, width = xMax - xMin, height = yMax - yMin}.
1.计算出mRect,获取mRect的中心点坐标。
2.根据side值获取对象将使用的基础锚定坐标v。
3.根据pixelOffset、relativeOffset等预设的偏移信息修正坐标v。
4.坐标转换,赋值。该功能无法修改渲染对象的大小。
//只截取核心代码段
float cx = (mRect.xMin + mRect.xMax) * 0.5f;//锚定目标的中心点,mRect锚定目标的信息。
float cy = (mRect.yMin + mRect.yMax) * 0.5f;
Vector3 v = new Vector3(cx, cy, 0f);//当前对象将被设置的坐标。
 
if (side != Side.Center)
{
   if (side == Side.Right || side == Side.TopRight || side == Side.BottomRight) v.x = mRect.xMax;
   else if (side == Side.Top || side == Side.Center || side == Side.Bottom) v.x = cx;
   else v.x = mRect.xMin;
 
   if (side == Side.Top || side == Side.TopRight || side == Side.TopLeft) v.y = mRect.yMax;
   else if (side == Side.Left || side == Side.Center || side == Side.Right) v.y = cy;
   else v.y = mRect.yMin;
}
 
float width = mRect.width;
float height = mRect.height;
 
v.x += pixelOffset.x + relativeOffset.x * width;//偏移值,pixelOffset绝对像素,relativeOffset相对比例
v.y += pixelOffset.y + relativeOffset.y * height;
 
v.x = Mathf.Round(v.x);
v.y = Mathf.Round(v.y);
 
//坐标转换
if (pc != null)
{
   v = pc.cachedTransform.TransformPoint(v);
}
else if (container != null)
{
   Transform t = container.transform.parent;
   if (t != null) v = t.TransformPoint(v);
}
v.z = mTrans.position.z;
if (mTrans.position != v) mTrans.position = v;//坐标设置
举个例子:在下面的图片中,小图锚定到大图中,铆钉信息如下:
小图坐标计算:这边的tf坐标根据Side的枚举值得出。
X = tf.x + relativeOffset.x * width + pixelOffset.x = -640 + 0.2*1280 + 100 = -284
Y = tf.y + relativeOffset.y * height+ pixelOffset.y =360 +(-0.2)*720 + (-100 )= 116
二、UIRect:渲染对象基类。
1、AnchorPoint:锚定功能。
保存了锚点相关的target\relative\absolute等核心属性,以及封装了部分公共方法。
2、重要属性。
  1. leftAnchor/rightAnchor/bottomAnchor/topAnchor:四个角的锚点。
默认left\bottom的relative为0,top/right的relative为1.
Inspector中设置的是absolute值。
  1. mUpdateFrame:当前更新帧,避免重复更新。
  1. mUpdateAnchors:标记当前帧是否要更新锚点。
  1. cachedGameObject/cachedTransform/parent/root:缓存相关节点。
3.重要方法。
  1. UpdateAnchorsInternal():更新锚点信息,包括更新锚点target的rect,该方法在Mono的Update方法执行。而NGUI的渲染逻辑在LateUpdate执行,确保了在渲染时获取到最新的锚点信息及最终的渲染范围。
  1. GetSides:虚方法,用于获取四条边相对于某个Transform的坐标,具体实现在子类(UIWidget,UILabel,UIPanel)。例如UIWidget实际取到的是:
/// <summary>
/// Get the sides of the rectangle relative to the specified transform.
/// The order is left, top, right, bottom.
/// </summary>
 
public override Vector3[] GetSides (Transform relativeTo)
{
   Vector2 offset = pivotOffset;
 
   float x0 = -offset.x * mWidth;
   float y0 = -offset.y * mHeight;
   float x1 = x0 + mWidth;
   float y1 = y0 + mHeight;
   float cx = (x0 + x1) * 0.5f;
   float cy = (y0 + y1) * 0.5f;
 
   Transform trans = cachedTransform;
   mCorners[0] = trans.TransformPoint(x0, cy, 0f);
   mCorners[1] = trans.TransformPoint(cx, y1, 0f);
   mCorners[2] = trans.TransformPoint(x1, cy, 0f);
   mCorners[3] = trans.TransformPoint(cx, y0, 0f);
 
   if (relativeTo != null)
   {
      for (int i = 0; i < 4; ++i)
         mCorners[i] = relativeTo.InverseTransformPoint(mCorners[i]);
   }
   return mCorners;
}
  1. OnAnchor:虚方法,核心方法。定义如何根据锚点信息更新自身位置及大小,具体实现在子类(UIWidget,UILabel,UIPanel),在UpdateAnchorsInternal里调用。以UIWidget为例:
protected override void OnAnchor ()
{
   float lt, bt, rt, tt;
   Transform trans = cachedTransform;
   Transform parent = trans.parent;
   Vector3 pos = trans.localPosition;
   Vector2 pvt = pivotOffset;
 
   // Attempt to fast-path if all anchors match
   if (leftAnchor.target == bottomAnchor.target &&
      leftAnchor.target == rightAnchor.target &&
      leftAnchor.target == topAnchor.target)
   {
      Vector3[] sides = leftAnchor.GetSides(parent);
 
      if (sides != null)
      {
         lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute;
         rt = NGUIMath.Lerp(sides[0].x, sides[2].x, rightAnchor.relative) + rightAnchor.absolute;
         bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute;
         tt = NGUIMath.Lerp(sides[3].y, sides[1].y, topAnchor.relative) + topAnchor.absolute;
      }
   }
 
   // Calculate the new position, width and height
   Vector3 newPos = new Vector3(Mathf.Lerp(lt, rt, pvt.x), Mathf.Lerp(bt, tt, pvt.y), pos.z);
   newPos.x = Mathf.Round(newPos.x);
   newPos.y = Mathf.Round(newPos.y);
 
   int w = Mathf.FloorToInt(rt - lt + 0.5f);
   int h = Mathf.FloorToInt(tt - bt + 0.5f);
 
   // Maintain the aspect ratio if requested and possible
   if (keepAspectRatio != AspectRatioSource.Free && aspectRatio != 0f)
   {
      if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
      {
         w = Mathf.RoundToInt(h * aspectRatio);
      }
      else h = Mathf.RoundToInt(w / aspectRatio);
   }
 
   // Don't let the width and height get too small
   if (w < minWidth) w = minWidth;
   if (h < minHeight) h = minHeight;
 
   // Update the position if it has changed
   if (Vector3.SqrMagnitude(pos - newPos) > 0.001f)
   {
      cachedTransform.localPosition = newPos;
      if (mIsInFront) mChanged = true;
   }
 
   // Update the width and height if it has changed
   if (mWidth != w || mHeight != h)
   {
      mWidth = w;
      mHeight = h;
      if (mIsInFront) mChanged = true;
      if (autoResizeBoxCollider) ResizeCollider();
   }
}
4.锚点具体步骤:
  1. 获取对应的锚点对象target的渲染区域sides。例如下面的Texture的Sides的4个点。
  1. 通过leftAnchor/rightAnchor/bottomAnchor/topAnchor确定四个边的坐标,例如左边坐标计算是
lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute;
static public float Lerp (float from, float to, float factor) 
{ 
    return from * (1f - factor) + to * factor; 
}
  • 这边leftAnchor.relative=0是在UIRectEditor.DrawAnchorTransform设置的,同理rightAnchor.relative= 1。
3.通过上一步计算的四条边和Pivot计算最终的显示坐标。
Vector3 newPos = new Vector3(Mathf.Lerp(lt, rt, pvt.x), Mathf.Lerp(bt, tt, pvt.y), pos.z);
4.通过4条边计算渲染区域的大小。
int w = Mathf.FloorToInt(rt - lt + 0.5f);
int h = Mathf.FloorToInt(tt - bt + 0.5f);
5.通过前面的知识点,NGUI通过矩形的4个点确定最终的渲染区域(大小及坐标)。锚点系统通过动态修改矩形的四条边,从而实现对最终渲染矩形坐标、大小的修改。
 
 
 
posted @ 2020-08-15 15:43  柯腾_wjf  阅读(329)  评论(0编辑  收藏  举报