في المقالة الأخيرة - مجموعة
متنوعة من الإحداثيات المستخدمة في Unity3d GUI حاولت التحدث بإيجاز عن أنواع الإحداثيات في Unity UI / RectTransform. الآن أريد تسليط الضوء على شيء مفيد مثل واجهة المستخدم مثل RectTransformUtility. وهي واحدة من الأدوات الرئيسية لحساب شيء ما في واجهة المستخدم فيما يتعلق بشيء آخر.
تحدي بسيط
هناك مهمة - تحتاج إلى مكون يقوم بإزالة عنصر واجهة المستخدم بطريقة متحركة خارج الحافة المحددة من الشاشة. يجب أن يكون المكون أرجوانيًا في مكانه هرميًا ، وفي أي الأماكن توجد نقاط التثبيت ، وحجم الشاشة ، وفي أي مكان على الشاشة. يجب أن يكون المكون قادراً على تنظيف الكائن في 4 جوانب (للأعلى والأسفل واليسار واليمين) لفترة معينة.
التفكيرمن حيث المبدأ ، كيف يمكن القيام بذلك؟ تعرف على حجم الشاشة في إحداثيات الكائن ، انقل الكائن إلى الإحداثي خارج حافة الشاشة ، ويبدو أن الأمر في القبعة. ولكن هناك زوجين ولكن:
كيفية معرفة إحداثيات الشاشة نسبة إلى واجهة المستخدم؟إذا كنت google على الجبين ، فقم google ببعض الأشياء الهراء أو غير المفيدة ، أو حتى الأسئلة التي لم تتم الإجابة عليها. أقرب شيء يناسبك هو عندما يتبع بعض عناصر واجهة المستخدم المؤشر ، والذي يوجد فقط في إحداثيات الشاشة.
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Input.mousePosition), null, out topRightLocalCoord);
هذه هي RectTransformUtility و ScreenPointToLocalPointInRectangle مباشرة. هنا نحصل على الإحداثيات المحلية داخل المستقيم (RectTransform) ، بناءً على موضع النقطة على الشاشة.
في المثال الحالي ، نجد الإحداثيات المحلية لمؤشر الماوس ، نحتاج إلى استبدالها بحافة الشاشة:
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas, new Vector2(Screen.width, Screen.height), null, out topRightLocalCoord);
وهكذا حصلنا على إحداثيات النقطة اليمنى العليا من الشاشة بحيث يترك الكائن الشاشة إلى اليمين ، يجب أن يكون كائننا أبعد من هذه النقطة + دعنا نقول عرض المستقيم أو المسافة البادئة المحددة.
لذلك ، التحذير الأولحصلنا على إحداثيات محلية مناسبة للكائنات مباشرة داخل اللوحة ، إذا كان النهر المراد إزالته يقع في مستطيل آخر ، فسيتم اعتبار الإحداثيات المحلية بالنسبة إلى الأصل ، وليس القماش. وهذا هو ، هذه الإحداثيات نفسها لا تناسبنا.
هناك طريقتان ، الأولى استخدام الإحداثيات العالمية ، لأنها عالمية. أو احسب إحداثيات الشاشة في الإحداثيات المحلية لكل مستقيم على حدة.
النظر في الحالة الأولى - كيفية تحويل الإحداثيات المحلية إلى العالمية.
تستخدم معظم طرق googled - TransformPoint.
transform.position = myCanvas.transform.TransformPoint(pos);
وبالتالي ، نقوم بتحويل الإحداثيات المحلية إلى العالمية.
في رأيي ، هذه خطوة إضافية بشكل عام ، لأن RectTransformUtility لديه أسلوب ScreenPointToWorldPointInRectangle يُرجع فورًا الموضع العالمي.
نحتاج إلى تحويل المستقيم إلى ما بعد الحافة اليمنى من الشاشة ، لذلك نأخذ إحداثيات X من الموضع الموجود ، ونضع Y نتحرك بحيث نتحرك ببساطة إلى اليمين.
new Vector3(topRightCoord.x+offset, rectTransform.position.y, 0);
يتم تغذية الإحداثي الناتج بواسطة DoTween.
rectTransform.DOMove(new Vector3(correctedTargetRight.x, rectTransform.position.y, 0), timeForHiding);
يا له من أمر ، يترك الكائن إلى اليمين. لكن ...
فارق بسيط الثانيةنكتشف هنا أنه في الواقع يعتمد وضع المستقيم على المحور المستقيم.

