GUI 皮肤的介绍

游戏必须为其玩家提供清晰、信息丰富且视觉上令人愉悦的用户界面。虽然 Control 节点具有开箱即用的功能外观,但始终有独特性和特定于案例的调整空间。为此,Godot 引擎包括一个用于 GUI 换肤(或主题化)的系统,它允许您自定义用户界面中每个控件的外观,包括您的自定义控件。

下面是这个系统的一个例子—— 一个游戏的 GUI 与引擎的默认 UI 主题完全不同:

../../_images/tank-kings-by-winterpixel-games.png

《坦克王》中的“装备起来!”界面,由 Winterpixel Games 提供

除了为您的游戏实现独特的外观外,该系统还使开发人员能够为最终用户提供自定义选项,包括交互设置。 UI 主题以级联方式应用(即从父控件传播到其子控件),这意味着色盲用户的字体设置或调整,可以在从某处应用并影响整个 UI 树。当然,这个系统也可以用于游戏:基于英雄的游戏可以为选定的玩家角色改变其风格,或者您可以为基于团队的项目中的双方赋予不同的风格。

主题基础知识

皮肤系统由 Theme 资源驱动。每个 Godot 项目都有一个固有的默认主题,其中包含内置控制节点使用的设置。这就是使控件具有开箱即用的独特外观的原因。然而,主题仅用于描述配置,并且每个单独控件的工作仍然是按照显示自身所需的方式使用该配置。在实现 :ref:`你自己的自定义控件<doc_custom_gui_controls>`时要记住这一点很重要。

注解

甚至 Godot 编辑器本身也依赖于默认主题。但它看起来与 Godot 项目不同,因为它在默认主题之上应用了自己高度定制的主题。原则上,这与在您的游戏中的工作方式完全相同,如下所述 <doc_gui_theme_in_project> 。

主题项目

存储在一个主题中的配置由主题项目组成。每个项目都有一个唯一的名称,并且必须是以下数据类型之一:

  • Color

    color 值,通常用于字体和背景。颜色也可用于控件和图标的调制。

  • 常量

    整型值,可用于控件的数字类型属性(例如 BoxContainer <class_BoxContainer> 的间隙设置)或布尔值标记(例如 Tree <class_Tree> 中是否绘制关系线条)。

  • 字体

    一个字体资源(ref font <class_Font> ),常常被用于显示控件中的文字。字体包含了许多渲染设置,像字体的大小和颜色.之后呢,用另一个单独的控件来控制对齐属性和文字方向.

  • 图标

    一个材质资源(ref:texture <class_Texture>),通常用于显示一个图标或者图片(例如在按钮中 ref:Button <class_Button>).

  • 样式

    一个样式盒资源(ref:StyleBox <class_StyleBox>),是一个用来定义一个UI面板怎样展示的配置项集合.不只是用于面板控件(ref:Panel <class_Panel>),它还常常用于许多控件的背景设置和遮罩设置.

主题类型

用来帮助组织分配这些项目中每个主题该属于哪些类型,并且每个项目只能属于单个类型.另一种说法是,用命名来定义每个主题项目,它是数据类型也是它的主题类型.这些组合体中的主题必须是唯一的.例如,标签(Label)类型中不可能有2个颜色项目都是叫做 字体颜色(font_color),但是在另一个线条编辑(LineEdit)中也可以出现``字体颜色(font_color)``项目.

Godot的默认主题诞生之初就已经定义了众多的主题类型,它内建于每个使用了UI皮肤的控件节点中.在默认主题里上述例子都是目前再用的主题项目.你可以在每个控件的类反射中溯源一下**主题属性**区域看看哪些项目是父类和子类都可用的.

注解

子类可以使用为其父类定义的主题项, Button 及其派生类型就是很好实例。事实上,如果需要的话,每个控件都可以使用任何主题类型的单个主题项,但为了清晰可控,在引擎中尽量避免这样做。

牢记子类中,哪些过程是自动执行的很重要.不论什么时候内建控件在主题里面请求主题项目时,我们可以忽略主题类型仅通过它的类名知悉.之后呢,下次时我们能根据它的父级类名来使用.可以通过改变父级类,例如``Button``,来影响所有派生类,而不是调整每一个类来实现.

