概述

Godot虽然有内置文档和在线文档,但是对于特定控件的讲解资料是很少的。因此对于使用者可能会造成一定困扰。

笔者(Bilibili@巽星石)一直想实现一个Godot控件的详细教程,为了实现这个目的,我会开始一系列的文章来进行总结,并不断地修改,以便于读者食用。

本篇主要介绍按钮类型控件的基础使用和自定义等。

注意:本篇所述内容皆基于Godot4.2.1版本,不同大小版本下可能会有API的差异。


Button

Button是最基础的按钮控件,也是日常最常用的按钮类型。其继承和派生关系如下图:

image.png

在“检视器”面板你可以看到Button类型的属性:

image.png

设定按钮文本

text属性设定按钮显示的文本,我们可以在“检视器”面板中直接设定,也可以通过代码来设定。

# 改变按钮文本
button.text = "普通按钮"

设定文本的对齐方式

通过alignment属性可以设定按钮文本在按钮矩形区域中的对齐方式。可以直接在“检视器”面板中选择,三个选项分别对应左对齐居中右对齐。默认文本对齐方式为居中

image.png
或者也可以用代码的形式进行设定:

button.alignment = HORIZONTAL_ALIGNMENT_LEFT     # 左对齐
button.alignment = HORIZONTAL_ALIGNMENT_CENTER   # 居中对齐(默认)
button.alignment = HORIZONTAL_ALIGNMENT_RIGHT    # 右对齐

分别对应效果如下:
image.png

设定文本超出按钮矩形区域的行为

因为text属性没有限定文本的长度,所以一定会出现文本超出按钮长度的情况。

  • 默认,按钮不会对文本进行裁切,而是根据设定的文本进行宽度自适应。
  • text_overrun_behaviorclip_text,都用于设定文本超出按钮矩形区域后是否裁切文本。
  • 使用中文文本时,text_overrun_behavior除了默认的“Trim Nothing”选项不施行裁剪外,其他几个选项似乎没有多大的区别,因此你可以忽略这个属性直接启用clip_text。效果是类似的。

下图为设定text_overrun_behavior属性:

image.png

下图为设定clip_text属性:

在这里插入图片描述

或者也可以使用代码形式:

button.clip_text = true  # 启用文本裁切

下面分别是未进行文本裁剪进行裁剪的效果:
在这里插入图片描述

为按钮设定图标

设定图标与按钮的不同位置组合

  • 通过icon_alignmentvertical_icon_alignment两个属性,结合设定文本对齐的alignment属性,可以组合出多种图标和按钮的位置关系。

image.png

以下是常见的几种文本和图标位置关系:

按钮样式属性设置说明
image.png在这里插入图片描述默认,图标在左,文本在右。
image.pngimage.png图标在上,文本在下。
image.pngimage.png文本在右,图标在左。
image.pngimage.png文本在上,图标在下。

创建按钮生成函数

  • 在创建复杂的自定义节点或编辑器插件时,可能需要动态生成按钮,这时使用重复性的代码,就不如直接编写函数或函数库

  • 上面的4种按钮,可以看做是普通按钮的变体特殊版本),我们可以通过对应的4个函数来快速生成,外加一种只有文本的按钮,就是5种按钮,5个函数

# 生成并返回普通纯文本按钮
func button(text:String = "Button") -> Button:
	var btn = Button.new()
	btn.text = text
	return btn

# 生成并返回带有图标的按钮 - 图标在左,文本在右
func icon_left_button(icon:Texture2D,text:String = "Button") -> Button:
	var btn = button(text)
	btn.icon = icon
	return btn
	
# 生成并返回带有图标的按钮 - 图标在右,文本在左
func icon_right_button(icon:Texture2D,text:String = "Button") -> Button:
	var btn = icon_left_button(icon,text)
	btn.icon_alignment = HORIZONTAL_ALIGNMENT_RIGHT
	return btn
	
# 生成并返回带有图标的按钮 - 图标在上,文本在下
func icon_top_button(icon:Texture2D,text:String = "Button") -> Button:
	var btn = icon_left_button(icon,text)
	btn.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
	btn.vertical_icon_alignment = VERTICAL_ALIGNMENT_TOP
	return btn

# 生成并返回带有图标的按钮 - 图标在下,文本在上
func icon_bottom_button(icon:Texture2D,text:String = "Button") -> Button:
	var btn = icon_left_button(icon,text)
	btn.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
	btn.vertical_icon_alignment = VERTICAL_ALIGNMENT_BOTTOM
	return btn

