Недавно у меня возникла необходимость использовать в своем проекте кнопки нестандартной формы. Однако эта на первый взгляд тривиальная задача вызвала у меня некоторые затруднения.

Проблема заключается в том, что стандартная кнопка UI обрабатывает нажатия даже по прозрачным областям, что в данном случае совсем не желательно. При этом у самого компонента Button нет никаких (видимых в инспекторе) параметров, которые могли бы отвечать за то, какие области кнопки могли бы обрабатывать наведение/нажатие.


Спустя некоторое время поисков в интернете и курения документации ко мне пришло следующее, при этом довольно простое, решение:

(Сразу оговорюсь, что подобный пример есть в документации Unity, однако на данный момент его код уже помечается как устаревший, поэтому я решил его актуализировать и немного доработать)

using UnityEngine;
using UnityEngine.UI;

public class ButtonClickMask : MonoBehaviour 
{
    [Range(0f, 1f)] //1
    public float AlphaLevel = 1f; //2
    private Image bt; //3
   
    void Start()
    {
        bt = gameObject.GetComponent<Image>();
        bt.alphaHitTestMinimumThreshold = AlphaLevel; //4
    }
}

  1. Атрибут, который выведет в инспекторе удобный бегунок со значениями от 0 до 1.
  2. Минимальное значение альфа-канала, которое должна иметь часть текстуры, на которую наведен курсор, чтобы обрабатывать нажатия.
  3. Компонент Image кнопки (работать нужно именно с ним, а не с Button).
  4. Параметр alphaHitTestMinimumThreshold как раз и отвечает за то, какой минимальный уровень прозрачности должен быть у части текстуры, чтобы она могла обработать нажатие.

Итак, скрипт готов. Теперь перейдем к настройке текстуры кнопки. Чтобы код работал и не выдавал ошибок, необходимо включить возможность чтения/записи в Import Settings текстуры. Не забудьте принять изменения, нажав Apply.



Теперь вешаем скрипт на нужную кнопку, выставляем Alpha Level на нужное значение (в моем случае — 1)…



… запускаем и радуемся, что кнопка теперь не обрабатывает нажатия по прозрачным областям!

Комментарии (8)


  1. VIONICK08
    04.12.2019 17:46

    Это распространяется только на обработку нажатий или на эффекты при наведении, выделении и т. д. тоже?


    1. TheGreatB Автор
      04.12.2019 18:09

      Этот параметр влияет также и на зоны наведения на кнопку (видно на последней гифке)


  1. SteeLMinder
    04.12.2019 18:46

    Это в принципе хорошая вещь, но использование Read/Write делает текстуру в памяти x2. А разве включение Use Sprite Mesh не делает тоже самое ?


    1. TheGreatB Автор
      04.12.2019 19:00

      Сейчас проверил — не делает (хотя есть вероятность, что я что-то не так настроил).


  1. TxN
    05.12.2019 07:27

    Есть более экономный вариант: Использовать коллайдер для отсечения нежелательных областей:
    gist.github.com/TxN/344c216ecd31cca5850ea7294fd5f0a6
    Да, придется нарисовать коллайдер, но зато по ресурсам проверить попадание точки в многоугольник будет гораздо менее затратно.


    1. TheGreatB Автор
      05.12.2019 11:28

      Такой вариант тоже возможен, но на мой личный взгляд, использование коллайдеров в канвасе — довольно странная идея. Но тут уж, думаю, что кому по вкусу.
      Решение же в моей статье более мобильно (требует минимум манипуляций с компонентами), а так же, насколько я понял, именно такой способ подразумевался разработчиками движка как основной для данной задачи.


      1. TxN
        05.12.2019 12:40

        Коллайдер тоже решение не идеальное (но ничто не мешает сделать свой алгоритм, вычисляющий попадание в многоугольник, и устранить эти недостатки), но открывать текстуру для чтения (или целый атлас), тратя этим самым память, которой на мобильных устройствах не всегда много.
        Плюс, этот способ позволяет делать то, что нельзя делать простым альфа-тестом — создать область реакции на клик больше самой кнопки. Или наоборот, если зачем-то надо. Опять же, для мобильных игр вещь очень полезная.


  1. SoraSiro
    05.12.2019 11:07

    Классное решение, спасибо за код