En el último artículo:
Variedades de coordenadas utilizadas en la GUI de Unity3d, traté de hablar brevemente sobre las variedades de coordenadas en Unity UI / RectTransform. Ahora quiero resaltar algo tan útil para la interfaz de usuario como RectTransformUtility. Cuál es una de las principales herramientas para calcular algo en la interfaz de usuario en relación con otra cosa.
Desafío simple
Hay una tarea: necesita un componente que elimine animadamente el elemento de la IU más allá del borde seleccionado de la pantalla. El componente debe ser morado donde está ubicado jerárquicamente, en qué lugares están los anclajes, qué tamaño de pantalla y en qué lugar de la pantalla se encuentra. El componente debe poder limpiar el objeto en 4 lados (arriba, abajo, izquierda, derecha) durante un tiempo determinado.
ReflexionesEn principio, ¿cómo se puede hacer esto? Descubra el tamaño de la pantalla en las coordenadas del objeto, mueva el objeto a la coordenada más allá del borde de la pantalla, y parece que el asunto está en el sombrero. Pero hay una pareja pero:
¿Cómo encontrar las coordenadas de la pantalla en relación con la interfaz de usuario?Si busca en Google en la frente, busque en Google algunas cosas sin sentido o que no sean útiles, o incluso preguntas sin respuesta. Lo más cercano que le conviene es cuando algún elemento de la IU sigue al cursor, que solo existe en las coordenadas de la pantalla.
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Input.mousePosition), null, out topRightLocalCoord);
Estos son RectTransformUtility y ScreenPointToLocalPointInRectangle directamente. Aquí obtenemos las coordenadas locales dentro del rect (RectTransform), en función de la posición del punto en la pantalla.
En el ejemplo actual, encontramos las coordenadas locales del cursor del mouse, necesitamos reemplazarlas con el borde de la pantalla:
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Screen.width, Screen.height), null, out topRightLocalCoord);
Y así obtuvimos la coordenada del punto superior derecho de la pantalla para que el objeto salga de la pantalla a la derecha, nuestro objeto debería estar más lejos que este punto + digamos el ancho del rect o la sangría especificada.
Entonces, la primera advertenciaObtuvimos coordenadas locales que son adecuadas para los objetos directamente dentro del lienzo, si el río a desplazar se encuentra en otro rectángulo, entonces sus coordenadas locales se considerarán en relación con el padre, no el lienzo. Es decir, estas coordenadas en sí mismas no nos convienen.
Hay dos formas , la primera es usar coordenadas globales, para eso son globales. O calcule las coordenadas de la pantalla en las coordenadas locales de cada rect por separado.
Considere el primer caso : cómo convertir coordenadas locales en globales.
La mayoría de los métodos buscados en Google usan TransformPoint.
transform.position = myCanvas.transform.TransformPoint(pos);
Por lo tanto, convertimos coordenadas locales a globales.
En mi opinión, este es generalmente un paso adicional, ya que RectTransformUtility tiene un método ScreenPointToWorldPointInRectangle que devuelve inmediatamente la posición global.
Necesitamos mover el rect más allá del borde derecho de la pantalla, para esto tomamos la coordenada X desde la posición encontrada, y Y dejamos el rect que nos movemos para que simplemente se mueva hacia la derecha.
new Vector3(topRightCoord.x+offset, rectTransform.position.y, 0);
La coordenada resultante es alimentada por DoTween.
rectTransform.DOMove(new Vector3(correctedTargetRight.x, rectTransform.position.y, 0), timeForHiding);
Y hurra, el objeto sale a la derecha. Pero ...
Segundo matizAquí descubrimos que, de hecho, el posicionamiento del rect depende del pivote del rect.