我们为一个HBoxContainer添加用按钮生成函数动态生成的各种按钮。
在这里插入图片描述

代码如下:

extends HBoxContainer

var icon = preload("res://custom_nodes/icons/dir.png")

func _ready():
	# 通过按钮生成函数创建并添加按钮
	add_child(button())
	add_child(button("按钮1"))
	add_child(icon_left_button(icon))
	add_child(icon_right_button(icon))
	add_child(icon_top_button(icon))
	add_child(icon_bottom_button(icon))

运行后可以看到生成的按钮效果:

在这里插入图片描述

纯图标按钮的应用

在创建类似工具栏程序界面时,按钮可以只设定图标,不设定文本,相应的可以设定鼠标提示文本来提示按钮的作用。

Godot编辑器中这种工具栏的设计比比皆是。你也可以在你的Godot桌面应用程序Godot编辑器插件中使用这种工具栏设计。

在这里插入图片描述

同样的我们可以为这种类型的按钮创建生成函数。

# 创建并返回纯图标按钮
func ToolButton(icon:Texture2D,tool_tip:String = "") -> Button:
	var btn = icon_left_button(icon,"")
	btn.tooltip_text = tool_tip
	return btn

让图标跟随按钮缩放

  • expand_icon设定图标是否跟随按钮的大小变化进行动态缩放。
  • 但在一般情况下这会使得小图标放大变得不清晰(低分辨率)。可以使用更高分辨率的图标来解决放大后不清晰的问题。

在这里插入图片描述
图标跟随按钮放大的效果:
image.png

BaseButton

Button继承自按钮基类BaseButton,它提供了所有按钮类型共用的一些基础属性方法信号

以下是BaseButton类型的属性:

image.png

禁用按钮

  • disabled属性规定了按钮是否处于禁用状态,禁用状态下按钮及其文本变为灰色,并且无法响应鼠标点击
  • 这里以普通按钮Button举例,但是其他按钮也可以被禁用。

image.png

切换按下与弹起状态

  • toggle_mode决定按钮是否在按下与**未按下(弹起)**的状态间进行切换,默认值为false
  • 通常默认(toggle_mode == false)情况下,按钮会跟随鼠标按键的按下与松开,经历一个短暂的“按下”-“持续”-“弹起”的过程,而不会一直处于按下状态
  • toggle_mode则改变了这一行为,按下的按钮将不会随鼠标按键的松开而回弹,而是一直保持按下的状态,直到鼠标再次点击并松开后才会回弹,类似于一种开关的状态切换
  • button_pressed设定或判断按钮是否被按下
    • toggle_mode == true状态下,button_pressedtruefalse之间来回切换。
    • toggle_mode == false状态下,鼠标按键按下时button_pressedtrue,鼠标按键松开时则为false
button.button_pressed = true  # 设定按钮的按下(或选中)状态
print(button.button_pressed)  # 返回按钮的按下(或选中)状态

按钮的信号处理

以下是按钮类型拥有的4个信号

在这里插入图片描述

  • button_down:按钮按下后触发
  • button_up:按钮松开时触发
  • pressed:可以看做是button_down+button_up,但是其发出的时机取决于action_mode属性的设置。
    • ★如果action_modeACTION_MODE_BUTTON_PRESS(默认值),则pressed信号在button_down也就是按钮按下时发出,否则在button_up也就是按钮松开时发出。
      image.png
  • toggled:如果toggle_mode == true,则button_downbutton_up以及pressed信号使用的意义就变得有限了,转而可以使用toggled信号来处理按钮开关状态的切换。其参数toggled_on可以看为是否button_pressed

设定按钮可以响应的鼠标按键

  • button_mask设定按钮可以响应的鼠标按键,可以选择鼠标左键、中键、右键三者中的其中一个或任意两个或三个全部。默认情况下按钮只接受左键点击。也就是鼠标中键和右键无法触发button_downbutton_up以及pressed信号。

image.png

因为这是一个多选项,所以你可以选择至少一个或两个或全部三个按键。用代码进行设定时,可以使用|符号分割多个选项。

# 设定按钮能同时响应鼠标左键和右键的点击
button.button_mask = MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_RIGHT

