Button with radial fill sprite only clickable on visible area?

I’m trying to create a dynamic radial menu where the amount of buttons can easily be changed. I had the idea of using a single circular sprite for the background of the buttons and adjusting the fill amount to create any size of button I need.

However, I’ve run into a problem. The clickable area of the buttons seems to cover the entire sprite, not just the area that is visible according to the fill amount. I tried setting Image.alphaHitTestMinimumThreshold to more than zero but it doesn’t affect the areas of the sprite hidden by fill amount, just the areas that are transparent in the original sprite. I also tried adding a mask that would hide parts of the button, but apparently you can click buttons even through a mask, which seems a bit weird to me.

Is there a way to create a button that uses a radial fill sprite, where only the visible are is clickable?

It is possible if you are good at math.
What you have to do is creating a custom class and derive it from image.
Then override IsRaycastLocationValid() and calculate if the given point should be detected or not.
As a reference you can look into the original image code: https://bitbucket.org/Unity-Technologies/ui/src/a3f89d5f7d145e4b6fa11cf9f2de768fea2c500f/UnityEngine.UI/UI/Core/Image.cs?at=2017.3&fileviewer=file-view-default

@Hosnkobf
Hey thanks for the tip, that solved it. You had me scared there but it only needed some very basic vector math.

What I did was I calculated the origin vector and the click point vector, then compared the angle between those vectors to the fill amount.

1 Like

I don’t understand what it is you did exactly to solve your issue either. Could someone provide an code example, or explain a little more in depth more on how I can calculate the angle between the original vector and the click point vector and implement it into a script that’ll only make the fill areas clickable?

For anyone struggling with the same problem, here the solution:

using UnityEngine;
using UnityEngine.UI;

public class ImageFilledClickableSlices : Image {
    public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) {
        bool result = base.IsRaycastLocationValid(screenPoint, eventCamera);
        if (!result) {
            return false;
        }
        Vector2 localPoint;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out localPoint);
        float clickAngle = Vector2.SignedAngle(localPoint, new Vector2(0, -1));
        return (clickAngle >= 0) && (clickAngle < (360f * fillAmount));
    }
}
7 Likes

This should get added to the base/core UI code – no reason not to, it isn’t expensive (at least relative to existing alpha test capabilities), and I think it’d be very helpful for many developers to have this be built-in & work directly out of the box!

So, to me the float “clickAngle” was returning a value between [0,180] when the cursor is bellow 0.5 of the fillAmount, and then from [-180, 0] when above 0.5 fillAmount. This was causing to only detect the cursor halfway through the button. To fix it, I just added this code right before the return:
if (clickAngle < 0) clickAngle += 360;