- Home /
(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.
Answer by Elringus · Nov 07, 2015 at 03:26 PM
Have a look to this asset: https://www.assetstore.unity3d.com/en/#!/content/28601
It checks texture transparency, before deciding whether to send input events for targeted object, which allows a precise interaction with objects that have a non-rect shapes (like circles, triangles, map regions, etc). That way you will be able to make buttons and other interactable UI objects of any shape and they will correctly react to user input.
this thing is called Alpha Raycaster
IT IS AWESO$$anonymous$$E, BUY I$$anonymous$$$$anonymous$$EDIATELY
it is completely supported and updated every few weeks. it's simply a must have until Unity build-in this functionality.
You don't need to purchase an asset from the store to accomplish this. It's quite easy (in Unity 5.5). You use alphaHitTest$$anonymous$$inimumThreshold so Unity ignores the alpha pixels of your image as "hit" zones. I use a small simple script I attach to button components.
First, create a C# script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI; // Required when Using UI elements.
public class AlphaButton : $$anonymous$$onoBehaviour
{
public float AlphaThreshold = 0.1f;
void Start()
{
this.GetComponent<Image>().alphaHitTest$$anonymous$$inimumThreshold = AlphaThreshold;
}
}
Attach this new script to the button component you wish to have a custom hit zone.
Then, you will need to edit your image import settings (your button image) and under Advanced, make sure Read/Write Enabled is checked.
Now, go back to your button component, and in the AlphaButton component in the inspector you can adjust the alpha threshold for your image -- currently set to 0.1f. This value defines the $$anonymous$$imum alpha level of a pixel in your button that qualifies as a "hit zone".
It takes about 60 seconds to apply! Piece of cake, and cheaper than a $30 asset!
$$anonymous$$ake sure you also set the $$anonymous$$esh Type of the sprite to Full Rect!!!
Good job man, ty very much! If I may know where did you find or how did you know that it needs to be Full Rect? You are a life saver!
It works with normal images with alpha but how can I make it work if I change the "Image Type" field to "Filled" and reduce the "Fill Amount"?
This solution only works with texture alpha and doesn't consider image fill types. If you need a complete solution for alpha-checking UI graphic check this Asset Store plugin: https://www.assetstore.unity3d.com/en/#!/content/28601
You can use this class to do it, without having the overhead of writable textures, for free: https://github.com/lucbloom/unity_image_with_hit_detection_on_shape
Answer by robertes · Jan 25, 2017 at 09:14 PM
https://docs.unity3d.com/ScriptReference/UI.Image-alphaHitTestMinimumThreshold.html looks promising. Has anyone experimented with it?
Interesting link but be careful. Having multiple images with Read/Write enabled will slow down the execution of your game.
Yes, it works! But you have to make image readable.
Joystick.GetComponent<Image>().alphaHitTest$$anonymous$$inimumThreshold = 0.01f;
Answer by Screenhog · Mar 27, 2015 at 05:42 PM
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.
Amazing point: I guess it uses "area of any child". Wild stuff!
Note thought that (5.3) it still just uses the overall rectangle of the image - it does not know about or understand transparency.
I was trying to do the hexagons with svgs images and turns out this is the only working solution on the web. Thanks!
That's a lot of steps and performance overhead to avoid dragging in a .cs file to your project.
Can you share a solution with a hexagonal svg and the module SVG Importer?
Answer by tjPark · Oct 19, 2017 at 01:56 AM
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!
Answer by RudyTheDev · Dec 14, 2014 at 04:19 PM
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
.
Your answer