Por lo tanto, el objeto puede bailar con el posicionamiento dependiendo del pivote, además el objeto puede ser muy grande y el desplazamiento no lo empujará completamente detrás de la pantalla, siempre habrá una posibilidad de que la pieza sobresalga.
Es decir, necesitamos atornillar la compensación para compensar, lo que tendrá en cuenta el tamaño de rect + pivot.
El segundo matiz es mover el objeto por el tamaño del rectángulo, necesita conocer las coordenadas locales o de anclaje, y obtenemos las coordenadas globales. Debo decir de inmediato que las coordenadas globales no se pueden tomar y convertir en coordenadas locales de la interfaz de usuario o anclar.
Se me ocurrió la siguiente muleta, recordamos la posición inicial del rectángulo, lo movemos a la posición global final, desplazamos la posición del ancla por el tamaño del rectángulo a la derecha, recordamos la posición global que tiene en cuenta el desplazamiento, teniendo en cuenta el tamaño del objeto, y lo alimentamos con un hueco, sin olvidar volver al original posición.
Ejemplo de código 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);
Parece una muleta gigante, pero esta muleta le permite sincronizar coordenadas globales y otras. Esto ayuda cuando hay objetos en la interfaz que se mueven uno con respecto al otro, y están en diferentes jerarquías. Bueno, además, hasta ahora, esta es la única forma en que he encontrado para obtener coordenadas rect de global.
En este punto, diremos no a las muletas y volveremos a la idea de obtener el tamaño de la pantalla en coordenadas locales.
Segunda forma
La segunda forma es obtener los tamaños de pantalla para cada rect individualmente, por lo que conoceremos las coordenadas locales de los bordes de la pantalla, independientemente del lienzo o la jerarquía.
Tercer matiz RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(Screen.width, Screen.height), null, out topRightCoord); RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(0, 0), null, out bottomScreenCoord);
Los objetos se pueden ubicar en cualquier lugar de la pantalla, a diferencia del lienzo que cubre toda la pantalla. Por lo tanto, las distancias a los bordes izquierdo y derecho de la pantalla pueden diferir significativamente. En el caso del lienzo, habríamos tenido solo el borde superior derecho, y menos el superior derecho sería el superior izquierdo. En este caso, debe obtener los puntos inferior izquierdo y superior derecho por separado, como se muestra en el ejemplo de código.
Cuarto matizLa coordenada local es el desplazamiento relativo al centro del padre, cuando el rect está incrustado en otro rect, que ocupa una pequeña parte del lienzo, entonces necesitamos una coordenada que tenga en cuenta ambos desplazamientos, bueno, todo es simple.
((Vector3)bottomLeftCoord + rectTransform.localPosition)
agregue los vectores y obtenga la coordenada que necesitamos. Resulta más confuso que con las coordenadas globales, pero ahora podemos realizar cualquier cálculo relacionado con el tamaño del rect. Y finalmente, con calma, agregue una compensación sin muletas.
(Vector3)topRightCoord + rectTransform.localPosition + (new Vector3((rectTransform.sizeDelta.x * rectTransform.pivot.x) + rectTransform.sizeDelta.x, 0, 0));
Así es como se ve la coordenada para desplazarse hacia la derecha con compensación por el ancho del rectángulo y desplazarse más allá de la pantalla al ancho del rectángulo, no hay forma de establecer el desplazamiento, planeo agregarlo un poco más tarde, pero creo que alguien estará interesado en intentar escribir esto yo mismo.
Conclusiones
- Para los elementos de la interfaz de usuario, es mejor usar coordenadas locales o de anclaje, y debe intentar comprenderlas. Las coordenadas globales se pueden usar para casos especiales, pero no permiten trabajar convenientemente, por ejemplo, con el tamaño de los rectos y en muchos otros micro episodios.
- Debe mirar RectTransformUtility, tiene muchas funciones útiles para la interfaz de usuario, todos los cálculos relacionados con la posición de algo dentro y alrededor del rectángulo se realizan a través de él.
Bueno, el componente en sí, si alguien quiere jugar con él, se necesitará DoTween para esto:
Componente 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; } }