Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

None Rectangle shaped button?

Discussion in 'UGUI & TextMesh Pro' started by LightStriker, Aug 22, 2014.

  1. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    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?
     
  2. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    no
     
  3. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Well, one good news today.
     
  4. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Yep, i tried it out and I successfully dragged three sliders at the same time :cool:
     
  5. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    ;)

    And with the old one, I couldn't press a button if I had a finger somewhere else on the screen at the same time, even if it wasn't on a UI item.
     
  6. senritsu

    senritsu

    Joined:
    Dec 12, 2012
    Posts:
    37
    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.
     
  7. senritsu

    senritsu

    Joined:
    Dec 12, 2012
    Posts:
    37
    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.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. [RequireComponent(typeof(RectTransform))]
    5. [RequireComponent(typeof(Image))]
    6. public class RaycastMask : MonoBehaviour, ICanvasRaycastFilter
    7. {
    8.     private Sprite _sprite;
    9.  
    10.     void Start ()
    11.     {
    12.         _sprite = GetComponent<Image>().sprite;
    13.     }
    14.    
    15.     public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    16.     {
    17.         var rectTransform = (RectTransform)transform;
    18.         Vector2 local;
    19.         RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform) transform, sp, eventCamera, out local);
    20.         // normalize local coordinates
    21.         var normalized = new Vector2(
    22.             (local.x + rectTransform.pivot.x*rectTransform.rect.width)/rectTransform.rect.width,
    23.             (local.y + rectTransform.pivot.y*rectTransform.rect.height)/rectTransform.rect.width);
    24.         // convert to texture space
    25.         var rect = _sprite.textureRect;
    26.         var x = Mathf.FloorToInt(rect.x + rect.width * normalized.x);
    27.         var y = Mathf.FloorToInt(rect.y + rect.height * normalized.y);
    28.         // destroy component if texture import settings are wrong
    29.         try
    30.         {
    31.             return _sprite.texture.GetPixel(x,y).a > 0;
    32.         }
    33.         catch (UnityException e)
    34.         {
    35.             Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'");
    36.             Destroy(this);
    37.             return false;
    38.         }
    39.     }
    40. }
    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.
     
    Last edited: Aug 22, 2014
    DTFun, alikun, equivoque and 7 others like this.
  8. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Interesting. Frankly, that should be a default option of Button.
     
    AdmiralThrawn likes this.
  9. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    Cool script, but it will (unfortunately) only work with 'simple' images as UV's are not 'square' if you are using nine slicing :(

    You could do something like add a 'vertex' modifier, cache the verts then when the click happens look up the correct UV from this dataset.
     
  10. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I think you don't have the way to shape a button from a custom set of vertices? Or is a button always a Rect?
     
  11. senritsu

    senritsu

    Joined:
    Dec 12, 2012
    Posts:
    37
    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.
     
  12. senritsu

    senritsu

    Joined:
    Dec 12, 2012
    Posts:
    37
    As it bugged me, i just went ahead and updated the script to handle sliced sprites as well.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. [RequireComponent(typeof(RectTransform))]
    5. [RequireComponent(typeof(Image))]
    6. public class RaycastMask : MonoBehaviour, ICanvasRaycastFilter
    7. {
    8.     private Image _image;
    9.     private Sprite _sprite;
    10.  
    11.     void Start ()
    12.     {
    13.         _image = GetComponent<Image>();
    14.     }
    15.  
    16.     public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    17.     {
    18.         _sprite = _image.sprite;
    19.  
    20.         var rectTransform = (RectTransform)transform;
    21.         Vector2 localPositionPivotRelative;
    22.         RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform) transform, sp, eventCamera, out localPositionPivotRelative);
    23.  
    24.         // convert to bottom-left origin coordinates
    25.         var localPosition = new Vector2(localPositionPivotRelative.x + rectTransform.pivot.x*rectTransform.rect.width,
    26.             localPositionPivotRelative.y + rectTransform.pivot.y*rectTransform.rect.height);
    27.      
    28.         var spriteRect = _sprite.textureRect;
    29.         var maskRect = rectTransform.rect;
    30.  
    31.         var x = 0;
    32.         var y = 0;
    33.         // convert to texture space
    34.         switch (_image.type)
    35.         {
    36.          
    37.             case Image.Type.Sliced:
    38.             {
    39.                 var border = _sprite.border;
    40.                 // x slicing
    41.                 if (localPosition.x < border.x)
    42.                 {
    43.                     x = Mathf.FloorToInt(spriteRect.x + localPosition.x);
    44.                 }
    45.                 else if (localPosition.x > maskRect.width - border.z)
    46.                 {
    47.                     x = Mathf.FloorToInt(spriteRect.x + spriteRect.width - (maskRect.width - localPosition.x));
    48.                 }
    49.                 else
    50.                 {
    51.                     x = Mathf.FloorToInt(spriteRect.x + border.x +
    52.                                          ((localPosition.x - border.x)/
    53.                                          (maskRect.width - border.x - border.z)) *
    54.                                          (spriteRect.width - border.x - border.z));
    55.                 }
    56.                 // y slicing
    57.                 if (localPosition.y < border.y)
    58.                 {
    59.                     y = Mathf.FloorToInt(spriteRect.y + localPosition.y);
    60.                 }
    61.                 else if (localPosition.y > maskRect.height - border.w)
    62.                 {
    63.                     y = Mathf.FloorToInt(spriteRect.y + spriteRect.height - (maskRect.height - localPosition.y));
    64.                 }
    65.                 else
    66.                 {
    67.                     y = Mathf.FloorToInt(spriteRect.y + border.y +
    68.                                          ((localPosition.y - border.y) /
    69.                                          (maskRect.height - border.y - border.w)) *
    70.                                          (spriteRect.height - border.y - border.w));
    71.                 }
    72.             }
    73.                 break;
    74.             case Image.Type.Simple:
    75.             default:
    76.                 {
    77.                     // conversion to uniform UV space
    78.                     x = Mathf.FloorToInt(spriteRect.x + spriteRect.width * localPosition.x / maskRect.width);
    79.                     y = Mathf.FloorToInt(spriteRect.y + spriteRect.height * localPosition.y / maskRect.height);
    80.                 }
    81.                 break;
    82.         }
    83.  
    84.         // destroy component if texture import settings are wrong
    85.         try
    86.         {
    87.             return _sprite.texture.GetPixel(x,y).a > 0;
    88.         }
    89.         catch (UnityException e)
    90.         {
    91.             Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'");
    92.             Destroy(this);
    93.             return false;
    94.         }
    95.     }
    96. }
    github
    Example mask: http://imgur.com/a/oZMEQ

    The example worked completely fine without doing anything else compared to simple sprites.
     
    Olipool, Chunikus, felixmann and 6 others like this.
  13. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    Super sexy :)
     
  14. tawsm

    tawsm

    Joined:
    Nov 12, 2013
    Posts:
    43
    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?

    mask.PNG
     
  15. senritsu

    senritsu

    Joined:
    Dec 12, 2012
    Posts:
    37
    I just re-tried the script in a clean project with a random mask, and it works fine for me with beta 20.



    For the import settings i just set it to UI sprite, and then to advanced to enable read/write.

    Can you do a minimum working example (or more like failing example) of your problem, and upload it somewhere?
     
    daydream_xbox likes this.
  16. tawsm

    tawsm

    Joined:
    Nov 12, 2013
    Posts:
    43
    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.
    MaskIssue.jpg
     
  17. senritsu

    senritsu

    Joined:
    Dec 12, 2012
    Posts:
    37
    Good to hear that you got it to work :)
    I wonder why those settings are not available in my screenshot though :O Is it a pro thing? Or has that to do with enabling the sprite packer or something in the editor settings?
     
  18. froglord

    froglord

    Joined:
    Oct 7, 2014
    Posts:
    8
    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.
     
  19. IntDev

    IntDev

    Joined:
    Jan 14, 2013
    Posts:
    152
    What's the advantage of mesh type Tight over Full Rect, for 2d Sprites?
     
  20. froglord

    froglord

    Joined:
    Oct 7, 2014
    Posts:
    8
    someone at unite 2014 did a pretty good thing on that (8m32s):
     
    Last edited: Oct 16, 2014
    IntDev likes this.
  21. Juruhn

    Juruhn

    Joined:
    Jun 25, 2013
    Posts:
    13
    Yay, worked for me. Thnx !
     
  22. margusmuru

    margusmuru

    Joined:
    Oct 8, 2013
    Posts:
    18
    Last edited: Mar 5, 2015
    Eater_Games likes this.
  23. StaffanEk

    StaffanEk

    Joined:
    Jul 13, 2012
    Posts:
    380
    No, you could do something like that, so that the feature will be updated along with Unity.

    Scripts by users to add basic functionality to the UI system run a risk of breaking when Unity is updated. Which is a pain in the ass.
     
  24. jniris

    jniris

    Joined:
    Jan 28, 2015
    Posts:
    1
  25. margusmuru

    margusmuru

    Joined:
    Oct 8, 2013
    Posts:
    18
    DTFun and huyakinemota_unity like this.
  26. kjempff

    kjempff

    Joined:
    Aug 14, 2013
    Posts:
    3
    Works fine for the GUI events being triggered, but..
    When I try to EventSystem.current.RaycastAll I still get the GUI elements for the entire square, ignoring the raycast mask implementation of ICanvasRaycastFilter.IsRaycastLocationValid.
     
  27. miguelSantirso

    miguelSantirso

    Joined:
    Aug 12, 2013
    Posts:
    3
    I found a simpler (a bit hacky maybe) option using a Collider2D and a custom raycast filter:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. [RequireComponent (typeof (RectTransform), typeof (Collider2D))]
    5. public class Collider2DRaycastFilter : MonoBehaviour, ICanvasRaycastFilter
    6. {
    7.     Collider2D myCollider;
    8.     RectTransform rectTransform;
    9.    
    10.     void Awake ()
    11.     {
    12.         myCollider = GetComponent<Collider2D>();
    13.         rectTransform = GetComponent<RectTransform>();
    14.     }
    15.  
    16. #region ICanvasRaycastFilter implementation
    17.     public bool IsRaycastLocationValid (Vector2 screenPos, Camera eventCamera)
    18.     {
    19.         var worldPoint = Vector3.zero;
    20.         var isInside = RectTransformUtility.ScreenPointToWorldPointInRectangle(
    21.             rectTransform,
    22.             screenPos,
    23.             eventCamera,
    24.             out worldPoint
    25.         );
    26.         if (isInside)
    27.             isInside = myCollider.OverlapPoint(worldPoint);
    28.  
    29.         return isInside;
    30.     }
    31. #endregion
    32. }
    33.  
    This could be improved by creating a custom alternative to replace the Collider2D but that's a bit too much work for what I need.
     
    andyz likes this.
  28. Cromfeli

    Cromfeli

    Joined:
    Oct 30, 2014
    Posts:
    202
    I am getting this error when trying to reproduce your result:

    importer.GetNPOTScale() == TextureImporter::kNPOTKeep
    UnityEditor.DockArea:OnGUI()

    When I set the texture mode to advanced and sprite single on Unity5.1.0f3

    Edit: Error got removed when I set the texture type back to Sprite 2D and then again to advanced. So no idea what was causing it but this seemed to avoid the problem.
     
    Last edited: Jun 10, 2015
  29. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    anyway for RawImage ?
     
  30. Ox_

    Ox_

    Joined:
    Jun 9, 2013
    Posts:
    93
    Could you please share if there's any chance we can get non-rectangular button/toggle functionality out of the box in the future versions?
     
  31. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    I would like to offer one more solution to the problem: we can use objects texture and check its alpha when deciding whether to respond to input events. That way UI objects will not react to input in any transparent areas, which seems like the most versatile solution.

    I’ve made a plugin that works like that: https://www.assetstore.unity3d.com/en/#!/content/28601

    It also handles any transformations (scale, rotation), anchor setups, works with both orthographic and perspective camera modes (screen/world spaces), supports atlases and all the filled image modes. Been using it myself in several projects for all kind of non-rect buttons\toggles and should say it does it’s job quite good :)

     
    Cromfeli, tawsm and StaffanEk like this.
  32. moghes

    moghes

    Joined:
    Oct 10, 2012
    Posts:
    14
    Hello,

    first thanks to @senritsu for this cool script.

    I was searching for the best method to "None Rectangel shaped button".
    And I found this post, however it works partially for me and can't figure out the problem.

    I have triangle buttons, with this script, transparent area are not clickable as I wanted, but some colored/non-transparent areas are being disabled too.
    I am printing the alpha values of the current mouse positon, and in the green areas in the picture, alpha is printed 0 and the button is not clickable.
    This happens randomly since I have another triangle buttons, where the borders are respected completed, while others have different edge-areas not working .. I though of a collider-rlated issue, and now using polygon colliders, and colliders expand to the borders of my triangle image, but the behavoir was the same.

    Can you tell me what my issue might be related to or any hints?

    Thanks!
    quest1.jpg
     
  33. SpyGamer13

    SpyGamer13

    Joined:
    May 5, 2017
    Posts:
    2
    Hello there
    You can make a panel and add on it a "button component".
    And now, you can change colors on press or above it!
     
  34. Margravio

    Margravio

    Joined:
    Oct 29, 2017
    Posts:
    1
    Hi,
    I tried both the proposed methods, the alphaHitTestMinimumThreshold and the RaycastMask script.
    They work fine except there seems to be a vertical offset between the pixels of the sprite and the position of the mouse, when the on enter/exit events occur.

    I attach a test project to show the problem: you can see that when you put the mouse in the transparent area, very near the cars icons, the image reacts with a vertical offset of about 5 pixels.

    What could be the problem?

    Thank you
     

    Attached Files:

  35. Adrien-Gannerie

    Adrien-Gannerie

    Joined:
    Feb 18, 2015
    Posts:
    12
    Hi, i updated the RaycastMask class with all the Image type included if you want

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. [RequireComponent(typeof(RectTransform))]
    5. [RequireComponent(typeof(Image))]
    6. public class RaycastMask : MonoBehaviour, ICanvasRaycastFilter
    7. {
    8.     #region Properties
    9.     Image m_Image;
    10.     Sprite m_Sprite;
    11.     #endregion
    12.  
    13.  
    14.     #region Public Methods
    15.     public bool IsRaycastLocationValid(Vector2 screenPosition, Camera eventCamera)
    16.     {
    17.         // Set sprite
    18.         m_Sprite = m_Image.sprite;
    19.  
    20.         // SetRectTransform
    21.         RectTransform rectTransform = (RectTransform)transform;
    22.  
    23.         // GetLocalPosition relative to pivot point
    24.         Vector2 localPositionPivotRelative;
    25.         RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)transform, screenPosition, eventCamera, out localPositionPivotRelative);
    26.  
    27.         // convert to bottom-left origin coordinates
    28.         Vector2 localPosition = new Vector2(localPositionPivotRelative.x + rectTransform.pivot.x * rectTransform.rect.width,
    29.             localPositionPivotRelative.y + rectTransform.pivot.y * rectTransform.rect.height);
    30.  
    31.         Rect spriteRect = m_Sprite.textureRect;
    32.         Rect maskRect = rectTransform.rect;
    33.  
    34.         Vector2Int pixelPosition = new Vector2Int(0, 0);
    35.         Vector2 ratioPosition = new Vector2(localPosition.x / maskRect.width, localPosition.y / maskRect.height);
    36.        
    37.         bool isValid = false;
    38.  
    39.         switch (m_Image.type)
    40.         {
    41.             case Image.Type.Filled:
    42.                 {
    43.                     pixelPosition = new Vector2Int(Mathf.FloorToInt(spriteRect.x + spriteRect.width * ratioPosition.x), Mathf.FloorToInt(spriteRect.y + spriteRect.height * ratioPosition.y));
    44.                     if( m_Image.fillMethod == Image.FillMethod.Vertical || m_Image.fillMethod == Image.FillMethod.Horizontal)
    45.                     {
    46.                         float position = 0;
    47.                         switch (m_Image.fillMethod)
    48.                         {
    49.                             case Image.FillMethod.Horizontal:
    50.                                 position = ratioPosition.x;
    51.                                 break;
    52.                             case Image.FillMethod.Vertical:
    53.                                 position = ratioPosition.y;
    54.                                 break;
    55.                         }
    56.                         isValid = (m_Image.fillOrigin == 0 && position <= m_Image.fillAmount) || (m_Image.fillOrigin == 1 && position >= (1 - m_Image.fillAmount));
    57.                     }
    58.                     else
    59.                     {
    60.                         Vector2 ratioRelativeToCenter = new Vector2();
    61.                         float positionAngle = 0, startFillAngle = 0, variableFillAngle = 0;
    62.                         switch (m_Image.fillMethod)
    63.                         {
    64.                             case Image.FillMethod.Radial90:
    65.                                 variableFillAngle = Mathf.PI / 2;
    66.                                 switch ((Image.Origin90)m_Image.fillOrigin)
    67.                                 {
    68.                                     case Image.Origin90.BottomLeft:
    69.                                         ratioRelativeToCenter = ratioPosition;
    70.                                         startFillAngle = 0;
    71.                                         break;
    72.                                     case Image.Origin90.BottomRight:
    73.                                         ratioRelativeToCenter = ratioPosition + Vector2.left;
    74.                                         startFillAngle = Mathf.PI / 2;
    75.                                         break;
    76.                                     case Image.Origin90.TopLeft:
    77.                                         ratioRelativeToCenter = ratioPosition + Vector2.down;
    78.                                         startFillAngle = -Mathf.PI / 2;
    79.                                         break;
    80.                                     case Image.Origin90.TopRight:
    81.                                         ratioRelativeToCenter = ratioPosition - Vector2.one;
    82.                                         startFillAngle = -Mathf.PI;
    83.                                         break;
    84.                                 }
    85.                                 break;
    86.                             case Image.FillMethod.Radial180:
    87.                                 variableFillAngle = Mathf.PI;
    88.                                 switch ((Image.Origin180)m_Image.fillOrigin)
    89.                                 {
    90.                                     case Image.Origin180.Bottom:
    91.                                         ratioRelativeToCenter = new Vector2(2 * ratioPosition.x - 1, ratioPosition.y);
    92.                                         startFillAngle = 0;
    93.                                         break;
    94.                                     case Image.Origin180.Right:
    95.                                         ratioRelativeToCenter = new Vector2(ratioPosition.x - 1, 2 * ratioPosition.y - 1);
    96.                                         startFillAngle = Mathf.PI / 2;
    97.                                         break;
    98.                                     case Image.Origin180.Left:
    99.                                         ratioRelativeToCenter = new Vector2(ratioPosition.x, 2 * ratioPosition.y - 1);
    100.                                         startFillAngle = -Mathf.PI / 2;
    101.                                         break;
    102.                                     case Image.Origin180.Top:
    103.                                         ratioRelativeToCenter = new Vector2(2 * ratioPosition.x - 1, ratioPosition.y - 1);
    104.                                         startFillAngle = -Mathf.PI;
    105.                                         break;
    106.                                 }
    107.                                 break;
    108.                             case Image.FillMethod.Radial360:
    109.                                 ratioRelativeToCenter = new Vector2(2 * ratioPosition.x - 1, 2 * ratioPosition.y - 1);
    110.                                 variableFillAngle = 2 * Mathf.PI;
    111.                                 switch ((Image.Origin360)m_Image.fillOrigin)
    112.                                 {
    113.                                     case Image.Origin360.Bottom:
    114.                                         startFillAngle = -Mathf.PI / 2;
    115.                                         break;
    116.                                     case Image.Origin360.Top:
    117.                                         startFillAngle = Mathf.PI / 2;
    118.  
    119.                                         break;
    120.                                     case Image.Origin360.Left:
    121.                                         startFillAngle = -Mathf.PI;
    122.                                         break;
    123.                                     case Image.Origin360.Right:
    124.                                         startFillAngle = 0;
    125.                                         break;
    126.                                 }
    127.                                 break;
    128.                         }
    129.                         positionAngle = Mathf.Atan2(ratioRelativeToCenter.y, ratioRelativeToCenter.x);
    130.                         if (positionAngle < startFillAngle) positionAngle += 2 * Mathf.PI;
    131.                         isValid = (m_Image.fillClockwise && positionAngle >= startFillAngle + variableFillAngle * (1 - m_Image.fillAmount)) || (!m_Image.fillClockwise && positionAngle <= startFillAngle + variableFillAngle * m_Image.fillAmount);
    132.                     }
    133.                 }
    134.                 break;
    135.             case Image.Type.Sliced:
    136.                 {
    137.                     Vector4 border = m_Sprite.border;
    138.                     isValid = true;
    139.  
    140.                     // x slicing
    141.                     if (localPosition.x < border.x) pixelPosition.x = Mathf.FloorToInt(spriteRect.x + localPosition.x);
    142.                     else if (localPosition.x > maskRect.width - border.z) pixelPosition.x = Mathf.FloorToInt(spriteRect.x + spriteRect.width - (maskRect.width - localPosition.x));
    143.                     else pixelPosition.x = Mathf.FloorToInt(spriteRect.x + border.x + ((localPosition.x - border.x) / (maskRect.width - border.x - border.z)) * (spriteRect.width - border.x - border.z));
    144.  
    145.                     // y slicing
    146.                     if (localPosition.y < border.y) pixelPosition.y = Mathf.FloorToInt(spriteRect.y + localPosition.y);
    147.                     else if (localPosition.y > maskRect.height - border.w) pixelPosition.y = Mathf.FloorToInt(spriteRect.y + spriteRect.height - (maskRect.height - localPosition.y));
    148.                     else pixelPosition.y = Mathf.FloorToInt(spriteRect.y + border.y + ((localPosition.y - border.y) / (maskRect.height - border.y - border.w)) * (spriteRect.height - border.y - border.w));
    149.                 }
    150.                 break;
    151.             case Image.Type.Simple:
    152.             default:
    153.                 {
    154.                     isValid = true;
    155.                     pixelPosition = new Vector2Int(Mathf.FloorToInt(spriteRect.x + spriteRect.width * ratioPosition.x), Mathf.FloorToInt(spriteRect.y + spriteRect.height * ratioPosition.y));
    156.                 }
    157.                 break;
    158.         }
    159.  
    160.         try
    161.         {
    162.             isValid &= m_Sprite.texture.GetPixel(pixelPosition.x,pixelPosition.y).a > 0;
    163.             return isValid;
    164.         }
    165.         catch (UnityException e)
    166.         {
    167.             Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'");
    168.             return false;
    169.         }
    170.     }
    171.     #endregion
    172.  
    173.     #region Private Methods
    174.     void Start ()
    175.     {
    176.         m_Image = GetComponent<Image>();
    177.     }
    178.     #endregion
    179.  
    180. }
    181.  
     
  36. huyakinemota_unity

    huyakinemota_unity

    Joined:
    Feb 4, 2018
    Posts:
    3
    Eater_Games and DTFun like this.
  37. Newermind

    Newermind

    Joined:
    Aug 10, 2019
    Posts:
    4
    Theese scripts are so long and difficult to understand... I dig out 1 solution from some guy. Easy in 2 steps - add script to button object; setup your custom image import settings.

    Enjoy your custom button :)


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class ButtonClickToVisibleOnly : MonoBehaviour
    7. {
    8.     public float alphaTreshold = 0.1f;
    9.  
    10.     /*
    11.      * IMAGE MUST HAVE IN SETTINGS
    12.      *          TEXTURE TYPE - SPRITE (2D AND UI)
    13.      *          READ/WRITE ENABLED
    14.      */
    15.  
    16.  
    17.     void Start()
    18.     {
    19.         this.GetComponent<Image>().alphaHitTestMinimumThreshold = alphaTreshold;
    20.     }
    21.  
    22. }
    upload_2020-12-31_16-14-37.png
     
  38. Skotrap7

    Skotrap7

    Joined:
    May 24, 2018
    Posts:
    125
    This works so perfectly and doesn't require any complex script or asset purchase.
     
  39. Hend_d

    Hend_d

    Joined:
    Feb 28, 2017
    Posts:
    5
    Any better solution than that yet?