鼠标移入、移出的细节

  • 默认状态下,鼠标在移入按钮矩形范围后使其外观呈现hover状态,而鼠标移出后使其恢复normal状态。
  • 如果按钮被按下
    • 如果随即鼠标按键松开(也就是最常见的鼠标单击情况),则按钮呈现一过性的pressed状态后恢复normal状态,并因为获得焦点而呈现focused状态。
    • 如果鼠标按键持续按下,未松开,如果鼠标在按钮矩形范围内,则按钮呈现持续的pressed+focused状态,此时鼠标移出按钮矩形范围,则按钮恢复normal状态,呈现normal+focused状态。
  • 而如果设定keep_pressed_outsidetrue,则按钮被按下后不松开,鼠标移出按钮区域外时仍然保持按钮被按下的状态,直到鼠标按键松开时才恢复到松开状态。但是由于按钮的pressed信号默认是在button_down时就触发,与button_up没有关系,所以并不影响按钮pressed信号的发出。

创建按钮组

  • 通过为多个按钮的button_group属性创建同一个ButtonGroup资源,可以将多个按钮联系在一起,形成一个按钮组

  • 按钮组的特性是:同一时间只有一个按钮能被按下

  • 创建按钮组的前提:按钮组中的所有按钮都启用了toggle_mode

创建按钮组的方式

我们可以用以下3种方式手动创建按钮组:

  1. 方法一:创建一个Button,为其button_group属性新建一个ButtonGroup资源,然后Ctrl+D复制出多Button控件,这样新复制出的按钮会和第一个按钮共用相同的ButtonGroup资源,从而实现按钮组的创建。
    image.png
  2. 方法二:先创建多个Button,然后在“”场景“”面板的节点树中按ShiftCtrl选择要成组的所有按钮,在检视器面板统一为其button_group属性新建一个ButtonGroup资源(Godot支持这种操作)。
    image.png
  3. 方法三:创建多个Button,选中其中一个,为其button_group属性新建一个ButtonGroup资源,然后右键“复制值”(或者Ctrl+C),然后转到其他要成组的Button节点的button_group属性处,右键“粘贴值”(或Ctrl+V)。
    image.png

运行后就会发现三个按钮只有一个按钮能被按下,类似于在多个可选项中进行单选

image.png

按钮组通常可以用于游戏设定,以及UI界面中简单的标签页效果。

按钮组生成函数

我们以为可以用代码方式创建按钮组,甚至你可以编写函数来生成一个按钮组

下面是一个简单的按钮组生成函数

# 生成并返回使用相同"ButtonGroup"的按钮组
func BtnGup(texts:PackedStringArray) -> Array[Button]:
	var arr:Array[Button] = []
	var gup = ButtonGroup.new()
	for text in texts:
		var btn = Button.new()
		btn.text = text
		btn.toggle_mode = true
		btn.button_group = gup
		arr.append(btn)
	return arr

我们通过一个HboxContainer节点,添加这个按钮组:
在这里插入图片描述
我们为其添加如下代码:

extends HBoxContainer

func _ready():
	# 创建并添加按钮组
	var btngup = BtnGup(["按钮1","按钮2","按钮3"])
	for btn in btngup:
		add_child(btn)
  • 通过调用按钮组生成函数BtnGup(),传入多个按钮的文本,我们就获得了具有统一toggle_modebutton_group设定的多个按钮实例。
  • 再通过遍历添加到HboxContainer容器进行横向排列。

实际运行效果与手动创建无异。

在这里插入图片描述

上面只是创建了Button类型的按钮组生成函数,你也可以继续扩展创建其他按钮类型的按钮组生成函数。或者写一个支持所有按钮类型的按钮组生成函数。

为按钮设定快捷键

  • 通过为按钮的shortcut属性创建Shortcut资源可以为按钮设定快捷键。
  • 甚至你可以为同一个按钮设置多个快捷键,或者为不同的输入设备(如键盘或游戏手柄)设定此按钮的快捷键。在这里插入图片描述
  • Shortcut资源的events属性部分,我们可以设定一个或多个InputEvent
    • 电脑键盘快捷键是InputEventKey,Godot4.2也贴心的为我们提供了按键监听输入,我们只需要点击“配置”按钮,在打开的类似于“输入映射”配置的窗口中按下键盘快捷键就可以自动录入。
      image.png
      关闭后,会自动帮我们填写到其属性中。image.png
  • 我们也可以将手柄按键设为该按钮的快捷键,只需要添加InputEventJoypadButton类型的资源就可以。
    在这里插入图片描述
    同样我们在连接了手柄设备的状态下,可以直接使用按键监听输入的形式快速录入手柄按键信息。

按钮快捷键的细节设定

  • shortcut_feedback属性值决定在按快捷键时,按钮是否显示被按下的时的高亮,默认为true
  • 如果是false,而且toggle_mode=false时,则按快捷键时,按钮本身不会有任何视觉反馈。
  • shortcut_in_tooltip决定是否在鼠标提示文本中添加快捷键信息,默认为true。将鼠标移动到按钮上可以看到用括号括起来的快捷键信息。image.png

