Dalam artikel terakhir -
Varietas koordinat yang digunakan dalam GUI Unity3d, saya mencoba untuk berbicara singkat tentang varietas koordinat di Unity UI / RectTransform. Sekarang saya ingin menyoroti sedikit hal yang berguna untuk UI sebagai RectTransformUtility. Yang merupakan salah satu alat utama untuk menghitung sesuatu di UI dalam kaitannya dengan sesuatu yang lain.
Tantangan sederhana
Ada tugas - Anda memerlukan komponen yang menghapus elemen UI secara animasi di luar tepi layar yang dipilih. Komponen harus berwarna ungu di mana ia berada secara hierarkis, di tempat mana jangkar berada, ukuran layar apa, dan di mana tempat layar itu berada. Komponen harus dapat membersihkan objek di 4 sisi (atas, bawah, kiri, kanan) untuk waktu tertentu.
RefleksiPada prinsipnya, bagaimana ini bisa dilakukan? Cari tahu ukuran layar dalam koordinat objek, pindahkan objek ke koordinat di luar tepi layar, dan tampaknya masalahnya ada di topi. Tapi ada beberapa tapi:
Bagaimana cara mengetahui koordinat layar relatif terhadap UI?Jika Anda google di dahi, maka google beberapa hal yang tidak berguna atau tidak berguna, atau bahkan pertanyaan yang belum terjawab. Hal terdekat yang cocok untuk Anda adalah ketika beberapa elemen UI mengikuti kursor, yang hanya ada di koordinat layar.
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Input.mousePosition), null, out topRightLocalCoord);
Ini adalah RectTransformUtility dan ScreenPointToLocalPointInRectangle secara langsung. Di sini kita mendapatkan koordinat lokal di dalam kotak (RectTransform), berdasarkan posisi titik di layar.
Dalam contoh saat ini, kami menemukan koordinat lokal kursor mouse, kami perlu menggantinya dengan tepi layar:
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Screen.width, Screen.height), null, out topRightLocalCoord);
Dan jadi kami mendapat koordinat titik kanan atas layar sehingga objek meninggalkan layar ke kanan, objek kami harus lebih jauh dari titik ini + katakanlah lebar kotak atau inden yang ditentukan.
Jadi, peringatan pertamaKami mendapat koordinat lokal yang cocok untuk objek langsung di dalam kanvas, jika sungai yang akan dipindahkan terletak di kotak lain, maka koordinat lokalnya akan dianggap relatif terhadap induknya, bukan kanvas. Artinya, koordinat ini sendiri tidak cocok untuk kita.
Ada dua cara , yang pertama adalah menggunakan koordinat global, untuk itu bersifat global. Atau hitung koordinat layar dalam koordinat lokal setiap kotak secara terpisah.
Pertimbangkan kasus pertama - bagaimana mengkonversi koordinat lokal ke global.
Sebagian besar metode yang di-Google-go - TransformPoint
transform.position = myCanvas.transform.TransformPoint(pos);
Karenanya, kami mengonversi koordinat lokal ke global.
Menurut pendapat saya, ini umumnya merupakan langkah ekstra, karena RectTransformUtility memiliki metode ScreenPointToWorldPointInRectangle yang segera mengembalikan posisi global.
Kita perlu menggeser kotak di luar tepi kanan layar, untuk ini kita mengambil koordinat X dari posisi yang ditemukan, dan Y meninggalkan kotak yang kita pindahkan sehingga hanya bergerak ke kanan.
new Vector3(topRightCoord.x+offset, rectTransform.position.y, 0);
Koordinat yang dihasilkan diumpankan oleh DoTween.
rectTransform.DOMove(new Vector3(correctedTargetRight.x, rectTransform.position.y, 0), timeForHiding);
Dan hore, benda itu pergi ke kanan. Tapi ...
Nuansa keduaDi sini kita mengetahui bahwa sebenarnya posisi rect tergantung pada pivot rect.