你当然可以定义你自己的主题类型,并且此外你还可以自定义所有内建控件和自建控件.因为内建控件不会有你自定义主题的内容,所以你必须使用脚本把它们加入到这些项目中.所有控件节点都有一系列的方法可以用来把当前应用主题的主题项目都例遍出来.这些方法通常是把主题类型做为一个可输入参数.

GDScript

C#

  1. var accent_color = get_color("accent_color", "MyType")
  2. label.add_color_override("font_color", accent_color)
  1. Color accentColor = GetColor("accent_color", "MyType");
  2. label.AddColorOverride("font_color", accentColor);

自定义控件

可以不用主题直接对各个控件节点进行自定义。这种方式称为本地重载。控件的类参考手册中列出的每个主题属性,无论是通过检查器面板还是脚本,都可以在该控件上直接重载。这样就可以针对 UI 中的特定部份进行精细的修改,不影响项目中包括该控件子类在内的其他内容。

../../_images/themecheck.png

本地重载对于提升用户界面的美观程度意义不大,如果你注重一致性的话就更是如此。然而,本地重载对于布局节点而言是不可或缺的。BoxContainerGridContainer 等节点通过主题常量定义其子节点的间隙大小,MarginContainer 用主题项目来保存自定义边距。

控件存在本地主题项目重载时,会直接使用这个值,主题中所提供的值会被忽略。

自定义项目

所有全新项目使用的都是 Godot 提供的默认项目主题。默认主题本身是常量,无法修改,但可以通过自定义主题进行覆盖。设置自定义主题有两种方法:修改项目设置,或者修改场景树控件节点的节点属性。

当前有2个计划设定适用于你的整个计划项目 :gui/theme/custom 允许你设置一种自定义计划项目宽屏主题,并且 gui/theme/custom_font 的功能和默认的字体是一样的。当一个自定义主题的控件节点使用一个主题项目时,如果项目存在它将被当先选中。只有当它不存在项目时,默认主题将被选中。

在一个单独的主题资源中,你可以设置所有Godot控件的默认样式与外观,但是你可以做更多的细节调整.每一个控件节点同样拥有一个:ref:`theme <class_Control_property_theme>`属性,通过这个属性你可以为一个控件的所有节点分支设置一个自定义的主题.那意味着那个控件与其所有的子类,和子类的子类,在回滚当前项目和默认主题之前自定义主题的资源将第一个被检查.

注解

计划设定作为一种变化的替代手段,可以让你通过设置自定义主题资源对几乎整个UI分支中的根控件节点做出相同的影响. 然而运行计划项目时可以充当预期效果展示,当单独场景直接预览或者运行时还将使用默认主题展示。为了解决这个问题你可以为每一个单独场景中的根控件设置相同的主题资源.

例如,你可以在项目主题中为按钮设置特定的样式,希望在弹出对话框中的按钮又有不同的外观。你可以为弹出窗口的根控件设置自定义主题资源,并在该资源中为按钮定义不同的样式。只要弹出窗口的根控件和按钮之间的节点链不中断,这些按钮就会使用最接近它们的主题资源中定义的样式。所有其他控件仍将使用整个项目的主题和默认的主题样式。

综上所述,对于任意控件,其主题项的查找会是这样的:

  1. 检查相同数据类型和名称的本地重写。

  2. 使用控件的类名和父类名:

    1. 从自身开始检查每个控件,看看它是否设置了 theme 属性;

    2. 如果设置了,就在该主题中查找名称、数据、主题类型都相同的项目;

    3. 如果没有自定义主题,或者主题中没有匹配的条目,就前往父控件;

    4. 重复步骤 a 至 c,到场景树的根节点或者非控件节点为止。

  3. 如果存在项目范围的主题,就在这个主题中查找控件的类名。

  4. 在默认主题中查找控件的类名。

即便所有主题中都不存在对应的项目,也会返回一个针对该数据类型的默认值。

超越控件

主题是一种用来保存视觉效果配置的理想资源,也非常合理。虽然其他节点并没有像控件节点一样内置针对主题的支持,但还是可以和使用其他资源一样来使用主题。

举个非控件使用主题的例子:在策略游戏中,相同单位需要根据队伍的不同而使用不同颜色的精灵。可以在主题资源中定义颜色的合集,精灵(在脚本的帮助下)就可以使用这些颜色来绘制纹理。这样做的最大好处是可以为红绿蓝队制作不同的主题但使用相同的主题项目,切换队伍只需要替换资源就可以了。