RectTransformUtility, ou comment créer un composant qui anime des éléments d'interface utilisateur derrière l'écran

Dans le dernier article - Variétés de coordonnées utilisées dans l'interface graphique Unity3d, j'ai essayé de parler brièvement des variétés de coordonnées dans Unity UI / RectTransform. Maintenant, je veux souligner un peu une chose aussi utile pour l'interface utilisateur que RectTransformUtility. Qui est l'un des principaux outils pour calculer quelque chose dans l'interface utilisateur par rapport à autre chose.

Défi simple


Il y a une tâche - vous avez besoin d'un composant qui supprime de manière animée l'élément d'interface utilisateur au-delà du bord sélectionné de l'écran. Le composant doit être violet à l'endroit où il se trouve hiérarchiquement, à quel endroit se trouvent les ancres, à quelle taille de l'écran et à quel endroit de l'écran il se trouve. Le composant doit pouvoir nettoyer l'objet sur 4 côtés (haut, bas, gauche, droite) pendant un temps donné.

Réflexions

En principe, comment procéder? Découvrez la taille de l'écran dans les coordonnées de l'objet, déplacez l'objet vers les coordonnées au-delà du bord de l'écran, et il semble que la matière soit dans le chapeau. Mais il y en a deux mais:

Comment connaître les coordonnées de l'écran par rapport à l'interface utilisateur?

Si vous google sur le front, alors google des bêtises ou des choses inutiles, ou même des questions sans réponse. La chose la plus proche qui vous convient est lorsqu'un élément de l'interface utilisateur suit le curseur, qui n'existe que dans les coordonnées de l'écran.

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

Ce sont RectTransformUtility et ScreenPointToLocalPointInRectangle directement. Ici, nous obtenons les coordonnées locales à l'intérieur du rect (RectTransform), en fonction de la position du point sur l'écran.
Dans l'exemple actuel, nous trouvons les coordonnées locales du curseur de la souris, nous devons les remplacer par le bord de l'écran:

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

Et donc nous avons obtenu les coordonnées du point supérieur droit de l'écran afin que l'objet quitte l'écran vers la droite, notre objet devrait être plus loin que ce point + disons la largeur du rect ou du retrait spécifié.

Donc, la première mise en garde

Nous avons obtenu des coordonnées locales qui conviennent aux objets directement à l'intérieur de la toile, si la rivière à déplacer se trouve dans un autre rect, alors ses coordonnées locales seront considérées par rapport au parent, pas à la toile. Autrement dit, ces coordonnées elles-mêmes ne nous conviennent pas.

Il y a deux façons , la première est d'utiliser des coordonnées globales, pour cela elles sont globales. Ou calculez séparément les coordonnées de l'écran dans les coordonnées locales de chaque rect.

Considérons le premier cas - comment convertir des coordonnées locales en globales.

La plupart des méthodes googlées utilisent - TransformPoint.

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

Ainsi, nous convertissons les coordonnées locales en globales.

À mon avis, il s'agit généralement d'une étape supplémentaire, car RectTransformUtility a une méthode ScreenPointToWorldPointInRectangle qui renvoie immédiatement la position globale.

Nous devons déplacer le rect au-delà du bord droit de l'écran, pour cela, nous prenons la coordonnée X de la position trouvée, et Y quittons le rect que nous déplaçons pour qu'il se déplace simplement vers la droite.

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

La coordonnée résultante est alimentée par DoTween.

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

Et hourra, l'objet part vers la droite. Mais ...

Deuxième nuance

On constate ici qu'en fait le positionnement du rect dépend du pivot rect.



Par conséquent, l'objet peut danser avec un positionnement en fonction du pivot, plus l'objet peut être très grand et le décalage ne le poussera pas complètement derrière l'écran, il y aura toujours une chance que la pièce dépasse.

Autrement dit, nous devons visser la compensation pour compenser ce qui tiendra compte de la taille de rect + pivot.

La deuxième nuance consiste à déplacer l'objet de la taille du rect, vous devez connaître les coordonnées locales ou d'ancrage, et nous obtenons les coordonnées globales. Je dois dire tout de suite que les coordonnées globales ne peuvent pas être prises et converties en coordonnées UI locales ou ancrées.
Je suis venu avec la béquille suivante, on se souvient de la position de départ du rect, on le déplace vers la position globale finale, on décale la position d'ancrage de la taille du rect à droite, on se souvient de la position globale qui prend en compte le décalage, en tenant compte de la taille de l'objet, et on l'alimente avec un creux, sans oublier de revenir à l'original position.

Exemple de code
  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); 


Il ressemble à une béquille géante, mais cette béquille vous permet de synchroniser les coordonnées globales et autres. Cela est utile lorsque certains objets de l'interface se déplacent les uns par rapport aux autres et qu'ils se trouvent dans des hiérarchies différentes. Eh bien, jusqu'à présent, c'est le seul moyen que j'ai trouvé pour obtenir des coordonnées rectangulaires à partir du global.

À ce stade, nous dirons non aux béquilles et reviendrons à l'idée d'obtenir la taille de l'écran en coordonnées locales.

Deuxième voie


La deuxième façon consiste à obtenir les tailles d'écran pour chaque rect individuellement, afin que nous connaissions les coordonnées locales des bords de l'écran, indépendamment du canevas ou de la hiérarchie.

Troisième nuance

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

Les objets peuvent être situés n'importe où sur l'écran, contrairement au canevas qui couvre tout l'écran. Par conséquent, les distances entre les bords gauche et droit de l'écran peuvent différer considérablement. Dans le cas de la toile, nous n'aurions eu que le bord supérieur droit et moins le coin supérieur droit ce serait le coin supérieur gauche. Dans ce cas, vous devez obtenir les points inférieur gauche et supérieur droit séparément, comme indiqué dans l'exemple de code.

Quatrième nuance

La coordonnée locale est le décalage par rapport au centre du parent, lorsque le rect est intégré dans un autre rect, qui occupe une petite partie du canevas, alors nous avons besoin d'une coordonnée qui prend en compte les deux déplacements, eh bien, tout est simple.

 ((Vector3)bottomLeftCoord + rectTransform.localPosition) 

ajoutez les vecteurs et obtenez les coordonnées dont nous avons besoin. Cela s'avère plus déroutant qu'avec les coordonnées globales, mais maintenant nous pouvons effectuer tous les calculs liés à la taille du rect. Et enfin calmement ajouter une compensation sans béquilles.

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

Voici à quoi ressemble la coordonnée pour se déplacer vers la droite avec compensation de la largeur du rect et se déplacer au-delà de l'écran vers la largeur du rect, il n'y a aucun moyen de régler le décalage, je prévois de l'ajouter un peu plus tard, mais je pense que quelqu'un sera intéressé d'essayer d'écrire cela moi-même.

Conclusions


  1. Pour les éléments d'interface utilisateur, il est préférable d'utiliser des coordonnées locales ou d'ancrage, et vous devez essayer de les comprendre. Les coordonnées globales peuvent être utilisées pour des cas particuliers, mais elles ne permettent pas de travailler facilement, par exemple, avec les tailles de rects et dans de nombreux autres micro-épisodes.
  2. Vous devez regarder RectTransformUtility, il a beaucoup de fonctionnalités utiles pour l'interface utilisateur, tous les calculs liés à la position de quelque chose à l'intérieur et autour du rectangle sont effectués à travers lui.

Eh bien, le composant lui-même, si quelqu'un veut jouer avec lui, DoTween sera nécessaire pour cela:

Composant
 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/fr457142/


All Articles