我们为按钮添加一段鼠标提示文本。

image.png

再次运行可以看到带有鼠标提示文本和快捷键提示的效果。

image.png

Control

因此按钮本身也是控件,所以Control类型自然在其继承链上,这意为着按钮可以使用继承自Control类型的属性、方法和信号。

image.png

自定义按钮不同状态下的样式

在上面我们已经提到过按钮与鼠标交互时的几种状态切换,也就是下表列出的5种。

状态说明
normal默认(鼠标未移入)
hover鼠标移入
pressed按下
disabled禁用
focus获得焦点

相应的我们可以在主题覆盖部分,设定按钮在五种状态下的“样式盒”(边框、背景、内外边距、圆角等)、文本以及图标的颜色。

image.png

自定义按钮字体和字号

同样是在样式覆盖部分,可以自定义按钮的字体和字号。
image.png

为按钮文本添加描边

  • 样式覆盖常量部分,可以通过outline_size设定按钮文本的描边宽度
  • 样式覆盖颜色部分,通过font_outline_color可以设定描边的颜色

image.pngimage.png

通过两个属性的结合,可以实现按钮文本的描边效果。

image.png

限制按钮图标的最大宽度

样式覆盖常量部分,可以通过icon_max_width设定按钮图标的最大宽度

image.png

设定按钮的焦点顺序和邻居

  • 图形用户界面的控件之间存在一种焦点的东西,在同一个窗口中,通常同一时间只能有一个控件获得焦点。
  • 而且可以人为的设定同一窗口或局部界面中控件的焦点顺序,以及控件的毗邻关系
  • 通过键盘的Tab/Shift+Tab键可以快速向前或向后切换焦点到不同的控件。
  • 用键盘或手柄的方向键或上下左右键可以切换到指定的毗邻控件。

在Godot中我们同样可以为我们的控件设定焦点顺序和上下左右的毗邻控件:

  • 通过focus_neighbor_leftfocus_neighbor_rightfocus_neighbor_topfocus_neighbor_bottom可以设定按钮的左右上下“”邻居“”节点。
    在这里插入图片描述
    可以手动通过检视器面板进行设定。
    image.png
    也可以通过代码设定:
extends Control
@onready var button = $Button
@onready var button_bottom = $ButtonBottom
@onready var button_right = $ButtonRight
@onready var button_left = $ButtonLeft
@onready var button_top = $ButtonTop


func _ready():
	# 为button获取焦点
	button.grab_focus()
	# 为按钮设定上下左右邻居控件
	button.focus_neighbor_left = button_left.get_path()
	button.focus_neighbor_right = button_right.get_path()
	button.focus_neighbor_top = button_top.get_path()
	button.focus_neighbor_bottom = button_bottom.get_path()

运行后,可以看到默认中键按钮获得焦点,通过键盘左右上下键可以控制焦点在几个按钮间切换。

请添加图片描述
更常见的一种情况是,我们需要为多个并排的按钮添加焦点顺序,通过为按钮设定focus_nextfocus_previous,可以将多个按钮连起来,实现焦点顺序

这里我们以三个竖向并列的按钮为例:
在这里插入图片描述

我们通过如下代码,实现三个按钮的焦点顺序:

extends Control
@onready var button1 = $Button1
@onready var button2 = $Button2
@onready var button3 = $Button3


func _ready():
	# 为button1获取焦点
	button1.grab_focus()
	# 为按钮设定焦点
	button1.focus_next = button2.get_path()
	button2.focus_next = button3.get_path()
  • 这里看到我们只使用了focus_next,而没有使用focus_previous,这是因为在焦点顺序中,设定一个控件是另一个控件“下一个”,就意为着定义了另一个控件的“上一个”是此控件。
  • 所以我们只需要按顺序将三个节点用focus_next连起来就可以了。

运行后,默认还是第一个按钮获得焦点,我们使用Tab键或下方向键进行按钮焦点的正序切换。当达到末尾时,焦点又会重新回到第一个按钮,如此循环。

请添加图片描述
我们使用Shift+Tab键或上方向键,可以进行按钮焦点的逆序切换。也就是向上切换,同样到顶之后,又会回到焦点顺序最末尾的按钮。

