RectTransformUtility oder wie man eine Komponente erstellt, die UI-Elemente hinter dem Bildschirm animiert

Im letzten Artikel - In der Unity3d-Benutzeroberfläche verwendete Koordinatenvarianten habe ich versucht, kurz auf die Koordinatenvarianten in Unity UI / RectTransform einzugehen. Jetzt möchte ich eine so nützliche Sache für die Benutzeroberfläche wie RectTransformUtility hervorheben. Welches ist eines der Hauptwerkzeuge für die Berechnung von etwas in der Benutzeroberfläche in Bezug auf etwas anderes.

Einfache Herausforderung


Es gibt eine Aufgabe: Sie benötigen eine Komponente, die das UI-Element animiert über den ausgewählten Bildschirmrand hinaus entfernt. Die Komponente sollte lila sein, wo sie sich hierarchisch befindet, an welchen Stellen sich die Anker befinden, welche Bildschirmgröße und an welcher Stelle sich der Bildschirm befindet. Die Komponente sollte in der Lage sein, das Objekt für eine bestimmte Zeit an 4 Seiten (oben, unten, links, rechts) zu reinigen.

Reflexionen

Wie kann das im Prinzip gemacht werden? Finden Sie die Größe des Bildschirms in den Koordinaten des Objekts heraus, verschieben Sie das Objekt auf die Koordinate jenseits des Bildschirmrandes, und es scheint, dass die Angelegenheit im Hut ist. Aber es gibt ein paar aber:

Wie finde ich Bildschirmkoordinaten in Bezug auf die Benutzeroberfläche heraus?

Wenn Sie auf der Stirn googeln, googeln Sie Unsinn oder nicht nützliche Dinge oder sogar unbeantwortete Fragen. Das Beste, was zu Ihnen passt, ist, wenn ein UI-Element dem Cursor folgt, der nur in den Koordinaten des Bildschirms vorhanden ist.

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

Dies sind RectTransformUtility und ScreenPointToLocalPointInRectangle direkt. Hier erhalten wir die lokalen Koordinaten innerhalb des Rect (RectTransform), basierend auf der Position des Punkts auf dem Bildschirm.
Im aktuellen Beispiel finden wir die lokalen Koordinaten des Mauszeigers. Wir müssen sie durch den Bildschirmrand ersetzen:

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

Und so haben wir die Koordinate des oberen rechten Punkts des Bildschirms erhalten, so dass das Objekt den Bildschirm nach rechts verlässt. Unser Objekt sollte weiter als dieser Punkt sein + sagen wir die Breite des Rechtecks ​​oder des angegebenen Einzugs.

Also die erste Einschränkung

Wir haben lokale Koordinaten, die für Objekte direkt innerhalb der Leinwand geeignet sind. Wenn der zu verschiebende Fluss in einem anderen Rechteck liegt, werden seine lokalen Koordinaten relativ zum übergeordneten Element und nicht zur Leinwand betrachtet. Das heißt, diese Koordinaten selbst passen nicht zu uns.

Es gibt zwei Möglichkeiten : Die erste besteht darin, globale Koordinaten zu verwenden, dafür sind sie global. Oder berechnen Sie die Koordinaten des Bildschirms in den lokalen Koordinaten jedes Rechtecks ​​separat.

Betrachten Sie den ersten Fall - wie lokale Koordinaten in globale konvertiert werden.

Die meisten gegoogelten Methoden verwenden - TransformPoint.

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

Daher konvertieren wir lokale Koordinaten in globale.

Meiner Meinung nach ist dies im Allgemeinen ein zusätzlicher Schritt, da RectTransformUtility über eine ScreenPointToWorldPointInRectangle-Methode verfügt, die die globale Position sofort zurückgibt.

Wir müssen das Rechteck über den rechten Bildschirmrand hinaus verschieben. Dazu nehmen wir die X-Koordinate von der gefundenen Position und Y verlässt das Rechteck, das wir bewegen, so dass es sich einfach nach rechts bewegt.

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

Die resultierende Koordinate wird von DoTween gespeist.

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

Und Hurra, das Objekt geht nach rechts. Aber ...

Zweite Nuance

Hier stellen wir fest, dass die Positionierung des Rect tatsächlich vom Rect-Pivot abhängt.



Daher kann das Objekt je nach Drehpunkt mit Position tanzen. Außerdem kann das Objekt sehr groß sein und durch den Versatz wird es nicht vollständig hinter den Bildschirm gedrückt. Es besteht immer die Möglichkeit, dass das Teil hervorsteht.

