I just gave a quick glance at the new UI and dig some in the code of the new classes.
However, one thing I noticed - or failed to find - is if it’s possible to make not-rectangle shaped button. I know I can use transparent bitmap, but then how would the input zone be made to fit that shape? I’ve noticed current button are “clicked” even if you click in a fully transparent zone.
Another thing I would like to know, the old UI had really lot of troubles with multi-touch on phone/tablet. Is this issue still in the new GUI?
One way to do it that would make sense (but doesn’t currently work) is use a mask.
So i would like to extend the question by:
Why do masks not block raycasts? or at least have a toggle for it, since it would hardly make sense to do a texture lookup for every single mask even if it is not really needed.
With the information provided by Tim C in this thread i made a simple component that enables masks to block raycasts, making it possible to have arbitrary shaped buttons with correct interaction.
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
[RequireComponent(typeof(Image))]
public class RaycastMask : MonoBehaviour, ICanvasRaycastFilter
{
private Sprite _sprite;
void Start ()
{
_sprite = GetComponent<Image>().sprite;
}
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
var rectTransform = (RectTransform)transform;
Vector2 local;
RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform) transform, sp, eventCamera, out local);
// normalize local coordinates
var normalized = new Vector2(
(local.x + rectTransform.pivot.x*rectTransform.rect.width)/rectTransform.rect.width,
(local.y + rectTransform.pivot.y*rectTransform.rect.height)/rectTransform.rect.width);
// convert to texture space
var rect = _sprite.textureRect;
var x = Mathf.FloorToInt(rect.x + rect.width * normalized.x);
var y = Mathf.FloorToInt(rect.y + rect.height * normalized.y);
// destroy component if texture import settings are wrong
try
{
return _sprite.texture.GetPixel(x,y).a > 0;
}
catch (UnityException e)
{
Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'");
Destroy(this);
return false;
}
}
}
You have to parent your button to a Mask (with an image in the shape you want for your button) and add the RaycastMask script to the Mask GameObject.
Unfortunately you also have to adjust the import settings of your sprite texture, setting the texture type to Advanced and checking the Read/Write Enabled checkbox, this is necessary to read the sprite pixels.
Script is also available on github with potential updates down the line.
Hm, that is true, i had not thought of that.
Which component contains the generated mesh, i assume the CanvasRenderer?
It might also be possible (easier?) to just recalculate the UVs from the slicing settings of the sprite itself, and only do that if the image is in fact set to sliced. That would also eliminate the need for a manual toggle.
I somehow can’t get the script to work properly. The “collider” is always messed up, and somewhere completely different than where my actual button is.
Did i do something wrong? I just parented my button under my mask object. The mask object has a mask component, an image and the RaycastMask script. My texture is set to Read/Write Enabled.
I also added a screenshot.
Do i need to change something in the Graphic Raycaster on my Canvas as well?
My god, i figured it out! The ‘Mesh Type’ was set to Tight, it needs to be Full Rect -.-
I think they introduced these settings just recently…
Thanks for all your help senritsu, works like a charm now.
Good to hear that you got it to work
I wonder why those settings are not available in my screenshot though Is it a pro thing? Or has that to do with enabling the sprite packer or something in the editor settings?
great script, though I found out the hard way that it doesn’t support the Preserve Aspect option for images.
I tried doing it myself but I couldn’t figure it out.