Oleh karena itu, objek dapat menari dengan posisi tergantung pada pivot, ditambah objek bisa sangat besar dan offset tidak akan mendorongnya sepenuhnya di belakang layar, akan selalu ada kemungkinan potongan akan menonjol.
Artinya, kita perlu mengacaukan kompensasi untuk offset yang akan memperhitungkan ukuran rect + pivot.
Nuansa kedua adalah memindahkan objek dengan ukuran kotak, Anda harus tahu koordinat lokal atau jangkar, dan kami mendapatkan koordinat global. Saya harus segera mengatakan bahwa koordinat global tidak dapat diambil dan dikonversi ke koordinat UI lokal, atau untuk anchor.
Saya datang dengan kruk berikut, kita mengingat posisi awal dari persegi, memindahkannya ke posisi global akhir, menggeser posisi jangkar dengan ukuran persegi ke kanan, mengingat posisi global yang memperhitungkan offset, memperhitungkan ukuran objek, dan memberi makan dengan lubang, tidak lupa untuk kembali ke aslinya. posisi.
Contoh kode 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);
Ini terlihat seperti penopang raksasa, tetapi penopang ini memungkinkan Anda untuk menyinkronkan koordinat global dan lainnya. Ini membantu ketika ada objek di antarmuka yang bergerak relatif satu sama lain, dan mereka berada di hierarki yang berbeda. Yah, ditambah sejauh ini ini adalah satu-satunya cara yang saya temukan untuk mendapatkan koordinat persegi dari global.
Pada titik ini, kita akan mengatakan tidak pada kruk, dan kembali ke ide mendapatkan ukuran layar dalam koordinat lokal.
Cara kedua
Cara kedua adalah untuk mendapatkan ukuran layar untuk setiap kotak secara individual, jadi kita akan tahu koordinat lokal dari tepi layar, terlepas dari kanvas atau hierarki.
Nuansa ketiga RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(Screen.width, Screen.height), null, out topRightCoord); RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(0, 0), null, out bottomScreenCoord);
Objek dapat ditemukan di mana saja di layar, tidak seperti kanvas yang menutupi seluruh layar. Oleh karena itu, jarak ke tepi kiri dan kanan layar dapat berbeda secara signifikan. Dalam kasus kanvas, kita hanya memiliki tepi kanan atas, dan minus kanan atas itu akan menjadi kiri atas. Dalam hal ini, Anda perlu mendapatkan titik kiri bawah dan kanan atas secara terpisah, seperti yang ditunjukkan dalam contoh kode.
Nuansa keempatKoordinat lokal adalah offset relatif terhadap pusat induk, ketika rect tertanam dalam rect lain, yang menempati sebagian kecil kanvas, maka kita membutuhkan koordinat yang memperhitungkan perpindahan, well, semuanya sederhana.
((Vector3)bottomLeftCoord + rectTransform.localPosition)
tambahkan vektor dan dapatkan koordinat yang kita butuhkan. Ternyata lebih membingungkan daripada dengan koordinat global, tapi sekarang kita bisa melakukan perhitungan yang terkait dengan ukuran kotak. Dan dengan tenang akhirnya menambahkan kompensasi tanpa kruk.
(Vector3)topRightCoord + rectTransform.localPosition + (new Vector3((rectTransform.sizeDelta.x * rectTransform.pivot.x) + rectTransform.sizeDelta.x, 0, 0));
Beginilah tampaknya koordinat untuk bergeser ke kanan dengan kompensasi untuk lebar kotak dan bergeser ke luar layar ke lebar kotak, tidak ada cara untuk mengatur offset, saya berencana untuk menambahkannya sedikit nanti, tapi saya pikir seseorang akan tertarik untuk mencoba menulis ini sendiri.
Kesimpulan
- Untuk elemen UI, lebih baik menggunakan koordinat lokal atau jangkar, dan Anda harus mencoba memahaminya. Koordinat global dapat digunakan untuk kasus-kasus khusus, tetapi mereka tidak memungkinkan untuk bekerja dengan baik, misalnya, dengan ukuran rect dan dalam banyak episode mikro lainnya.
- Anda perlu melihat RectTransformUtility, ia memiliki banyak fungsi yang berguna untuk UI, semua perhitungan terkait dengan posisi sesuatu di dalam dan di sekitar persegi panjang dilakukan melaluinya.
Nah, komponen itu sendiri, jika ada yang ingin bermain dengannya, DoTween akan dibutuhkan untuk ini:
Komponen 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; } }