0
\$\begingroup\$

I have an image that is attached to a panel and I want to detect On Pointer Enter and exit. The event is working fine but currently, it is working on the full image while I want to detect this event on an inner oval shape. Actual the remaing are of the image is transparent. enter image description here

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Assuming you have the bounding rectangle that wraps the ellipse. There is a transformation from that to a circle. You can create the transformation that maps that rectangle to a square.

You can use it to transform the position of the mouse pointer, and then check the distance to the center and compare with the radius (the same as the radius of the square you mapped to), and that will tell you if the mouse pointer is in the ellipse or not.


I have an implementation of an ellipse touch joystick in GDScript. The following is based on that.

We will need a rectangle defined in "global coordinates" (meaning the same coordinates of the mouse pointer). That rectangle is the rectangle that bounds the ellipse.

Let us say that:

  • size is a vector with the width and height of the rectangle.
  • position is a vector with the left and top of the rectangle.

We define offset as size * 0.5. And center as position + offset.

Then we convert from "global coordinates" to "local coordinates" (meaning coordinates with center = (0,0), and center + offset = (1,1), meaning that the rectangle goes from (-1,-1) to (1,1)) like this (GDScript):

func global_to_local(global_position: Vector2):
    return (global_position - center) / offset;

Once we have the "local coordinates", just checking the length of the vector will do.


This is my interpretation of global_to_local in C#:

Vector2 GlobalToLocal(Vector2 globalPosition)
{
    var diff = globalPosition - center;
    return new Vector2(diff.x / offset.x, diff.y / offset.y);
}

Check GlobalToLocal(mousePosition).magnitude <= 1.0f.


I suppose another way to go about it is GetComponent<Image>().sprite.texture.GetPixel(x,y).a > 0.0f. Which would be getting the pixel color and checking if it is not fully transparent. I don't know how to about the transformation to get the right coordinates to query the texture. I suppose it is similar to what I describe above, except mapped to the dimensions of the texture.

Mapping (-1,-1) to (0,0) and (1,1) to (w,h) is

return new Vector2((p.x + 1.0) * 0.5 * w, (p.y + 1.0) * 0.5 * h);

Thus, doing the transformation I described earlier (except for the rectangle of the panel instead of the rectangle of the ellipse) and then this transformation should give the coordinates for GetPixel.

\$\endgroup\$
1
\$\begingroup\$

By far the easiest solution would be setting the Image.alphaHitTestMinimumThreshold to anything other then 0. This is a property, so you can't set it in the inspector. Also note that you need to set the Sprite import settings correctly. In my test I had to set the following but there might be others, so if anyone finds any other that need to be set, please comment on this answer...

  1. Read/write Enabled (Unity will throw an error if you don't do this)
  2. Mesh type set to full rect (In my test it didn't follow the transparent edge correctly when set to tight)

Example code would be:

[RequireComponent(typeof(Image))]
public class Example : MonoBehaviour
{
    private void Start()
    {
        GetComponent<Image>().alphaHitTestMinimumThreshold = 0.5f;
    }
}

Where 0.5f is hardcoded in this example, but could ofcourse be set to your needs.

If you need a more sophisticated way of determining which parts are clickable and which parts aren't, you could also implement ICanvasRaycastFilter with your own custom logic.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .