如何使一个看不见的透明按钮起作用?

13

浏览Unity论坛和Q&A网站的一些答案,关于如何制作一个不可见按钮的回答并不适用,因为取消与按钮相关联的图像会使其无法工作。

如何解决这个问题,在保持不可见属性的同时允许按钮实际工作?


只需删除子文本并将图像透明度更改为0。 - Łukasz Motyczka
为什么不呢?会有任何问题吗? - Łukasz Motyczka
1
简而言之,Unity忘记或选择不在OO链中为像图片这样的“可触摸”物体设置“可触摸”的概念。基本上,这意味着我们开发人员必须(以某种方式)为Unity制作自己的Touchable类-这是经典的“回填”OO情况。在“回填”整个问题时,它必须完全自动维护,幸运的是似乎有一个很好的解决方案。 - Fattie
@JoeBlow 我删除了我的回答,因为我使用了你的答案并且它是有效的,但是为了记录,我之前发布的答案是我自己的答案的复制,我认为它是一个有效的答案。 - Ryan Cocuzzo
1
顺便说一句,这是一个非常重要的问题。令人惊讶的是,这是第一次在SO上被提出来。Unity“缺少”可触摸的概念,这是Unity中3或4个“奇怪事情”之一 - 你必须在每个项目中都允许它存在。好问题。 - Fattie
4个回答

25

这是Unity中的一个奇怪问题...

现实世界中100%的项目都需要它,但Unity却忘了做。

简洁版:

每个Unity项目都需要 Touchable.cs

// 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. 使用Unity的普通“创建按钮”编辑器功能

  2. 正如您所知,编辑器功能会自动为您添加两个组件。一个是Text,另一个是Image...

  3. 只需将它们都删除

  4. 将上面的脚本Touchable.cs 拖放到按钮上

完成了。就是这样。

它不会随着Unity升级而“腐烂”。

实际上,您可以通过将Touchable拖放到任何.UI中来使任何东西都具有按钮效果。

再也不需要“添加透明图像”来制作按钮了。


Unity忘记在OO链中抽象出一个“可触摸”的概念。

因此,我们开发者必须从Unity的类中“制作”自己的Touchable类。

这是面向对象编程(OO)中经典的“回填”问题

当进行“回填”时,唯一的问题在于:它必须完全自动维护。只有一个好的解决方案Touchable.cs,所有人都在使用。


因此,在所有真实的Unity项目中,按钮看起来像这样:

enter image description here

第一步是使用Unity的 Button.cs

第二步是添加Touchable.cs

有些团队会创建一个编辑器功能“创建更好的按钮”,它仅仅是制作一个带有Button.cs + Touchable.cs的游戏对象。

重要提示...

假设您的UI面板非常复杂。因此它需要调整大小甚至具有动画效果。

事实上,您只需将“Button+Touchable”拖放到任何类似的对象上,它就能正常工作。

只需设置Button+Touchable以扩展填充其父级。就是这样。

enter image description here

在此示例图像中,“resume”和“quit”可以是任何东西。(动画、复杂的带有多个部分、文本、精灵、不可见的东西、堆栈 - 任何东西。)

在所有情况下,只需在其下面放置Button+Touchable,即可获得完美的按钮。

事实上:这种方法非常简单,您可能会将其用于甚至是简单的情况。

假设您的按钮只是一个微不足道的图像。使用图像并将Button+Touchable拖放到它上面要简单得多。(而不是使用编辑器中令人困惑和问题多多的“Button”功能。)

了解该情况...

1) Unity的Button.cs类非常好。

2) 但是


1
从Unity 2018.2开始,您不再需要像这样绕路了: 只需将图像的alpha设置为0,并在CanvasRenderer组件上检查cullTransparentMesh即可。 这将导致一个不可见的按钮,没有任何渲染(绘制调用)。 - hanxu
嗨@hanxu!问题的重点在于要正确地做到这一点,也就是说,不使用“透明图像”。(削减网格与此无关,您需要一个可以放置在任何需要触摸的物体上的解决方案,并且在2018年没有值得注意的变化。) - Fattie
1
嗨Fattie,ugui按钮需要一个图形来执行光线投射,并且使用cullTransparentMesh选项将图形的alpha设置为0可以使其不可见而无需渲染费用。这可以实现开发人员对创建不可见按钮的要求。添加额外的编码工作以从文本中派生类,或者仅覆盖UpdateGeometry即可完成相同的操作。大多数项目都有一个小的n * n像素(例如4 * 4)白色图像用于遮罩、绘制线条等等,在这种情况下,我宁愿也使用它,而不是为相同的点编写额外的代码。 - hanxu

7
作为对 Fattie 的答案的一种可能改进,将 Touchable 的基类更改为 Graphic 并覆盖 protected void UpdateGeometry() 似乎可以很好地工作,同时减少了与 Text 相关的(尽管很小的)开销。
public class Touchable:Graphic
{
    protected override void UpdateGeometry() { }
}

这是一个很棒的想法 - 我得去看看!谢谢! - Fattie

-1

我启动了Gimp(那个免费的编码器图形工具)。创建了一个新的图像(任何大小,我选择了10像素x 10像素),在创建对话框中从高级选项中选择其背景应该是透明的。保存文件。将其导出为png,并选择保存背景颜色。将其作为精灵拖入Unity中。将其放到按钮图形上。禁用按钮的文本组件。不需要代码...只需在Gimp中不要绘制任何东西(这是最困难的部分)。


这是一个不好的想法,因为它会导致很多过度绘制。 - Bas Smit

-1

我的第一个解决方案是像下面这样启用禁用组件:

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;
    }
}

但是那并没有起作用。如果按钮的imagetext组件disabled,则按钮点击事件不会触发。其中一个必须enabled才能发送点击事件。

解决方案是将imagetext组件的alpha值设置为0以隐藏,再设置为1以重新显示。它们将被隐藏但不会被禁用,点击events将会起作用。

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;

        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接