(Unity 4.6) Is it possible for UI buttons to be non-rectangular?

I’d like to have buttons that are not rectangular. Circle, hexagon, odd shapes… is this possible? I’d heard rumors that this can work with masking, but I haven’t observed this to be true.

I finally found a way to make it work for certain shapes, without resorting to custom code.

  1. Create a button.
  2. Create an image. Make it a child of the button.
  3. Offset the image so that at least part of the image is not within the quad area of the button.
  4. Press Play, and mouse over the child image. It should be a clickable surface for the button.

Now, if you create a transparent image, and use that as a child of a button, you can make a new, hidden clickable area of the button. With a little creativity, you can use three overlapping rectangular images to designate a hexagonal button area.

Obviously, this technique won’t work well for certain shapes (e.g. circles, any shape with an angle less than 90 degrees). However, it certainly helps me, as I was wanting a hexagonal button.

https://docs.unity3d.com/ScriptReference/UI.Image-alphaHitTestMinimumThreshold.html looks promising. Has anyone experimented with it?

figured it out from @robertes 's answer:

  1. in setting of your button’s sprite image with alpha, check : Advanced → Read/Write Enabled,
  2. load the Image of the button in script on Start(), give alphaHitTestMinimumThreshold value as you wish, more than 0f

codes from the unity documentation:

public Image theButton;

// Use this for initialization
void Start()
{
    theButton.alphaHitTestMinimumThreshold = 0.5f;
}

It really works!

You can script custom areas/shapes for event interaction via ICanvasRaycastFilter.IsRaycastLocationValid. Here’s a similar forum post I used for reference. Here’s an implementation script I use to have an optional BoxCollider2D “mask”:

public class RaycastFilter : MonoBehaviour, ICanvasRaycastFilter
{
    private RectTransform rectTransform;
    private new BoxCollider2D collider2D;

    public void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
        collider2D = GetComponent<BoxCollider2D>();
    }

    public bool IsRaycastLocationValid(Vector2 screenPosition, Camera raycastEventCamera) //uGUI callback
    {
        // If we don't have a collider to check against, any raycast can be captured
        if (collider2D == null)
            return true;

        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPosition, raycastEventCamera, out localPoint);

        Vector2 pivot = rectTransform.pivot - new Vector2(0.5f, 0.5f);

        Vector2 pivotScaled = Vector2.Scale(rectTransform.rect.size, pivot);

        Vector2 realPoint = localPoint + pivotScaled;

        Rect colliderRect = new Rect(
            collider2D.center.x - collider2D.size.x / 2,
            collider2D.center.y - collider2D.size.y / 2,
            collider2D.size.x,
            collider2D.size.y); // TODO: CACHE

        bool containsRect = colliderRect.Contains(realPoint);

        return containsRect;
    }
}

I would imagine you can extend this to whatever needs, including actual sprite’s pixels (though sliced and such would need fancier UV maths). Here’s another forum post using Mask.

If you don’t want to pay the additional cost that comes with making a texture Writable, you can use this class:

Right now it works with the sprite’s Shape from the Shape Editor, but you can easily convert it to using a mathematical shape like a circle or single triangle.