RectTransformUtility,或如何制作在屏幕后面为UI元素添加动画效果的组件

在上一篇文章-Unity3d GUI中使用的各种坐标中,我试图简要地讨论一下Unity UI / RectTransform中的各种坐标。 现在,我想为UI突出一点诸如RectTransformUtility这样有用的东西。 这是用于计算UI中与其他事物相关的某些事物的主要工具之一。

简单的挑战


有一项任务-您需要一个组件,以动画方式将UI元素删除到屏幕的选定边缘之外。 组件在分层位置,锚点在何处,屏幕的大小以及屏幕的位置应为紫色。 该组件应能够在给定时间内从四个侧面(上,下,左,右)清洁物体。

感言

原则上,该怎么做? 在对象的坐标中找出屏幕的大小,将对象移至屏幕边缘以外的坐标,看来问题出在帽子上。 但是有几个但是:

如何找出相对于UI的屏幕坐标?

如果您在额头上用谷歌搜索,请用谷歌搜索一些废话或无用的东西,甚至没有答案。 最适合您的是某个UI元素跟随光标,而光标恰好存在于屏幕的坐标中。

RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Input.mousePosition), null, out topRightLocalCoord); 

它们分别是RectTransformUtility和ScreenPointToLocalPointInRectangle。 在这里,我们根据屏幕上点的位置获取rect(RectTransform)内部的局部坐标。
在当前示例中,我们找到了鼠标光标的局部坐标,我们需要将它们替换为屏幕边缘:

 RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Screen.width, Screen.height), null, out topRightLocalCoord); 

这样我们就得到了屏幕右上角的坐标,以便对象离开屏幕向右,我们的对象应该比该点更远+假设rect的宽度或指定的缩进量。

所以,第一个警告

我们得到了适合于直接在画布内部对象的局部坐标,如果要移动的河流位于另一个区域,则将相对于父对象而不是画布考虑其局部坐标。 也就是说,这些坐标本身不适合我们。

有两种方法 ,第一种是使用全局坐标,因为它们是全局坐标。 或者在每个矩形的局部坐标中分别计算屏幕的坐标。

考虑第一种情况 -如何将局部坐标转换为全局坐标。

大多数谷歌方法使用-TransformPoint。

 transform.position = myCanvas.transform.TransformPoint(pos); 

因此,我们将局部坐标转换为全局坐标。

我认为这通常是一个额外的步骤,因为RectTransformUtility具有一个ScreenPointToWorldPointInRectangle方法,该方法可立即返回全局位置。

我们需要将rect移到屏幕的右边缘之外,为此,我们从找到的位置获取X坐标,而Y离开我们要移动的rect,因此它仅向右移动。

 new Vector3(topRightCoord.x+offset, rectTransform.position.y, 0); 

结果坐标由DoTween馈送。

 rectTransform.DOMove(new Vector3(correctedTargetRight.x, rectTransform.position.y, 0), timeForHiding); 

欢呼,物体离开了右边。 但是...

细微差别

在这里,我们发现实际上rect的位置取决于rect枢轴。



因此,对象可以根据枢轴的位置进行跳舞,再加上对象可能非常大,并且偏移量不会将其完全推入屏幕的后面,因此总有可能会出现碎片。

也就是说,我们需要对补偿进行螺钉补偿,以考虑rect +枢轴的大小。

第二个细微差别是按照rect的大小移动对象,您需要知道局部或锚点坐标,然后获得全局坐标。 我必须马上说,不能获取全局坐标并将其转换为本地UI坐标或锚定坐标。
我想出了以下拐杖,我们记得矩形的起始位置,将其移动到最终的全局位置,将锚位置按矩形的大小向右移动,记住考虑了偏移量的全局位置,并考虑了对象的大小,并给它填充了空心,不要忘记返回到原始位置位置。

代码示例
  var targetRight = new Vector3(topRightLocalCoord.x, rectTransform.position.y, 0); rectTransform.position = targetRight; rectTransform.anchoredPosition += rectTransform.sizeDelta; var correctedTargetRight = rectTransform.position; rectTransform.localPosition = startPoint; rectTransform.DOMove(new Vector3(correctedTargetRight.x, rectTransform.position.y, 0), timeForHiding); 


它看起来像一个巨大的拐杖,但是这个拐杖使您可以同步全局坐标和其他坐标。 当界面中存在彼此相对移动的对象,并且它们位于不同的层次结构中时,这会有所帮助。 好吧,到目前为止,这是我发现从global获取rect坐标的唯一方法。

在这一点上,我们将对拐杖说不,然后回到以本地坐标获取屏幕尺寸的想法。

第二种方式


第二种方法是分别获取每个矩形的屏幕尺寸,因此我们将知道屏幕边缘的局部坐标,而与画布或层次结构无关。

