13

Looking at some of the answers in the Unity forums and Q&A site, the answers for how to make an invisible button do not work because taking away the image affiliated with the button makes it not work.

How do you get around this and keep the invisible property while allowing the button to actually work?

Fattie
  • 27,874
  • 70
  • 431
  • 719
Ryan Cocuzzo
  • 3,109
  • 7
  • 35
  • 64
  • Just remove child text and change Image alpha to 0. – Łukasz Motyczka Apr 27 '16 at 12:57
  • Why not? Will there be any problem? – Łukasz Motyczka Apr 27 '16 at 13:51
  • 1
    In short, **Unity forgot to, or chose not to**, have a "touchable" concept in the OO chain for things like images that are, well, "touchable". Essentially, this means us developers have to (one way or another) make our own `Touchable` class for Unity - which is a classic "backfilling" OO situation. When "backfilling" the overall issue is that it must be perfectly auto-maintain-ing and fortunately there seems to be one good solution. – Fattie Apr 27 '16 at 14:35
  • @JoeBlow I deleted my answer because I used your answer and it was valid, but for the record, the answer that I posted earlier was a copy of MY own answer that I thought was a valid answer – Ryan Cocuzzo Apr 27 '16 at 16:28
  • 1
    By the way this is a **remarkably important question**. It's remarkable it's the first time it's been asked on SO really. The fact that Unity is "missing" a touchable concept, is one of the 3 or 4 "oddball things about Unity" - you have to allow for it in every single project. Great question. – Fattie Apr 27 '16 at 18:00

4 Answers4

25

This is one of those weird things about Unity...

100% of real-world projects need this, but Unity forgot to do it.

Short version:

You need Touchable.cs in every Unity project:

// file Touchable.cs
// Correctly backfills the missing Touchable concept in Unity.UI's OO chain.

using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Touchable))]
public class Touchable_Editor : Editor
     { public override void OnInspectorGUI(){} }
#endif
public class Touchable:Text
     { protected override void Awake() { base.Awake();} }
  1. Use Unity's ordinary 'Create Button' editor function

  2. As you know, the editor function adds two components for you automatically. One is a Text and one is an Image...

  3. Simply delete them both

  4. Drop the above script Touchable.cs on the Button

You are done. That's all there is to it.

It cannot "decay" with Unity upgrades.

You can actually "buttonize" anything in .UI by dropping Touchable on top of it.

Never again "add a transparent Image" to make a button.


Unity forgot to abstract a "touchable" concept in the OO chain.

So, us developers have to make our own Touchable class "from" Unity's classes.

This is a classic "backfilling" problem in OO.

When "backfilling" the only issue is that: it must be perfectly auto-maintaining. There is only one good solution, Touchable.cs, which everyone uses.


So in all real-world Unity projects a button looks like this:

enter image description here

ONE You have Unity's Button.cs

TWO you have to add Touchable.cs

Some teams make an editor function "Create Better Button" which simply makes a game object, with, Button.cs + Touchable.cs.

Important tip...

Say you may have a very complex UI panel. So it resizes or even has an animation.

In fact, you can just drop "Button+Touchable" on to anything like that, and it will work.

Just set the Button+Touchable so as to expand to fill the parent. That's all there is to it.

enter image description here

In this example image, "resume" and "quit" could be anything. (An animation, a complicated panel with many parts, text, sprites, something invisible, a stack - anything.)

In all cases, just drop a Button+Touchable underneath and you have a flawless button.

In fact: this approach is so simple, you'll probably use it for even simple cases.

Say your button is a trivial image. It's much easier to just have an image, and then drop a Button+Touchable on it. (Rather than use the confusing and problematic "Button" function in the editor.)

Understanding the situation...

1) Unity's Button.cs class is fantastic.

2) But the editor function "make a Button" is garbage...

3) It makes an "upside down" button,

4) i.e., it puts a text/image under Button.cs

5) "Button-ness" is something you should be able to add to anything at all. This is precisely how it works with Button+Touchable.

6) So - quite simply -

1. Have anything you want. Text, image, panel, invisible, animation - whatever.

2. Drop Button+Touchable on it - you're done.

That's how everyone does all buttons in Unity!


Historic credit: I believe Unity forum user "signalZak" was the first to think this out many, many years ago!

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 1
    Start from Unity 2018.2, you don't need to do any walkaround like this any more: Just Set image's alpha to 0 and check cullTransparentMesh on CanvasRenderer component. This leads to an invisible button without any rendering (draw call) – hanxu Dec 08 '20 at 08:43
  • hi @hanxu ! The whole point of the question is to do it properly, that is to say *without* using a "transparent image". (cull mesh is unrelated, you need a solution you can drop on anything at all that needs touchability, and there was no change of note in 2018.) – Fattie Dec 08 '20 at 11:56
  • 1
    Hi Fattie, ugui button needs a graphic to do raycast, and set the graphic's alpha to 0 with cullTransparentMesh checked can make it invisible without rendering expense. This can achieve the developers' request to make a invisible button. Add extra coding work to derive a class from text, or override UpdateGeometry just do the same thing. And most projects have a small n*n pixels(e.g 4*4) white image for masking, draw lines and so on, i'd rather to use it in this case too, instead of coding extra thing for same point. – hanxu Dec 09 '20 at 08:30
7

As a possible improvement to Fattie's answer, changing Touchable's base class to Graphic and overriding protected void UpdateGeometry() seems to work quite nicely white reducing the (admittedly minor) overhead associated with Text.

public class Touchable:Graphic
{
    protected override void UpdateGeometry() { }
}
taniwha
  • 371
  • 2
  • 5
-1

My first solution was to enable and disable the components like below:

void showButton(Button buttonToShow, bool show)
{
    Image bImage = buttonToShow.GetComponent<Image>();
    Text bText = buttonToShow.GetComponentInChildren<Text>(); //Text is a child of the Button

    if (bImage != null)
    {
        bImage.enabled = show;
    }

    if (bText != null)
    {
        bText.enabled = show;
    }
}

but that didn't work. If the button's image and text components are both disabled, the button click event will NOT fire. One of them MUST be enabled in able for click events to be sent.

The solution is to set the alpha of both the image and text components to 0 to hide and to 1 to show again. They will be hidden but not disabled and click events will work.

public Button button;

void Start()
{
    //Show Button
    showButton(button, true);

    //Hide Button
    //showButton(button, false);
}

void showButton(Button buttonToShow, bool show)
{
    Image bImage = buttonToShow.GetComponent<Image>();
    Text bText = buttonToShow.GetComponentInChildren<Text>(); //Text is a child of the Button

    if (bImage != null)
    {
        Color tempColor = bImage.color;

        if (show)
        {
            tempColor.a = 1f; //Show 
            bImage.color = tempColor;
        }
        else
        {
            tempColor.a = 0f; //Hide 
            bImage.color = tempColor;

        }
    }

    if (bText != null)
    {
        Color tempColor = bText.color;

        if (show)
        {
            tempColor.a = 1f; //Show 
            bText.color = tempColor;
        }
        else
        {
            tempColor.a = 0f; //Hide 
            bText.color = tempColor;

        }
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
-1

I fired up Gimp (that free coder graphic tool). Created new image (any size, I chose 10 pix x 10 pix), selected from advanced (in create dialog) that it's backgroud should be transparent. Saved the file. Exported it as png with save backgroud color selected. Dragged it into Unity as sprite. Put that to the button graphic. Disbaled the text-component of the button. No code required ... just don't draw anything while in Gimp (that was the hardest part).

esoinila
  • 99
  • 2