unity3d-RectTransform agile Posted on Aug 22 2023 坐标关系:子ui的原点是`pivot`,子ui的localposition是以父ui的`pivot`为原点创建的坐标系,父ui的rect包含(x,y,width,height)。其中x表示的是以父ui的`pivot`为坐标原点在父ui左下角的坐标值 获取anchorPosition,计算方式 ```C# public Vector2 getAnchorPosition(RectTransform child, RectTransform parent) { var anchorMinPostion = new Vector2(parent.rect.x + child.anchorMin.x * parent.rect.width, parent.rect.y + child.anchorMin.y * parent.rect.height); var anchorMaxPostion = new Vector2(parent.rect.x + child.anchorMax.x * parent.rect.width, parent.rect.y + child.anchorMax.y * parent.rect.height); var anchorRefPosition = anchorMinPostion + new Vector2(anchorMaxPostion.x - anchorMinPostion.x, anchorMaxPostion.y - anchorMinPostion.y) * child.pivot; return new Vector2(child.localPosition.x, child.localPosition.y) - anchorRefPosition; } ``` ```C# #offsetMin最原始的实现方式 public Vector2 getOffsetMin(RectTransform child, RectTransform parent) { var anchorMinPostion = new Vector2(parent.rect.x + child.anchorMin.x * parent.rect.width, parent.rect.y + child.anchorMin.y * parent.rect.height); return new Vector2(child.localPosition.x + child.rect.x - anchorMinPostion.x, child.localPosition.y + child.rect.y - anchorMinPostion.y); } #offsetMax最原始的实现方式 public Vector2 getOffsetMax(RectTransform child, RectTransform parent) { var anchorMaxPostion = new Vector2(parent.rect.x + child.anchorMax.x * parent.rect.width, parent.rect.y + child.anchorMax.y * parent.rect.height); return new Vector2(child.localPosition.x + child.rect.x + child.rect.width - anchorMaxPostion.x, child.localPosition.y + child.rect.y + child.rect.height - anchorMaxPostion.y); } #官方offsetMin获取方式 public Vector2 offsetMin { get { //sizeDelta增大,那offsetMin应该是需要减少 return this.anchoredPosition - Vector2.Scale(this.sizeDelta, this.pivot); } set { Vector2 a = value - (this.anchoredPosition - Vector2.Scale(this.sizeDelta, this.pivot)); this.sizeDelta -= a; this.anchoredPosition += Vector2.Scale(a, Vector2.one - this.pivot); } } #官方offsetMax获取方式 public Vector2 offsetMax { get { //sizeDelta增大,那offsetMax应该是需要增大 return this.anchoredPosition + Vector2.Scale(this.sizeDelta, Vector2.one - this.pivot); } set { Vector2 a = value - (this.anchoredPosition + Vector2.Scale(this.sizeDelta, Vector2.one - this.pivot)); this.sizeDelta += a; this.anchoredPosition += Vector2.Scale(a, this.pivot); } } #sizeDelta获取方式 offsetMax-offsetMin public Vector2 getSizeDelta(RectTransform child) { return child.offsetMax - child.offsetMin; } #sizeDelta获取方式等价于(子ui宽-锚框的宽,子UI高-锚框的高) public Vector2 getSizeDelta(RectTransform child, RectTransform parent) { return new Vector2(child.rect.width - (child.anchorMax.x - child.anchorMin.x) * parent.rect.width, child.rect.height - (child.anchorMax.y - child.anchorMin.y) * parent.rect.height); } ``` #一、基本要点 - `RectTransform`继承于`Transform`,在`Transform`基础上,`RectTransform`增加了轴心(`pivot`)、锚点(`anchors`)、和 尺寸变化量(`sizeDelta`)。 - 其中`anchors`和`pivot`都是坐标原点在`左下角`的`0-1`向量空间,`0-1`代表的是`比例`。`anchors`的向量空间是`子UI`相对`父UI`的`比例位置`,`pivot`的向量空间是相对`UI本身`的比例位置。  --- #二、Anchors(锚点或锚框) - 锚点功能的引入,使UI相对布局和绝对布局,自适应,等比缩放等完美融合一起。在uGUI中`Anchors`并不一定是`锚点`,也可能是一个`矩形`。当`Anchors`汇聚成一个`点`时,我们通常称之为`锚点`;当`Anchors`是一个`矩形`状时,我们通常称之为`锚框`。`Anchors`是`子UI`在`父UI`中的映射位置,但并`不代表子UI的实际大小`,子UI的实际大小,还会通过这些属性(`width,height,posX,posY,left,top,right,bottom`)相对`Anchors`进行调整。 ##基本概念 - `Pos (X, Y, Z)` ,矩形轴心点(`pivot`)与锚点(`anchors`)之间的距离。 - `Left, Top, Right, Bottom`,矩形的四条边与锚框(`anchors`)之间的间距 ###(一)Anchors是一个点时  - 在数值上表示为:achorMin.x==achorMax.x && achorMin.y==achorMax.y - Width/Height可以设置,不受Anchors影响。 - PosX/PosY可以设置。设置此值后,矩形轴心点与锚点之间的距离就恒定了,不管如何改变父UI的尺寸。子UI不会随父UI进行拉伸,位置和大小是固定的。 ###(二)Anchors是一条线时  - 一条横线:achorMin.y==achorMax.y,如上图。子UI宽度会随父UI进行拉伸,高度和Y方向的距离是固定的。此时,Left,Right,PosY,Height可以设置。 - 一条竖线:achorMin.x==achorMax.x。子UI高度会随父UI进行拉伸,宽度和X方向的距离是固定的。此时,Top,Bottom,PosX,Width可以设置。 ###(三)Anchors是一个矩形时  - achorMin.y!=achorMax.y && achorMin.y!=achorMax.y - 子UI宽高都是拉伸状态。 - 宽高不可以设置。 - Left, Top, Right, Bottom可设置,矩形的四条边与锚框的边间距是固定的 --- #三、anchoredPosition - 可以通过改动`anchoredPosition`来修改ui的偏移位置 - `anchoredPosition`官方描述为:The position of the pivot of this RectTransform relative to the anchor reference point.即:`RectTransform的pivot与锚点(anchor reference point)的向量` ##(一)Anchros汇聚一个点时 - `Anchros`汇聚一个点时,锚点(`anchor reference point`)比较好理解,就是Anchros汇聚的这个点.`anchoredPosition=pivot-anchorReferencePosition(锚点)`  ##(二)Anchros是一个矩形时 - Anchros是一个矩形时,锚点(anchor reference point)的位置就稍显复杂,此时**锚点(anchor reference point)的位置是根据pivot计算出来的线性插值。** ### 在此插入线性插值相关知识,复习一下基础知识 - 线性插值法是指使用连接两个已知量的直线来确定在这两个已知量之间的一个未知量的值的方法。  - 假设我们已知坐标(x0,y0)与(x1,y1),要得到[x0,x1]区间内某一位置x在直线上的值。根据图中所示,我们得到: >>`y = (1 − α)*y0 + α*y1 或者 y = y0 + α(y1 − y0)` >>`x = (1 − α)*x0 + α*x1 或者 x = x0 + α(x1 − x0)` 这样就可以通过α就可以直接得到 x,y。 ####通过线性插值的方法计算锚点 - `α`即为`pivot(0-1)`的比例系数! - 已知`pivot(0,0.5)`,`pivot`所在坐标值为`(100,250)` - 已知`左下角锚点`坐标`(x0,y0)=(0,0)` - 已知`右上角锚点`坐标`(x1,y1)=(600,500)` 以上值,通过Rect的Width,Height及Rect与锚的间距,以锚左下角为原点的坐标系获得。 - `anchorReferencePointX = (1 − 0)*0 + 0*600 = 0;` - `anchorReferencePointY = (1 − 0.5)*0 + 0.5*500 = 250;` - 得到:`anchorReferencePoint = (0,250)`  - 图中黄点即为计算出来的`anchorReferencePoint` - `anchorReferencePoint`到`pivot`之间的向量即为`anchoredPosition`,`anchoredPosition=pivot(100,250)-anchorReferencePoint(0,250)=(100,0);` --- #四、offsetMin与offsetMax - `offsetMax`为当前矩形右上角相对于锚点右上角的偏移。 - `offsetMin`为当前矩形左下角相对于锚点左下角的偏移。 ##1、Anchors汇聚一个点时 - offsetMax/offsetMin的计算如下图:  - `Anchors`汇聚一个点时,即以锚点为坐标原点(0,0),`offsetMax`和`offsetMin`就可以直接用坐标系的方式,快速得出右上角和左下角在此坐标系中的`(x,y)` ##2、Anchors是一个矩形时 - offsetMax/offsetMin的计算如下图:  - Anchors是一个矩形,则需要以左下角锚点和右上角锚点分别作为坐标原点画两个坐标系,以计算offsetMin和offsetMax. --- #五、sizeDelta - `sizeDelta`是锚点定义的子矩形与锚点区域大小偏移量,也可以称之为尺寸变化量。 - 其实`sizeDelta`的值就是`OffsetMax-OffsetMin`的值 - 所以就会出现有时候`sizeDelta得到的是UI元素的大小`,有时候又不是的情况,下面就复现一下这两种情况 ##5.1锚点情况下的sizeDelta  - 在锚点情况下,offsetMax和Min的起点相同,根据向量相减的三角形法则(不记得是不是这样说得了哈哈哈),可以得到一个新的向量,这个新的向量的X和Y的大小正好UI元素的宽和高相等,**所以在这个时候去设置sizeDelta的值,可以直接调整UI元素的大小** ##5.2锚框情况下的sizeDelta  - 在锚框的情况下,offstMax减去Min,得到的将不再是UI元素的大小,而是一个新的奇怪的向量,这个向量代表的物理意义是,**sizeDelta.x值就是UI元素的宽度与锚框的宽度的差值,sizeDelta.y的值就是UI元素的高度与锚框的的高度的差值** >>所以这个属性之所以叫做sizeDelta,是因为在锚点情况下其表征的是size(大小),在锚框的情况下其表征的是Delta(差值) --- #rect `rect`中的属性,不与ui所在的位置有关,只和其自身的属性相关。以`Pivot`为原点,创建的坐标系。返回的x,y值分别是该UI的左下角在`Pivot`坐标系的值。width和height分别表示该ui的宽和高。 --- #Pivot - Pivot中心点,就是该UI元素旋转缩放的中心点,左下角为(0,0)右上角为(1,1)    在绝对布局的情况下,PosX和PosY的值就是Pivot到锚点的值 --- #如何动态RectTransform的大小 ##1、Anchors汇聚一个点时 - sizeDelta(x,y)与Rect的宽高是一致的。RectTransform与锚点偏移量就是本身的大小。 - RectTransform的Rect是只读的,当需要动态设置RectTransform的尺寸时,在Anchors汇聚一个点时,就可以直接通过sizeDelta的x,y来动态设置RectTransform的对应的宽和高 ##2、Anchors是一个矩形时 - sizeDelta同样可以设置Rect大小,但理解上不太好转换。可以直接通过offsetMax和offsetMin的偏移量来动态调整Rect的大小 ###sizeDelta的计算实例 - 如下图,红色区域的Anchors是一个anchorMin(0,0)到anchorMax(1,1)的全拉伸锚框,left,right,top,bottom全为50   - 红线内数据,sizeDelta = offsetMax-offsetMin得到的向量,即:`sizeDelta = (-50-50,-50-50) = (-100,-100)` --- #Recttransform类中一些方法的介绍 ##`SetSizeWithCurrentAnchors(Animations.Axis axis, float size)` - 这个方法无论在绝对布局还是相对布局的情况下,都可以通过直接设置rect中的width和height值来改变UI元素的大小 ```C# public void ClickedBtn() { RectTransform rectTransform = GetComponent<RectTransform>(); rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,200); rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical,60); } ``` - 参考`SetSizeWithCurrentAnchors`的源码,修改为可以仅仅传入长宽,直接修改大小。是通过修改`sizeDelta`,来改变长宽。原理就是保证锚点,轴点不变,也就是`anchorsPosition`位置不变 ```C# private static Vector2 GetParentSize(ref RectTransform rectTransform) { RectTransform parent = rectTransform.parent as RectTransform; if (!(bool) ((Object) parent)) return Vector2.zero; return parent.rect.size; } public static void SetSize(ref RectTransform rectTransform, Vector2 size) { Vector2 sizeDelta = rectTransform.sizeDelta; for (int i = 0; i < 2; i++) { //sizeDelta.x值就是UI元素的宽度与锚框的宽度的差值,sizeDelta.y的值就是UI元素的高度与锚框的的高度的差值 sizeDelta[i] = size[i] - GetParentSize(ref rectTransform)[i] * (rectTransform.anchorMax[i] - rectTransform.anchorMin[i]); } rectTransform.sizeDelta = sizeDelta; } ``` - google到另外一个修改RectTransform大小的方法,是通过修改`offsetMin`,`offsetMax`来改变长宽 ```C# public static void SetRectTransformSize(ref RectTransform trans, Vector2 newSize) { //原先RectTransform的长宽 Vector2 oldSize = trans.rect.size; //差值原先RectTransform的长宽 Vector2 deltaSize = newSize - oldSize; trans.offsetMin = trans.offsetMin - new Vector2(deltaSize.x * trans.pivot.x, deltaSize.y * trans.pivot.y); trans.offsetMax = trans.offsetMax + new Vector2(deltaSize.x * (1f - trans.pivot.x), deltaSize.y * (1f - trans.pivot.y)); } ```  - 通过上图我们可以清晰的看出绿色向量分别为:`new Vector2(deltaSize.x * trans.pivot.x, deltaSize.y * trans.pivot.y)`以及`new Vector2(deltaSize.x * (1f - trans.pivot.x), deltaSize.y * (1f - trans.pivot.y))` ##`SetInsetAndSizeFromParentEdge(RectTransform.Edge edge, float inset, float size)` - 这个方法就比较冷门了可能,不过还是挺强大的。调用这个方法,可以根据父物体的Edge(某一边)去布局。其中第一个参数就是用于确定基准的边,第二个参数是UI元素的该边界与父物体该边界的距离,第三个元素是设定选定轴上UI元素的大小,可能说起来有点复杂,但是我上两张图相信各位就可以秒懂了 ```C# public void ClickSetInsetAndSizeFromParentEdge() { rectTransform = GetComponent<RectTransform>(); //这种情况下我选定父物体的右边界为基准,结果如下图 rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 200, 400); } ```  - 然后以下边界为基准 ```C# public void ClickSetInsetAndSizeFromParentEdge() { rectTransform = GetComponent<RectTransform>(); //rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Right, 200, 400); rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, 200, 400); } ```  ###在使用这个方法的时候要注意锚点也会改变,改变的规则为 - 以`左边界`为基准时,anchorMin和anchorMax 的`y不变x变为0`. - 以`右边界`为基准时,anchorMin和anchorMax 的`y不变x变为1`. - 以`上边界`为基准时,anchorMin和anchorMax 的`x不变y变为1`. - 以`下边界`为基准时,anchorMin和anchorMax 的`x不变y变为0`. ##`GetWorldCorners(Vector3[] fourCornersArray)` - 使用这个方法,可以取得UI元素四个角的世界坐标,具体使用方法,先建立一个长度为4的vector3数组,然后传进这个方法,调用一次后,数组被赋值,里面的四个元素分别是UI的左下角 ,左上角,右上角,右下角 - `public void GetLocalCorners(Vector3[] fourCornersArray)` - 使用这个方法,可以取得UI元素四个角的本地坐标(当前ui元素以轴心(Pvoit)为原点的坐标系) ```C# public void getPosition() { Vector3[] vector3s = new Vector3[4]; rectTransform.GetWorldCorners(vector3s); Debug.Log("Script_05_03_03+++++getPosition++start++++"); foreach (var corner in vector3s) { Debug.LogFormat("GetWorldCorners:{0}",corner); } rectTransform.GetLocalCorners(vector3s); foreach (var corner in vector3s) { Debug.LogFormat("GetLocalCorners:{0}",corner); } Debug.Log("Script_05_03_03+++++getPosition++end++++"); } ``` ## `public extern void ForceUpdateRectTransforms();` - 强制rectTransform内部数据的重新计算 --- #RectTransformUtility中的重要方法 - 当Canvas设置的Render mode为Screen Space - Overlay时,需要将camera设置为null ####`public static bool RectangleContainsScreenPoint(RectTransform rect, Vector2 screenPoint,Camera cam)` - 该屏幕坐标是否在RectTransforms中 ####`public static void FlipLayoutAxes(RectTransform rect, bool keepPositioning, bool recursive)` - 当`keepPositioning`为`true`,`recursive`为`true`时候,如下图所示效果  **上图中的button,位置保持不变,`width`和`height`交换了值** - 当`keepPositioning`为`false`,`recursive`为`true`时候,如下图所示效果  **上图中的button,`posx`和`posy`交换了大小,`width`和`height`交换了值** - 当`keepPositioning`为`true`,`recursive`为`false`时候,如下图所示效果  **上图中的button,位置保持不变,`width`和`height`交换了值**,但其child:text,不管是位置,还是长宽都没变化。 - 对于上诉的状态的变化,看下这方法的源码大体也能了解缘由 ```C# public static void FlipLayoutAxes(RectTransform rect, bool keepPositioning, bool recursive) { if ((Object) rect == (Object) null) return; if (recursive) { for (int index = 0; index < rect.childCount; ++index) { RectTransform child = rect.GetChild(index) as RectTransform; if ((Object) child != (Object) null) RectTransformUtility.FlipLayoutAxes(child, false, true); } } rect.pivot = RectTransformUtility.GetTransposed(rect.pivot); rect.sizeDelta = RectTransformUtility.GetTransposed(rect.sizeDelta); if (keepPositioning) return; rect.anchoredPosition = RectTransformUtility.GetTransposed(rect.anchoredPosition); rect.anchorMin = RectTransformUtility.GetTransposed(rect.anchorMin); rect.anchorMax = RectTransformUtility.GetTransposed(rect.anchorMax); } private static Vector2 GetTransposed(Vector2 input) { return new Vector2(input.y, input.x); } ``` ####`public static void FlipLayoutOnAxis(RectTransform rect,int axis,bool keepPositioning,bool recursive)` - 当axis为0时,表示横轴,修改的是x方向。当为1时,表示竖轴,修改的是y方向。 - `recursive`为true的时候表示也修改该root的子类的`povit`,同时会修改子类的`anchoredPosition,anchorMin,anchorMax` - `axis`为`0`,`keepPositioning`为`true`,`recursive`为`true`,如下图所示:  **如上图所示该ui `Povit`在变化,但anchoredPosition,anchorMin,anchorMax不会变化,但该子类的相应的值都在改变** - `axis`为`0`,`keepPositioning`为`false`,`recursive`为`true`,如下图所示:  **如上图所示该ui `Povit`在变化,同时anchoredPosition,anchorMin,anchorMax也会变化** - `axis`为`1`,`keepPositioning`为`true`,`recursive`为`true`,如下图所示:  **相对于axis为0的情况,UI现在是竖的方向上改变,来了个翻转** ```C# public static void FlipLayoutOnAxis( RectTransform rect, int axis, bool keepPositioning, bool recursive) { if ((Object) rect == (Object) null) return; if (recursive) { for (int index = 0; index < rect.childCount; ++index) { RectTransform child = rect.GetChild(index) as RectTransform; if ((Object) child != (Object) null) RectTransformUtility.FlipLayoutOnAxis(child, axis, false, true); } } Vector2 pivot = rect.pivot; pivot[axis] = 1f - pivot[axis]; rect.pivot = pivot; if (keepPositioning) return; Vector2 anchoredPosition = rect.anchoredPosition; anchoredPosition[axis] = -anchoredPosition[axis]; rect.anchoredPosition = anchoredPosition; Vector2 anchorMin = rect.anchorMin; Vector2 anchorMax = rect.anchorMax; float num = anchorMin[axis]; anchorMin[axis] = 1f - anchorMax[axis]; anchorMax[axis] = 1f - num; rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; } ``` ####`public static Bounds CalculateRelativeRectTransformBounds(Transform root, Transform child)` - 计算该RectTransform的包围盒,child如果不传递,就是计算该控件自己本身的包围盒 ####`public static Vector2 WorldToScreenPoint(Camera cam, Vector3 worldPoint)` - 世界空间中的点坐标转换到屏幕坐标 ####` public static Ray ScreenPointToRay(Camera cam, Vector2 screenPos)` - 屏幕坐标和屏幕plane组成的射线 - 下面是`ScreenPointToWorldPointInRectangle`方法对于`ScreenPointToRay`具体应用 ```C# public static bool ScreenPointToWorldPointInRectangle(RectTransform rect,Vector2 screenPoint,Camera cam,out Vector3 worldPoint) { worldPoint = (Vector3) Vector2.zero; Ray ray = RectTransformUtility.ScreenPointToRay(cam, screenPoint); float enter; //当光线投射与任何碰撞器交叉时为真,否则为假 if (!new Plane(rect.rotation * Vector3.back, rect.position).Raycast(ray, out enter)) return false; //返回沿着射线在distance距离单位的点 worldPoint = ray.GetPoint(enter); return true; } ``` ###`public static Rect PixelAdjustRect(RectTransform rectTransform, Canvas canvas)` - 返回RectTransform角点的像素精确坐标,角点的值是左下角相对于povit中心点的相对坐标,存储在Rect的x,y中 ###`public static Vector2 PixelAdjustPoint(Vector2 point,Transform elementTransform,Canvas canvas)` - 根据Transform和Canvas把point这个屏幕坐标点转换成像素正确的坐标点 上诉两个方法在ui源码的Graphic类中都被重新封装了下 ```C# public Vector2 PixelAdjustPoint(Vector2 point) { if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect) return point; else { return RectTransformUtility.PixelAdjustPoint(point, transform, canvas); } } public Rect GetPixelAdjustedRect() { if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect) return rectTransform.rect; else return RectTransformUtility.PixelAdjustRect(rectTransform, canvas); } ``` ####`public static bool ScreenPointToLocalPointInRectangle(RectTransform rect,Vector2 screenPoint,Camera cam,out Vector2 localPoint)` - UGUI屏幕坐标转UI坐标,通过localPoint参数 ####`public static bool ScreenPointToWorldPointInRectangle(RectTransform rect,Vector2 screenPoint,Camera cam,out Vector3 worldPoint)` - UGUI屏幕坐标转世界坐标,worldPoint参数 ```C# // 屏幕坐标 Vector3 pt = Input.mousePosition; Debug.Log("Camera.current:" + Camera.main); // 下面两个方法转换有问题啊 // Vector3 worldPos = Camera.main.ScreenToWorldPoint(pt); //屏幕坐标转换世界坐标 // Vector2 uiPos = rectTransform.InverseTransformPoint(worldPos); //世界坐标转换位本地坐标 // Debug.LogFormat("worldpos:{0},uiPos:{1}", worldPos, uiPos); Vector2 localPoint = Vector2.one; Vector3 worldPoint = Vector3.one; //当Canvas设置的Render mode为Screen Space - Overlay时,需要将camera设置为null Camera camera = GameObject.Find("UICamera").GetComponent<Camera>(); //屏幕坐标转成ui坐标 bool isScreenPointToLocalPointInRectangle = RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, pt, camera, out localPoint); //屏幕坐标转为世界坐标 bool isScreenPointToWorldPointInRectangle = RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, pt, camera, out worldPoint); //该屏幕坐标是否在Rectangle中 Debug.LogFormat( "pt:{0},RectangleContainsScreenPoint:{1}", pt, RectTransformUtility.RectangleContainsScreenPoint(rectTransform, pt, camera)); Debug.LogFormat("ScreenPointToLocalPointInRectangle:{0},flag:{1}", localPoint, isScreenPointToLocalPointInRectangle); Debug.LogFormat("ScreenPointToWorldPointInRectangle:{0},flag:{1}", worldPoint, isScreenPointToWorldPointInRectangle); ``` - 通过鼠标的坐标在屏幕上移动来更新UI的显示位置 ```C# using UnityEngine; public class Script_05_03_04 : MonoBehaviour { private RectTransform _rectTransform; private Camera _camera; // Start is called before the first frame update void Start() { _rectTransform = GetComponent<RectTransform>(); _camera = GameObject.Find("UICamera").GetComponent<Camera>(); } // Update is called once per frame void Update() { Vector2 pos; RectTransform pRect = _rectTransform.parent.GetComponent<RectTransform>(); if (RectTransformUtility.ScreenPointToLocalPointInRectangle(pRect, Input.mousePosition, _camera, out pos)) { //Vector2.Scale(_rectTransform.rect.size,_rectTransform.pivot - new Vector2(0.5f, 0.5f));是为了povit可能不是(0.5,0.5), //导致坐标定位没有处于ui中间 //再次调整位置是为了使原先鼠标点击的位置为ui的中间位置,而`povit`的位置并不一定在ui中间 _rectTransform.localPosition = pos + Vector2.Scale( Vector2.Scale(_rectTransform.rect.size, _rectTransform.localScale), _rectTransform.pivot - new Vector2(0.5f, 0.5f)); } } } ``` [Unity——RectTransform详解](https://www.jianshu.com/p/4592bf809c8b) [uGUI知识点剖析之RectTransform](http://www.2fz1.com/post/unity-ugui-recttransform/) [uGUI知识点剖析之AutoLayout](http://www.2fz1.com/post/unity-ugui-autolayout/) [UGUI——RectTransform详解](https://blog.csdn.net/serenahaven/article/details/78826851) [Unity开发-深入理解RectTransform](https://blog.csdn.net/buctyyzyn/article/details/80558946) [Unity Ugui射线坐标转换总结](http://www.cnblogs.com/fuliufuliu/p/5094557.html) [UGUI研究院之获取UI子节点在Canvas的2D坐标(十二)](https://www.xuanyusong.com/archives/3476) [Unity UI大小动态设置(Resize Unity UI RectTransform)](https://www.jianshu.com/p/2455109cf761) [unity3d基于ugui的ui模块](https://blog.csdn.net/snoopyna2co3/article/details/50409108) [ugui ui相对位置的计算,以及如何把ui限制在屏幕内](https://blog.csdn.net/SnoopyNa2Co3/article/details/50429604) 0829学习 C++面试题