【Godot4.2】按钮控件全解析
Godot虽然有内置文档和在线文档,但是对于特定控件的讲解资料是很少的。因此对于使用者可能会造成一定困扰。笔者(Bilibili@巽星石)一直想实现一个Godot控件的详细教程,为了实现这个目的,我会开始一系列的文章来进行总结,并不断地修改,以便于读者食用。通过主题覆盖部分,图标分组下,可以设定自定义的选中和非选中状态下的图标。比如这里我们使用的对钩图标。
概述
Godot虽然有内置文档和在线文档,但是对于特定控件的讲解资料是很少的。因此对于使用者可能会造成一定困扰。
笔者(Bilibili@巽星石)一直想实现一个Godot控件的详细教程,为了实现这个目的,我会开始一系列的文章来进行总结,并不断地修改,以便于读者食用。
本篇主要介绍按钮类型控件的基础使用和自定义等。
注意:本篇所述内容皆基于Godot4.2.1版本,不同大小版本下可能会有API的差异。
Button
Button
是最基础的按钮控件,也是日常最常用的按钮类型。其继承和派生关系如下图:
在“检视器”面板你可以看到Button
类型的属性:
设定按钮文本
text
属性设定按钮显示的文本,我们可以在“检视器”面板中直接设定,也可以通过代码来设定。
# 改变按钮文本
button.text = "普通按钮"
设定文本的对齐方式
通过alignment
属性可以设定按钮文本在按钮矩形区域中的对齐方式。可以直接在“检视器”面板中选择,三个选项分别对应左对齐、居中和右对齐。默认文本对齐方式为居中。
或者也可以用代码的形式进行设定:
button.alignment = HORIZONTAL_ALIGNMENT_LEFT # 左对齐
button.alignment = HORIZONTAL_ALIGNMENT_CENTER # 居中对齐(默认)
button.alignment = HORIZONTAL_ALIGNMENT_RIGHT # 右对齐
分别对应效果如下:
设定文本超出按钮矩形区域的行为
因为text
属性没有限定文本的长度,所以一定会出现文本超出按钮长度的情况。
- 默认,按钮不会对文本进行裁切,而是根据设定的文本进行宽度自适应。
text_overrun_behavior
和clip_text
,都用于设定文本超出按钮矩形区域后是否裁切文本。- 使用中文文本时,
text_overrun_behavior
除了默认的“Trim Nothing”选项不施行裁剪外,其他几个选项似乎没有多大的区别,因此你可以忽略这个属性直接启用clip_text
。效果是类似的。
下图为设定text_overrun_behavior
属性:
下图为设定clip_text
属性:
或者也可以使用代码形式:
button.clip_text = true # 启用文本裁切
下面分别是未进行文本裁剪和进行裁剪的效果:
为按钮设定图标
- 通过
icon
属性我们可以为按钮设定图标。 - 默认图标在左,文本在右。
- 所需图标资源可以在iconfont-阿里巴巴矢量图标库中获得。
设定图标与按钮的不同位置组合
- 通过
icon_alignment
和vertical_icon_alignment
两个属性,结合设定文本对齐的alignment
属性,可以组合出多种图标和按钮的位置关系。
以下是常见的几种文本和图标位置关系:
按钮样式 | 属性设置 | 说明 |
---|---|---|
默认,图标在左,文本在右。 | ||
图标在上,文本在下。 | ||
文本在右,图标在左。 | ||
文本在上,图标在下。 |
创建按钮生成函数
-
在创建复杂的自定义节点或编辑器插件时,可能需要动态生成按钮,这时使用重复性的代码,就不如直接编写函数或函数库。
-
上面的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
设定图标是否跟随按钮的大小变化进行动态缩放。- 但在一般情况下这会使得小图标放大变得不清晰(低分辨率)。可以使用更高分辨率的图标来解决放大后不清晰的问题。
图标跟随按钮放大的效果:
BaseButton
Button
继承自按钮基类BaseButton
,它提供了所有按钮类型共用的一些基础属性、方法和信号。
以下是BaseButton
类型的属性:
禁用按钮
disabled
属性规定了按钮是否处于禁用状态,禁用状态下按钮及其文本变为灰色,并且无法响应鼠标点击。- 这里以普通按钮
Button
举例,但是其他按钮也可以被禁用。
切换按下与弹起状态
toggle_mode
决定按钮是否在按下与**未按下(弹起)**的状态间进行切换,默认值为false
。- 通常默认(
toggle_mode == false
)情况下,按钮会跟随鼠标按键的按下与松开,经历一个短暂的“按下”-“持续”-“弹起”的过程,而不会一直处于按下状态。 toggle_mode
则改变了这一行为,按下的按钮将不会随鼠标按键的松开而回弹,而是一直保持按下的状态,直到鼠标再次点击并松开后才会回弹,类似于一种开关的状态切换。button_pressed
则设定或判断按钮是否被按下:toggle_mode == true
状态下,button_pressed
在true
和false
之间来回切换。toggle_mode == false
状态下,鼠标按键按下时button_pressed
为true
,鼠标按键松开时则为false
。
button.button_pressed = true # 设定按钮的按下(或选中)状态
print(button.button_pressed) # 返回按钮的按下(或选中)状态
按钮的信号处理
以下是按钮类型拥有的4个信号:
button_down
:按钮按下后触发button_up
:按钮松开时触发pressed
:可以看做是button_down
+button_up
,但是其发出的时机取决于action_mode
属性的设置。- ★如果
action_mode
为ACTION_MODE_BUTTON_PRESS
(默认值),则pressed
信号在button_down
也就是按钮按下时发出,否则在button_up
也就是按钮松开时发出。
- ★如果
toggled
:如果toggle_mode == true
,则button_down
、button_up
以及pressed
信号使用的意义就变得有限了,转而可以使用toggled
信号来处理按钮开关状态的切换。其参数toggled_on
可以看为是否button_pressed
。
设定按钮可以响应的鼠标按键
button_mask
设定按钮可以响应的鼠标按键,可以选择鼠标左键、中键、右键三者中的其中一个或任意两个或三个全部。默认情况下按钮只接受左键点击。也就是鼠标中键和右键无法触发button_down
、button_up
以及pressed
信号。
因为这是一个多选项,所以你可以选择至少一个或两个或全部三个按键。用代码进行设定时,可以使用|
符号分割多个选项。
# 设定按钮能同时响应鼠标左键和右键的点击
button.button_mask = MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_RIGHT
鼠标移入、移出的细节
- 默认状态下,鼠标在移入按钮矩形范围后使其外观呈现
hover
状态,而鼠标移出后使其恢复normal
状态。 - 如果按钮被按下:
- 如果随即鼠标按键松开(也就是最常见的鼠标单击情况),则按钮呈现一过性的
pressed
状态后恢复normal
状态,并因为获得焦点而呈现focused
状态。 - 如果鼠标按键持续按下,未松开,如果鼠标在按钮矩形范围内,则按钮呈现持续的
pressed
+focused
状态,此时鼠标移出按钮矩形范围,则按钮恢复normal
状态,呈现normal
+focused
状态。
- 如果随即鼠标按键松开(也就是最常见的鼠标单击情况),则按钮呈现一过性的
- 而如果设定
keep_pressed_outside
为true
,则按钮被按下后不松开,鼠标移出按钮区域外时仍然保持按钮被按下的状态,直到鼠标按键松开时才恢复到松开状态。但是由于按钮的pressed
信号默认是在button_down
时就触发,与button_up
没有关系,所以并不影响按钮pressed
信号的发出。
创建按钮组
-
通过为多个按钮的
button_group
属性创建同一个ButtonGroup
资源,可以将多个按钮联系在一起,形成一个按钮组。 -
按钮组的特性是:同一时间只有一个按钮能被按下。
-
创建按钮组的前提:按钮组中的所有按钮都启用了
toggle_mode
。
创建按钮组的方式
我们可以用以下3种方式手动创建按钮组:
- 方法一:创建一个
Button
,为其button_group
属性新建一个ButtonGroup
资源,然后Ctrl+D
复制出多Button
控件,这样新复制出的按钮会和第一个按钮共用相同的ButtonGroup
资源,从而实现按钮组的创建。
- 方法二:先创建多个
Button
,然后在“”场景“”面板的节点树中按Shift
或Ctrl
键选择要成组的所有按钮,在检视器面板统一为其button_group
属性新建一个ButtonGroup
资源(Godot支持这种操作)。
- 方法三:创建多个
Button
,选中其中一个,为其button_group
属性新建一个ButtonGroup
资源,然后右键“复制值”(或者Ctrl+C
),然后转到其他要成组的Button
节点的button_group
属性处,右键“粘贴值”(或Ctrl+V
)。
运行后就会发现三个按钮只有一个按钮能被按下,类似于在多个可选项中进行单选。
按钮组通常可以用于游戏设定,以及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_mode
和button_group
设定的多个按钮实例。 - 再通过遍历添加到
HboxContainer
容器进行横向排列。
实际运行效果与手动创建无异。
上面只是创建了Button
类型的按钮组生成函数,你也可以继续扩展创建其他按钮类型的按钮组生成函数。或者写一个支持所有按钮类型的按钮组生成函数。
为按钮设定快捷键
- 通过为按钮的
shortcut
属性创建Shortcut
资源可以为按钮设定快捷键。 - 甚至你可以为同一个按钮设置多个快捷键,或者为不同的输入设备(如键盘或游戏手柄)设定此按钮的快捷键。
- 在
Shortcut
资源的events
属性部分,我们可以设定一个或多个InputEvent
。- 电脑键盘快捷键是
InputEventKey
,Godot4.2也贴心的为我们提供了按键监听输入,我们只需要点击“配置”按钮,在打开的类似于“输入映射”配置的窗口中按下键盘快捷键就可以自动录入。
关闭后,会自动帮我们填写到其属性中。
- 电脑键盘快捷键是
- 我们也可以将手柄按键设为该按钮的快捷键,只需要添加
InputEventJoypadButton
类型的资源就可以。
同样我们在连接了手柄设备的状态下,可以直接使用按键监听输入的形式快速录入手柄按键信息。
按钮快捷键的细节设定
shortcut_feedback
属性值决定在按快捷键时,按钮是否显示被按下的时的高亮,默认为true
。- 如果是
false
,而且toggle_mode=false
时,则按快捷键时,按钮本身不会有任何视觉反馈。 shortcut_in_tooltip
决定是否在鼠标提示文本中添加快捷键信息,默认为true
。将鼠标移动到按钮上可以看到用括号括起来的快捷键信息。
我们为按钮添加一段鼠标提示文本。
再次运行可以看到带有鼠标提示文本和快捷键提示的效果。
Control
因此按钮本身也是控件,所以Control
类型自然在其继承链上,这意为着按钮可以使用继承自Control
类型的属性、方法和信号。
自定义按钮不同状态下的样式
在上面我们已经提到过按钮与鼠标交互时的几种状态切换,也就是下表列出的5种。
状态 | 说明 |
---|---|
normal | 默认(鼠标未移入) |
hover | 鼠标移入 |
pressed | 按下 |
disabled | 禁用 |
focus | 获得焦点 |
相应的我们可以在主题覆盖部分,设定按钮在五种状态下的“样式盒”(边框、背景、内外边距、圆角等)、文本以及图标的颜色。
自定义按钮字体和字号
同样是在样式覆盖部分,可以自定义按钮的字体和字号。
为按钮文本添加描边
- 在样式覆盖的常量部分,可以通过
outline_size
设定按钮文本的描边宽度。 - 在样式覆盖的颜色部分,通过
font_outline_color
可以设定描边的颜色。
通过两个属性的结合,可以实现按钮文本的描边效果。
限制按钮图标的最大宽度
在样式覆盖的常量部分,可以通过icon_max_width
设定按钮图标的最大宽度。
设定按钮的焦点顺序和邻居
- 图形用户界面的控件之间存在一种焦点的东西,在同一个窗口中,通常同一时间只能有一个控件获得焦点。
- 而且可以人为的设定同一窗口或局部界面中控件的焦点顺序,以及控件的毗邻关系。
- 通过键盘的
Tab
/Shift+Tab
键可以快速向前或向后切换焦点到不同的控件。 - 用键盘或手柄的方向键或上下左右键可以切换到指定的毗邻控件。
在Godot中我们同样可以为我们的控件设定焦点顺序和上下左右的毗邻控件:
- 通过
focus_neighbor_left
、focus_neighbor_right
、focus_neighbor_top
、focus_neighbor_bottom
可以设定按钮的左右上下“”邻居“”节点。
可以手动通过检视器面板进行设定。
也可以通过代码设定:
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_next
或focus_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
,来限定快捷键为局部快捷键。
特殊按钮类型
Button
派生的有4个按钮子类型,同时BaseButton
直接派生的还有2个按钮子类型。
CheckButton
CheckButton
类似于一个开关。它类似于一个开启了flat
的按钮后面跟了一个表示按下或未按下(也可以理解为选中或未选中)的图标。
自定义CheckButton的图标
- 在主题覆盖部分,图标分组下,可以设定自定义的
CheckButton
选中和非选中状态下的图标。 - 比如这里我们使用的对钩图标。
下面是按钮非选中和选中状态下的效果:
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
属性,我们可以设定每个菜单项。
运行后的效果如下:
也可以为每个菜单项设定图标,以及是否可以勾选并显示复选框或单选框。
通过添加separator
属性为true
的菜单项,可以添加菜单项之间的分割线,用于菜单项的人为分组。
运行后的效果如下:
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
)。
- 同样通过其
items
属性可以添加下拉选项,也可以设定分割线,为选项添加图标等。
LinkButton
LinkButton
模拟超链接的效果- 通过为其
uri
属性设定有效的网站链接或本地文件系统路径,点击时将通过系统默认浏览器打开网站或者资源管理器打开文件夹或用系统默认程序打开本地文件。
下面是默认样式:
TextureButton
TextureButton
是最特殊的一种按钮,它允许你通过设定按钮几种状态下的图片来创建图片按钮。- 或者你可以只设定一个
normal
状态下的图片来定义按钮。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)