لذلك ، يمكن للرقص أن يرقص مع وضعه حسب المحور ، بالإضافة إلى أن الكائن يمكن أن يكون كبيرًا جدًا وأن الإزاحة لن تدفعه بالكامل خلف الشاشة ، وستكون هناك دائمًا فرصة لإلغاء القطعة.
هذا هو ، نحن بحاجة إلى تعويض المسمار لإزاحة والتي سوف تأخذ في الاعتبار حجم المحور + المحور.
الفرق الدقيق الثاني هو تحريك الكائن بحجم المستطيل ، وتحتاج إلى معرفة الإحداثيات المحلية أو الإرساء ، ونحصل على الإحداثيات العالمية. يجب أن أقول على الفور أن الإحداثيات العالمية لا يمكن اتخاذها وتحويلها إلى إحداثيات واجهة المستخدم المحلية ، أو إلى الإرساء.
لقد توصلت إلى العكاز التالي ، نتذكر وضع البداية للمستقيم ، وننقله إلى الموضع العالمي النهائي ، ونحول موضع المرساة بحجم المستقيم إلى اليمين ، ونتذكر الموضع العالمي الذي يأخذ الإزاحة في الاعتبار ، مع الأخذ في الاعتبار حجم الكائن ، وتغذيته بالعودة إلى الأصل الموقف.
مثال رمز 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);
يبدو وكأنه عكاز عملاق ، ولكن هذا العكاز يسمح لك بمزامنة الإحداثيات العالمية وغيرها. يساعد هذا عندما تكون هناك كائنات في الواجهة تنتقل إلى بعضها البعض ، وتكون في تسلسلات هرمية مختلفة. حسنًا ، بالإضافة إلى ذلك ، هذه هي الطريقة الوحيدة التي وجدت بها للحصول على إحداثيات مستقيمة من العالمية.
في هذه المرحلة ، سنقول لا للعكازات ، ونعود إلى فكرة الحصول على حجم الشاشة في الإحداثيات المحلية.
الطريقة الثانية
الطريقة الثانية هي الحصول على أحجام الشاشة لكل مستقيمة بشكل فردي ، لذلك سوف نعرف الإحداثيات المحلية لحواف الشاشة ، بغض النظر عن قماش الرسم أو التسلسل الهرمي.
فارق بسيط ثالث RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(Screen.width, Screen.height), null, out topRightCoord); RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, new Vector2(0, 0), null, out bottomScreenCoord);
يمكن أن توجد الكائنات في أي مكان على الشاشة ، على عكس اللوحة التي تغطي الشاشة بأكملها. لذلك ، يمكن أن تختلف المسافات إلى الحواف اليمنى واليسرى من الشاشة بشكل كبير. في حالة اللوحة القماشية ، سيكون لدينا فقط الحافة اليمنى العلوية ، ناقصًا الجانب العلوي الأيمن سيكون الجزء العلوي الأيسر. في هذه الحالة ، تحتاج إلى الحصول على النقطتين اليمنى اليسرى العلوية والسفلية بشكل منفصل ، كما هو موضح في مثال التعليمات البرمجية.
فارق بسيط الرابعالإحداثيات المحلية هي الإزاحة بالنسبة إلى مركز الأصل ، عندما يكون المستطيل مضمنًا في مستطيل آخر ، يشغل جزءًا صغيرًا من اللوحة ، نحتاج إلى إحداثيات تأخذ في الحسبان كلتا الإزاحتين ، حسنًا ، كل شيء بسيط.
((Vector3)bottomLeftCoord + rectTransform.localPosition)
إضافة ناقلات والحصول على تنسيق نحتاج. لقد كان الأمر مربكًا أكثر من الإحداثيات العالمية ، ولكن الآن يمكننا إجراء أي حسابات تتعلق بحجم المستقيم. وأخيرا بهدوء إضافة التعويض دون عكازات.
(Vector3)topRightCoord + rectTransform.localPosition + (new Vector3((rectTransform.sizeDelta.x * rectTransform.pivot.x) + rectTransform.sizeDelta.x, 0, 0));
هذا هو ما يبدو عليه الإحداثي عند الانتقال إلى اليمين مع تعويض عن عرض المستقيم والانتقال خارج الشاشة إلى عرض المستقيم ، لا توجد طريقة لتعيين الإزاحة ، أخطط لإضافته لاحقًا بعض الشيء ، لكنني أعتقد أن شخصًا ما سيكون مهتمًا بمحاولة كتابة هذا بنفسي.
النتائج
- بالنسبة لعناصر واجهة المستخدم ، من الأفضل استخدام إحداثيات محلية أو مرساة ، ويجب أن تحاول فهمها. يمكن استخدام الإحداثيات العالمية للحالات الخاصة ، لكنها لا تتيح العمل بشكل مريح ، على سبيل المثال ، بأحجام المستطيلات وفي العديد من الحلقات الصغيرة الأخرى.
- تحتاج إلى إلقاء نظرة على RectTransformUtility ، فهي تتمتع بالكثير من الوظائف المفيدة لواجهة المستخدم ، حيث تتم جميع العمليات الحسابية المتعلقة بموقع شيء داخل المستطيل وحوله من خلاله.
حسنًا ، المكون نفسه ، إذا أراد أي شخص اللعب به ، ستكون هناك حاجة إلى 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; } }