第三细微差别

 RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(Screen.width, Screen.height), null, out topRightCoord); RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(0, 0), null, out bottomScreenCoord); 

对象可以位于屏幕上的任何位置,与覆盖整个屏幕的画布不同。 因此,到屏幕左右边缘的距离可能会明显不同。 在画布的情况下,我们将只有右上边缘,而减去右上将是左上。 在这种情况下,您需要分别获得左下角和右上角,如代码示例所示。

第四点细微差别

局部坐标是相对于父对象中心的偏移量,当rect嵌入另一个rect中时,该rect占画布的一小部分,那么我们需要一个考虑了两个位移的坐标,那么,一切都很简单。

 ((Vector3)bottomLeftCoord + rectTransform.localPosition) 

添加向量并获得我们需要的坐标。 事实证明,与全局坐标相比,它更令人困惑,但是现在我们可以执行与矩形大小有关的任何计算。 最后,冷静地增加补偿而没有拐杖。

  (Vector3)topRightCoord + rectTransform.localPosition + (new Vector3((rectTransform.sizeDelta.x * rectTransform.pivot.x) + rectTransform.sizeDelta.x, 0, 0)); 

这是坐标向右移动(补偿矩形的宽度并从屏幕向矩形的宽度移动)的样子,无法设置偏移量,我计划稍后再添加,但我认为有人会想尝试自己编写此偏移量。

结论


  1. 对于UI元素,最好使用局部或锚点坐标,并且您应该尝试了解它们。 全局坐标可以用于特殊情况,但是它们无法方便地进行处理,例如,使用矩形大小和许多其他微小片段。
  2. 您需要查看RectTransformUtility,它对UI具有很多有用的功能,所有与矩形内部和周围物体的位置有关的计算都是通过矩形完成的。

好吧,组件本身,如果有人想使用它,则需要DoTween:

组成部分
 using DG.Tweening; using UnityEngine; public enum Direction { DEFAULT, RIGHT, LEFT, TOP, BOTTOM } public enum CanvasType {OVERLAY, CAMERATYPE} public class HideBeyondScreenComponent : MonoBehaviour { [SerializeField] private Direction direction; [SerializeField] private CanvasType canvasType; [SerializeField] private float timeForHiding = 1; [SerializeField] private float offset = 50; private Vector3 startPoint; private RectTransform rectTransform; private Vector2 topRightCoord; private Vector2 bottomLeftCoord; private void Start() { rectTransform = transform as RectTransform; startPoint = rectTransform.localPosition; Camera camera = null; if (canvasType == CanvasType.CAMERATYPE) camera = Camera.main; RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(Screen.width, Screen.height), camera, out topRightCoord); RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(0, 0), camera, out bottomLeftCoord); Hide(); } public void Show() { rectTransform.DOLocalMove(startPoint, timeForHiding); } public void Hide() { switch (direction) { case Direction.LEFT: rectTransform.DOLocalMove(new Vector3(EndPosition(Direction.LEFT).x, rectTransform.localPosition.y, 0), timeForHiding); break; case Direction.RIGHT: rectTransform.DOLocalMove(new Vector3(EndPosition(Direction.RIGHT).x, rectTransform.localPosition.y, 0), timeForHiding); break; case Direction.TOP: rectTransform.DOLocalMove(new Vector3(rectTransform.localPosition.x, EndPosition(Direction.TOP).y, 0), timeForHiding); break; case Direction.BOTTOM: rectTransform.DOLocalMove(new Vector3(rectTransform.localPosition.x, EndPosition(Direction.BOTTOM).y, 0), timeForHiding); break; } } private Vector3 NegativeCompensation() { return new Vector2((-rectTransform.sizeDelta.x - offset) + rectTransform.sizeDelta.x * rectTransform.pivot.x, (-rectTransform.sizeDelta.y - offset) + rectTransform.sizeDelta.y * rectTransform.pivot.y); } private Vector3 PositiveCompensation() { return new Vector2((rectTransform.sizeDelta.x * rectTransform.pivot.x) + offset, (rectTransform.sizeDelta.y * rectTransform.pivot.y) + offset); } private Vector2 EndPosition(Direction direction) { switch (direction) { case Direction.LEFT: return ((Vector3)bottomLeftCoord + rectTransform.localPosition) + NegativeCompensation(); case Direction.RIGHT: return (Vector3)topRightCoord + rectTransform.localPosition + PositiveCompensation(); case Direction.TOP: return ((Vector3)topRightCoord + rectTransform.localPosition) + PositiveCompensation(); case Direction.BOTTOM: return ((Vector3)bottomLeftCoord + rectTransform.localPosition) + NegativeCompensation(); } return startPoint; } } 

Source: https://habr.com/ru/post/zh-CN457142/


All Articles