Работа тумблера заключается в перемещении между нижним и верхним краем панели меню на канвасе. Эти края будут определяться высотой самой панели. И так, завершив настройку элементов канваса можно переходить к программной части.
Создадим новый скрипт CustomSlider унаследованный от класса UIBehaviour, этот класс можно найти подключив библиотеку EventSystem.
- using UnityEngine.EventSystem;
- public sealed class CustomSlider : UIBehaviour {}
Далее объявим новую переменную fillRect, которая будет ссылаться на RectTransform панели меню.
- using UnityEngine.EventSystem;
- public sealed class CustomSlider : UIBehaviour {
- public RectTransform fillRect;
- }
Теперь добавим скрипт
CustomSlider тумблеру и в поле
Fill Rect поместим панель меню.
Так как сам тумблер мы будем перемещать по канвасу перетаскивая его по экрану, нужно будет использовать дополнительные инструменты обработки событий касания. Для этого в системе UI есть целая дюжина интерфейсов, которые помогут обработать каждое действие пользователя, в том числе касания, перемещения, нажатия и тд. В этом примере нам понадобятся только три из них, это IBeginDragHandler – для обработки события начала перетаскивания, IDragHandler – для обработки самого перетаскивания и IEndDragHandler – для обработки окончания перетаскивания элемента.
Наследуем скрипт CustomSlider от каждого из трех выбранных интерфейсов после чего реализуем все их методы.
- using UnityEngine.EventSystem;
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- public void OnBeginDrag(PointerEventData eventData) {}
- public void OnDrag(PointerEventData eventData) {}
- public void OnEndDrag(PointerEventData eventData) {}
- }
Метод OnBeginDrag сработает перед началом перетаскивания тумблера, метод OnDrag будет обрабатывать сам процесс перетаскивания, а метод OnEndDrag сработает после того как мы закончим перетаскивать элемент.
Каждый метод принимает параметр PointerEventData, этот класс содержит много полезных данных от каждого события, но нам понадобится только два из них: это камера, от которой поступило событие касания и сама позиция касания.
Для проверки работоспособности скрипта попробуем перемещать тумблер в методе OnDrag. Для этого нужно будет всего лишь перевести позицию касания на экране в мировую точку на сцене, а потом в локальную точку относительно канваса, выглядит это так:
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- public void OnBeginDrag(PointerEventData eventData) {}
- public void OnDrag(PointerEventData eventData) {
- Camera eventCam = eventData.pressEventCamera;
- Vector2 worldPoint = eventCam.ScreenToWorldPoint(eventData.position);
- Vector2 localPoint = this.canvas.transform.InverseTransformPoint(worldPoint);
- this.transform.localPosition = localPoint;
- }
- public void OnEndDrag(PointerEventData eventData) {}
- }
Получив локальную позицию localPoint при помощи метода InverseTransformPoint Transform’а канваса, применяем эти координаты Transform’у тумблера. Теперь можно запустить и попробовать “повозить” тумблер по экрану.
Отлично, все работает – события обрабатываются исправно можно продолжать работу со скриптом.
Для начала сотрем все что написали в методе OnDrag и добавим несколько новых переменных: canvasRect и rectTransform, которые будут указывать на Transform’ы канваса и тумблера.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- public void OnBeginDrag(PointerEventData eventData) {}
- public void OnDrag(PointerEventData eventData) {}
- public void OnEndDrag(PointerEventData eventData) {}
- }
Дальше нам понадобится хранить высоту панели меню в переменной fillHeight, высоту изображения тумблера imageHeight, нижний предел в переменной minPosY, верхний предел в maxPosY и текущую высоту тумблера в переменной targetPosY.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
-
- public RectTransform fillRect;
-
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, imageHeight, minPosY, maxPosY, targetPosY;
-
- public void OnBeginDrag(PointerEventData eventData) {}
- public void OnDrag(PointerEventData eventData) {}
- public void OnEndDrag(PointerEventData eventData) {}
-
- }
Этих переменных нам будет достаточно для начала. Далее в методе Start займемся сбором данных.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- protected override void Start() {}
- /*…остальной код…*/
- }
Начнем с определения переменных Transform’а канваса и тумблера. Для того, чтобы найти канвас, будем использовать метод GetComponentInParent.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- protected override void Start() {
- Canvas canvas = GetComponentInParent<Canvas>();
- this.canvasRect = canvas.transform as RectTransform;
- this.rectTransform = this.transform as RectTransform;
- }
- /*…остальной код…*/
- }
Дальше, в методе Start определим размеры панели меню и тумблера, но делать мы это будем двумя разными способами.
Дело в том, что у тумблера в настройках компонента RectTransform указаны его длина и высота (Width и Height), а у панели меню указаны только отступы лево, верх, право и низ (Left, top, right и bottom), то есть панель всегда, независимо от размеров экрана, будет заполнять максимум родительского пространства (в данном случае весь канвас). Поэтому узнать размеры панели через длину и высоту, как у тумблера – не получится, для этого придется использовать другой подход, а именно – найти расположения краев панели на канвасе.
И так, начнем с высоты тумблера, здесь все просто – используем свойство sizeDelta у его Transform’а.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- protected override void Start() {
- Canvas canvas = GetComponentInParent<Canvas>();
- this.canvasRect = canvas.transform as RectTransform;
- this.rectTransform = this.transform as RectTransform;
- this.imageHeight = this.rectTransform.sizeDelta.y;
- }
- /*…остальной код…*/
- }
Чтобы найти края панели меню нам понадобится небольшой массив векторов Vector3 и метод GetLocalCorners, который вернет все четыре угла Transform’а панели.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- protected override void Start() {
- Canvas canvas = GetComponentInParent<Canvas>();
- this.canvasRect = canvas.transform as RectTransform;
- this.rectTransform = this.transform as RectTransform;
- this.imageHeight = this.rectTransform.sizeDelta.y;
- Vector3[] fillCorners = new Vector3[4];
- this.fillRect.GetLocalCorners(fillCorners);
- }
- /*…остальной код…*/
- }
Вообще все углы любого элемента UI располагаются по часовой стрелке начиная с нижнего левого края элемента.
Теперь, используя края панели, определим ее размеры, а также нижний и верхний пределы.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- protected override void Start() {
- Canvas canvas = GetComponentInParent<Canvas>();
- this.canvasRect = canvas.transform as RectTransform;
- this.rectTransform = this.transform as RectTransform;
- this.imageHeight = this.rectTransform.sizeDelta.y;
- Vector3[] fillCorners = new Vector3[4];
- this.fillRect.GetLocalCorners(fillCorners);
- this.fillHeight = Mathf.Abs(fillCorners[0].y * 2f);
- this.maxPosY = fillCorners[0].y + this.imageHeight / 2f;
- this.minPosY = fillCorners[1].y – this.imageHeight / 2f;
- }
- /*…остальной код…*/
- }
Для высоты fillHeight используем нижний левый угол панели под индексом 0 массива fillCorners умноженный на 2. Нижний предел minPosY будет располагаться в левом нижнем углу панели, верхний предел maxPosY – в левом верхнем углу соответственно.
Готово, после выполнения метода Start мы получим все необходимые данные об элементах панели и тумблера.
Само же перемещение тумблера теперь будем обрабатывать в методе Update, так как в отличие от метода OnDrag, метод Update работает всегда, а не только когда мы совершаем действие “перетаскивания”. Поэтому добавим новый метод Update, и два дополнительных метода для обработки перемещения тумблера UpdatePosition и заполнения панели UpdateFill.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- /*…остальной код…*/
- private void Update() {
- UpdateFill();
- UpdatePosition();
- }
- private void UpdateFill() {}
- private void UpdatePosition() {}
- /*…остальной код…*/
- }
Для перемещения тумблера будем использовать переменную targetPosY которую будем заполнять в методе OnDrag.
- public sealed class CustomSlider : UIBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
- public RectTransform fillRect;
- private RectTransform canvasRect, rectTransform;
- private float fillHeight, minPosY, maxPosY, imageHeight, targetPosY;
- /*…остальной код…*/
- private void Update() {
- UpdateFill();
- UpdatePosition();
- }
- private void UpdateFill() {}
- private void UpdatePosition() {}
- public void OnDrag(PointerEventData eventData) {
- Camera eventCam = eventData.pressEventCamera;
- Vector2 worldPoint = eventCam.ScreenToWorldPoint(eventData.position);
- Vector2 localPoint = this.canvasRect.InverseTransformPoint(worldPoint);
- this.targetPosY = localPoint.y;
- }
- /*…остальной код…*/
- }