限定按钮快捷键为局部快捷键

  • 上面通过shortcut属性设定的快捷键默认是全局的。
  • 但有些情况下可能需要在某个局部GUI获得焦点时,在局部使用这些快捷键,这时可以设定shortcut_context,来限定快捷键为局部快捷键。
    image.png

特殊按钮类型

Button派生的有4个按钮子类型,同时BaseButton直接派生的还有2个按钮子类型。

在这里插入图片描述

CheckButton

CheckButton类似于一个开关。它类似于一个开启了flat的按钮后面跟了一个表示按下或未按下(也可以理解为选中或未选中)的图标。

image.png

自定义CheckButton的图标
  • 主题覆盖部分,图标分组下,可以设定自定义的CheckButton选中和非选中状态下的图标。
  • 比如这里我们使用的对钩图标。image.png
    下面是按钮非选中和选中状态下的效果:
    image.png
CheckButton生成函数
# 生成并返回CheckButton
func check_button(text:String = "CheckButton",icon:Texture2D = null,checked_icon:Texture2D = null,unchecked_icon:Texture2D = null,tool_tip:String = "") -> CheckButton:
	var btn = CheckButton.new()
	btn.text = text
	btn.icon = icon
	if checked_icon:
		btn.set("theme_override_icons/checked",checked_icon)    # 选中图标
	if unchecked_icon:
		btn.set("theme_override_icons/unchecked",unchecked_icon)  # 未选中图标
	btn.tooltip_text = tool_tip
	return btn

使用CheckButton生成函数:

extends HBoxContainer

var icon = preload("res://custom_nodes/icons/dir.png")
const CHECKED = preload("res://custom_nodes/icons/checked.png")
const UNCHECKED = preload("res://custom_nodes/icons/unchecked.png")

func _ready():
	# 通过按钮生成函数创建并添加按钮
	add_child(check_button())
	add_child(check_button("CheckButton",icon))
	add_child(check_button("CheckButton",icon,CHECKED,UNCHECKED))

生成的CheckButton效果:

在这里插入图片描述

MenuButton

MenuButton是一种特殊按钮,点击后会弹出一个下拉菜单。
在这里插入图片描述

通过其items属性,我们可以设定每个菜单项。

image.png

运行后的效果如下:

image.png

也可以为每个菜单项设定图标,以及是否可以勾选并显示复选框或单选框。

image.png

通过添加separator属性为true的菜单项,可以添加菜单项之间的分割线,用于菜单项的人为分组。

image.png

运行后的效果如下:

image.png

MenuButton生成函数
  • MenuButton其实并没有items属性,“检视器”面板中的items是一个属性分组,其编辑列表是Godot的一种特殊设计。
  • 其菜单项部分其实是一个PopupMenu节点,可以使用其get_popup()方法获取
  • PopupMenu节点添加菜单项的方式使用一堆乱七八糟并且不返回菜单项引用的方法
  • 这种特殊化的设计,使得代码生成菜单变得很难受(希望后续版本能改进吧)
# 记录PopupMenu菜单项信息的自定义资源
class PopupMenuItem extends Resource:
	@export var text:String
	@export var icon:Texture2D
	@export var checkable:int
	@export var checked:bool
	@export var disabled:bool
	@export var separator:bool
	

# 生成并返回简单的MenuButton
func menu_button(text:String = "CheckButton",icon:Texture2D = null,items:Array[PopupMenuItem] = []) -> MenuButton:
	var btn = MenuButton.new()
	var menu:PopupMenu = btn.get_popup()
	for item in items:
		# PopupMenu的选项生成具有复杂的代码
		pass 
	return btn
  • 这里我创建了一个名叫PopupMenuItem的自定义资源,用来存储表示单个菜单项所需的数据。
  • menu_button函数尚未完工,后续再改

#==========================================================#
等待继续扩充…

最后修改日期:2024年2月27日00:39:58
#==========================================================#


OptionButton

  • OptionButton也是一个带下拉框的按钮,其样式更像传统的“组合框”(ComboBox)。
    image.png
  • 同样通过其items属性可以添加下拉选项,也可以设定分割线,为选项添加图标等。image.png

LinkButton

  • LinkButton模拟超链接的效果
  • 通过为其uri属性设定有效的网站链接或本地文件系统路径,点击时将通过系统默认浏览器打开网站或者资源管理器打开文件夹用系统默认程序打开本地文件image.png
    下面是默认样式:
    image.png

TextureButton

  • TextureButton是最特殊的一种按钮,它允许你通过设定按钮几种状态下的图片来创建图片按钮。
  • 或者你可以只设定一个normal状态下的图片来定义按钮。image.png
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