Das heißt, wir müssen die Schraubenkompensation auf den Versatz einstellen, der die Größe von Rect + Pivot berücksichtigt.

Die zweite Nuance besteht darin, das Objekt um die Größe des Rect zu verschieben. Sie müssen die lokalen oder Ankerkoordinaten kennen und wir erhalten die globalen Koordinaten. Ich muss sofort sagen, dass globale Koordinaten nicht genommen und in lokale UI-Koordinaten oder in Ankerkoordinaten konvertiert werden können.
Ich habe mir die folgende Krücke ausgedacht: Wir erinnern uns an die Startposition des Rect, verschieben sie in die endgültige globale Position, verschieben die Ankerposition um die Größe des Rect nach rechts, merken uns die globale Position, die den Versatz berücksichtigt, berücksichtigen die Größe des Objekts und füttern sie mit einem Hohlraum, ohne zu vergessen, zum Original zurückzukehren Position.

Codebeispiel
  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); 


Es sieht aus wie eine riesige Krücke, aber mit dieser Krücke können Sie globale und andere Koordinaten synchronisieren. Dies ist hilfreich, wenn sich Objekte in der Schnittstelle relativ zueinander bewegen und sich in unterschiedlichen Hierarchien befinden. Nun, und bis jetzt ist dies der einzige Weg, den ich gefunden habe, um direkte Koordinaten von global zu erhalten.

An dieser Stelle werden wir Nein zu Krücken sagen und zu der Idee zurückkehren, die Bildschirmgröße in lokalen Koordinaten zu erhalten.

Zweiter Weg


Die zweite Möglichkeit besteht darin, die Bildschirmgrößen für jedes Rechteck einzeln zu ermitteln, damit wir die lokalen Koordinaten der Bildschirmränder unabhängig von Leinwand und Hierarchie kennen.

Dritte Nuance

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

Objekte können sich an einer beliebigen Stelle auf dem Bildschirm befinden, im Gegensatz zur Leinwand, die den gesamten Bildschirm abdeckt. Daher können sich die Abstände zum linken und rechten Bildschirmrand erheblich unterscheiden. Bei Leinwand hätten wir nur den oberen rechten Rand gehabt, und abzüglich des oberen rechten wäre es der obere linke. In diesem Fall müssen Sie den unteren linken und oberen rechten Punkt separat abrufen, wie im Codebeispiel gezeigt.

Vierte Nuance

Die lokale Koordinate ist der Versatz relativ zur Mitte des übergeordneten Elements. Wenn das Rechteck in ein anderes Rechteck eingebettet ist, das einen kleinen Teil der Leinwand einnimmt, benötigen wir eine Koordinate, die beide Verschiebungen berücksichtigt. Nun, alles ist einfach.

 ((Vector3)bottomLeftCoord + rectTransform.localPosition) 

Addiere die Vektoren und erhalte die Koordinate, die wir brauchen. Es ist verwirrender als bei globalen Koordinaten, aber jetzt können wir alle Berechnungen durchführen, die sich auf die Größe des Rect beziehen. Und ruhig endlich Ausgleich ohne Krücken hinzufügen.

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

So sieht die Koordinate aus, wenn Sie nach rechts verschieben, um die Breite des Rechtecks ​​zu kompensieren und über den Bildschirm hinaus zur Breite des Rechtecks ​​zu verschieben. Es gibt keine Möglichkeit, den Versatz festzulegen. Ich plane, ihn etwas später hinzuzufügen, aber ich denke, jemand wird daran interessiert sein, dies selbst zu schreiben.

Schlussfolgerungen


  1. Für UI-Elemente ist es besser, lokale oder Ankerkoordinaten zu verwenden, und Sie sollten versuchen, diese zu verstehen. Globale Koordinaten können für spezielle Fälle verwendet werden, ermöglichen jedoch nicht das bequeme Arbeiten, beispielsweise mit der Größe von Rekten und in vielen anderen Mikroepisoden.
  2. Sie müssen sich RectTransformUtility ansehen, es verfügt über viele nützliche Funktionen für die Benutzeroberfläche. Alle Berechnungen, die sich auf die Position von etwas innerhalb und um das Rechteck beziehen, werden über das RectTransformUtility ausgeführt.

Nun, die Komponente selbst, wenn jemand damit spielen möchte, wird DoTween dafür benötigt:

Komponente
 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/de457142/


All Articles