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.
- Create a button.
- Create an image. Make it a child of the button.
- Offset the image so that at least part of the image is not within the quad area of the button.
- 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:
- in setting of your button’s sprite image with alpha, check : Advanced → Read/Write Enabled,
- 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.