变量必须以字母或下划线开头,可以由字母、下划线、数字构成变量名:
var a = 1
var b = "Hello"
# 不同类型的变量可以相互赋值
=a
b
# 指定变量类型,基本变量类型有int、float、String、bool,指定后相互赋值前需先转换
var a :int = 1
var b :float = 2.6
var c :String = "Hello"
var d :bool = true
不同类型变量的转换
# float转为int会取整数部分
=b
aprint(a) #输出2
# int或float转为String
=String.num_int64(a) #1
c=String.num(b) #2.6
c=c+c #2.62.6
c
# bool不能转为String,非0数字为true,0为false
=a #true
d=!a #取反,false
d=0 #false d
数组
# 数组
var a :Array = [1, 2, 1.2, "string", true]
# 指定数组期待的元素类型
var b :Array[int] = [2, 3, 4, 5, 4]
# 数组下标从0开始
print(b[1]) #3
# 增删数组的元素
append(6) #在最后添加6,变为[2, 3, 4, 5, 4, 6]
b.erase(4) #删除数组中出现的第一个的4,变为[2, 3, 5, 4, 6]
b.
print(b) #打印数组
print(b.size()) #打印数组的元素个数,输出5
# 数组是引用类型
var c :Array[int] = [2, 3, 4, 5, 6]
var d = c
append(4)
d.print(c) #[2, 3, 4, 5, 6, 4]
print(d) #[2, 3, 4, 5, 6, 4]
字典
var dict = {"a": 0, "b": 1, "c": 2}
函数块通过Tab
缩进识别,有Tab
的是函数的作用域,无Tab
的是当前节点公用的作用域;各个函数都有一个独立的作用域,在一个函数内部定义的变量无法在另一个函数中直接访问变量名。函数运行结束后基本变量会自动销毁,引用类型的变量在被外部引用时函数运行结束后不会自动销毁。
空函数体要加pass
,注意pass
要缩进。非空的函数体也可以添加pass
,只是没有任何作用。
# _enter_tree 是内置虚函数
func _enter_tree():
var d = add(2,3)
print(d) #5
var e = add(1)
print(e) #11
var f = add2(3.5, 4)
print(f) #7
# 指定参数类型,默认参数
func add(a:int, b:int=10):
var c=a+b
return
# 让返回值转为int
func add2(a,b)->int:
var c=a+b
return
func test()
pass
内置虚函数
内置虚函数是指没有实际处理流程的函数,节点内的虚函数会在特定的条件下自动被触发,类似安卓的生命周期函数。
常用的虚函数有:
_init()
:节点被创建时触发;_ready()
:节点的全部子节点加入场景树后触发;_process(delta)
:画面刷新时触发,即每逻辑帧触发一次;_physic_process(delta)
:物理引擎刷新时触发;if
每个if
、elif
、else
都有独立的作用域,在其作用域内定义的变量外部的作用域无法访问。while
和for
同理。
比较运算符:>
、>=
、<
、<=
、==
、!=
var a = 1
if (a!=0):
print("Hello")
var b = 2
if b>3:
print("b>3")
elif b>2:
print("b>2")
elif b>1:
print("b>1") #输出
else:
print("b<=1")
对于浮点数要注意:
var a = 0.1
var b = 0.2
if (a+b==0.3):
print("Hello") #不会输出
# 要改为
if (is_equal_approx(a+b, 0.3)):
print("Hello") #会输出
字符串虽然不能转为bool,但是if
可以写成如下形式:
var a:String = "test"
if a:
print("Hello") #会输出
var b:String = ""
if b:
print("Hello") #空字符串代表false,所以这里不会输出
var c:String = "123"
if c=="123":
print("Hello") #会输出
while
var a = 0
while a<10:
+=1
aprint(a)
if a == 5:
break
for
# 遍历数组
var a :Array[int] = [2, 3, 4, 5]
for i in a:
print(i)
# 遍历字典
var dict = {"a": 0, "b": 1, "c": 2}
for key in dict:
print(dict[key])
# 遍历字符串
for char in "Hello":
print(char)
单例是一个可以在任意脚本中对其进行访问的对象,Godot内置了很多单例,主要成员是各类Server;我们也可以自定义单例,但自定义单例必须是节点类型的对象,是开发者自定义的全局对象。每个单例都是独一无二的对象。
内置单例
比如Input
单例,他可以对玩家的按键进行反馈。游戏中的按键通过如下方法设置:项目->项目设置->输入映射->添加新动作(输入动作名字)->右侧”+“号->设置对应键位。然后就可以在代码中通过Input单例来获取:
func _process(delta: float) -> void:
# 比如设置了一个名为"left"的动作,通过Input.get_action_strength可以获取按下对应键的力度,是0-1的值,若是键盘按键则只会是0或者1
if Input.get_action_strength("left"):
self.position.x = self.position.x - 1 #其中self可以省略
再比如ProjectSettings
单例,可以使用代码在这个单例中设置项目设置的信息,并保存为project.godot
或override.cfg
。
自定义单例
先新建脚本,然后:项目->项目设置->全局(或者叫Autoload)->路径(选择刚刚创建的脚本)->修改节点名称->添加。然后就可以在刚刚创建的脚本中定义函数,并在任意脚本中通过节点名称.函数名
调用对应函数。
节点(Node)是Godot中最基本最常用的开发组件。
Node的获取:
# 比如有一个声音节点名字为audio(双击左侧节点栏中对应的节点可以改名)
var soud = get_node("audio")
play()
soud.
# get_node()也可以简写为 $节点名称 ,比如这里可以简写为
$soud.play()
# 获取子节点,比如第2个子节点
get_child(1)
# 获取所有直接子节点
get_children()
# 获取多级子节点(类似目录的形式)
get_node("node1/node2")
# 获取父节点
get_parent()
get_node("..")
补充:为节点添加导出属性
# 添加导出属性,在右侧检查器中可以快速调整该属性
var x = 5 @export
场景
Godot是以场景->节点管理内容的,每个场景(比如人物场景、地图场景等)下有多个子节点、及子节点的子节点等,要运行脚本,就要为节点附加脚本。场景文件是一组节点的集合,是节点加载和存储的基本单位。Godot通过Server在节点与渲染引擎之间沟通。
一般来说只会在场景的根节点附加脚本,避免脚本管理混乱。
加载场景并添加到场景树的代码如下:
func _ready():
var scene_resource = load("res://sprite.tscn") #加载场景资源
var root_node = scene_resource.instantiate() #生成节点集合
self.add_child(root_node)
场景实例化的过程:①资源文件->②经过load()
转换为资源对象->③调用instantiate()
实例化->④生成各个节点并调用_init()
,此时脚本中定义的函数外部变量也首次出现->⑤节点建立起位置和父子关系->⑥经过add_child
将节点集合加入场景树⑦从根节点自上而下开始执行_enter_tree()
->⑧自下而上开始执行_ready()
->⑨节点开始受场景树管控.
也就是说,在执行_init()
时,节点间的层级关系还没构建,此时若在_init()
中使用get_child()
等方法是无法获取到其他节点的。
在函数外部使用 @onready
来修饰变量,可将变量的赋值拖延到
_ready()
执行的时刻。
var a = get_child(0)
var b = get_child(0)
@onready
func _init():
print("_init-start")
print(get_child(0)) #<Object#null>
print(a) #<Object#null>
print(b) #<null>
print("_init-end")
func _ready():
print("_ready-start")
print(get_child(0)) #可以获取到对象
print(a) #<Object#null>
print(b) #可以获取到对象
print("_ready-end")
节点的owner属性:owner
是一个节点类型的变量,用来表示某个节点。在一个场景文件实例化所产生的节点集合中所有节点的owner
属性都指向这次实例化生成的根节点。
使用代码生成场景并设置owner:
extends Node
func _ready():
var root_node = Node.new()
var child_node1 = Node.new()
= "aaa"
root_node.name = "bbb"
child_node1.name add_child(child_node1)
root_node.# owner指向根节点
= root_node
child_node1.owner
# 使用场景资源对象的相关函数保存场景
var scene_pack :PackedScene = PackedScene.new()
# 打包场景时只打包场景的根节点
pack(root_node)
scene_pack.# 使用ResourceSaver单例相关函数导出到项目文件
save(scene_pack, "res://aaa.tscn") ResourceSaver.
场景树(SceneTree)是游戏的管理者,它负责Godot的内置服务器与节点的沟通。内置服务是Godot的各种模块,包括计时系统、物理模拟系统、图像绘制系统等。
场景树继承自主循环(MainLoop)类,场景树是在主循环的基础上,对节点管理进行了扩写。虽然存在主循环类,但游戏运行时只存在场景树对象。
程序启动后,程序会创建一个主循环对象(一般是场景树),它包含了初始化、空闲帧同步回调、物理帧同步回调等方法。这个类中不包含节点相关的具体操作。
物理处理(Physic Process):Godot游戏程序中会内置一个物理服务器,用于处理游戏世界内的各种物理运算,在每一次物理运算前,这个服务器都会给予主循环一次参与计算的机会,这就是主循环中的物理处理。默认情况下,物理服务器一秒内会进行60次运算,因此主循环中的物理处理也会每秒进行60次处理。
空闲处理(Idle Process):在Godot游戏程序相对”空闲”,即某些内置服务器运行结束的时候,执行此处理。引擎会尽可能快的利用空闲时间来绘制新的游戏图像。
相关函数:
_physic_process(delta)
,其中delta
表示距上次物理帧调用的时间;_process(delta)
,其中delta
表示距上次空闲帧调用的时间;call(函数名)
,立即调用函数;call_deffer(函数名)
,到下个空闲帧调用函数;一般修改界面的代码都要在空闲帧中更新,比如更改焦点、删除场景树下的节点等;涉及到删除对象的代码,都必须要在空闲帧中执行。
场景树的功能
使用场景树可以命令同组节点调用函数或修改属性:
# 分组的操作
self.add_to_group("group1")
self.remove_from_group("group1")
self.is_in_group("group1")
# 获取场景树
get_tree()
# 获取组内的所有节点
var nodes :Array[Node] = get_tree().get_nodes_in_group("group1")
# 命令同一组内节点调用函数
get_tree().call_group("group1", "test_func") #假设分组group1中的节点都有test_func函数
游戏暂停,当场景树进入暂停后,节点会根据自己的暂停模式来调节自己的状态,并且paused
及process_mode
满足条件时不再调用_process()
、_physic_process
、_input()
等虚函数:
# 场景树暂停
get_tree().paused = true
# 设置当前节点的暂停模式
self.process_mode = Node.PROCESS_MODE_PAUSABLE
使用get_node
获取节点后,如果要调用的节点函数不存在,程序会崩溃;而使用信号调用函数,如果双方对象或目标方法不存在,程序只会警告,而不会崩溃。
信号是指将一个对象的”信号”与另一个对象的”函数”绑定,信号发射后程序会进行函数的调用。这样可以保证每个场景都能单独运行,方便单独测试。信号有内置信号与自定义信号,一个节点所有的信号可以在右侧节点处查看,右键选择信号名可以选择链接并选取要绑定的函数,也可以通过代码去绑定。
自定义信号(信号的作用域只在对象内部,其他对象无法访问):
# 定义一个函数
func test_function(p):
print(p)
# 定义一个信号
signal test_signal
func _ready() -> void:
# 进行绑定,将test_signal信号绑定到当前对象的test_function函数中
self.connect("test_signal", Callable(self, "test_function"))
# 发射信号
emit_signal("test_signal", "Hello world")
await
可以使某个函数暂停运行,直至接收到了来自某个对象的信号:
func _ready() -> void:
# 暂停2秒
var time = get_tree().create_timer(2)
await time.timeout
我们称拥有自己属性、函数与信号的数据单位为对象,比如节点,它有内置属性、内置函数,也可以通过脚本的形式添加属性或函数。我们可以称具有相同、函数与信号的对象们为一个类。
继承分为:
Object
是所有对象的基类;extends 类名或路径
。类名使用class_name
或class
关键字指定;Object中常用的函数:
# 初始化的虚函数,当一个对象生成时,此函数会被自动调用
func _init() -> void:
pass
# 从程序中删除此对象,但不能直接使用
# self.free()
# 应该改为在空闲帧中删除
self.call_deferred("free") # call或call_deferred都是Object的函数
# 效果和如下相同,但该函数不是Object的函数
self.queue_free()
# 接收通知虚函数,比如节点即将被销毁前会调用,此时 what=1
func _notification(what: int) -> void:
if what == 1:
print("节点即将被销毁")
# 为对象设置脚本,大部分对象都可以设置脚本
var scr = load("res://script.gd")
$node.set_script(scr)
$node.test() #设置好后就可以调用脚本内的函数了
继承
定义一个类:
class_name A
extends Object
func test():
pass
另一个类继承:
# extends "res://A.gd"
# 或者
extends A
func test():
pass
func _ready() -> void:
# 调用自己的函数
test()
# 调用父类的函数
test() super.
创建对应的实例:
var a :A = A.new()
# 继承自Object的类的实例就像一个游戏物体,不会自动删除释放,需要手动释放内存
free() a.
内部类
我们通过class_name
定义的是全局类,全局类在Godot整个项目内都可以通过代码实例化。
我们可以通过class
关键字来定义一个内部类,内部类只有当前脚本才可以实例化。内部类默认继承自Object,也可以通过extends
关键字指定要继承自哪个类。内部类的作用域和脚本不同,是两个独立的作用域,因此内部类无法访问脚本中的全局变量。
静态变量和静态方法
静态变量是在程序运行期间,保持其存在和值的变量,静态变量用static
关键字修饰:
static var a :int = 0
类似的,有静态方法,静态方法只能访问静态变量。
我们可以直接通过类名访问静态变量和静态方法:
func _ready() -> void:
print(AAA.name)
test_static()
AAA.
class AAA:
extends RefCounted
static var name :String = "name"
static func test_static():
print("test")
一般来说,继承自Object
的类要手动删除,否则会一直留在内存里。
RefCounted
继承自Object
,但它内置了一个独特的引用计数器,当引用计数器归0时,此对象会自动被程序删除。它的优点在于不包含任何内置属性,仅有四个内置函数,也不必像节点那样要频繁地受到场景树的控制参与服务器的计算,相比节点更加轻量,并且还能进行内存的自动管理。
RefCounted
对象的生成可以通过 类名.new()
或
load("路径").new()
或 preload("路径").new()
.
例如,定义一个类继承自RefCounted
:
class_name person
extends RefCounted
var name:String = ""
var age:int = 0
# 通过 _init 的参数,指定 new 时要传入的参数
func _init(p_name) -> void:
= p_name
name print(name + "被初始化")
func one_year_past():
self.age += 1
func _notification(what: int) -> void:
if what == 1 :
print(name + String.num_int64(age) + "被销毁")
创建对应的实例:
var person1
func _ready() -> void:
= person.new("张三")
person1 # 或者
var person2 = load("res://person.gd").new("李四")
one_year_past() person2.
输出结果如下,可以看到,张三并没有被销毁:
张三被初始化
李四被初始化
李四1被销毁
常用的RefCounted子类:
Resource
将资源文件转为Resource有几种方法:
方法一:
1、将资源文件拖入到项目中;
2、编辑器对资源文件进行导入,生成特殊文件及其资源文件对应的import文件;
3、游戏启动后借由import文件加载特殊文件生成资源对象;
方法二:
使用 load("路径")
或 preload("路径")
.
通过这种方法加载的资源,如果加载的文件已经被转化为资源,且此资源引用计数器不为0,则再次加载该文件不会产生新的资源对象,而是返回一个原先资源对象的引用,此时任意一处对该资源的修改,都将影响所有使用此资源的对象。
load
和preload
的区别:load
的参数可以是字符串变量,而preload
则无法使用变量。脚本文件转换为脚本资源时,转换程序会自动翻找文件中是否出现了preload
函数,若出现,则在脚本资源转换的同时进行preload
资源的加载,这时变量还没有出现,因此只能以文本字符串的形式来告知preload
加载的内容。
方法三:
通过 类名.new()
创建,通过这种方法加载的资源不会产生相同的引用。某些文件也可以通过内置资源的代码进行加载。
extends Sprite2D
func _ready():
var img = Image.new()
load("res://icon.svg")
img.var tex = ImageTexture.create_from_image(img)
self.texture = tex
常用节点有:
Viewport
和Window
:视窗,由Godot生成的根节点;一般用于输入事件的处理;CanvasItem
:CanvasItem
节点继承自Node
,可以用这个节点进行2D图画绘制,比如Modulate
和Self Modulate
属性可以改变颜色,Z Index
和Y Sort Enable
可以调节渲染顺序。它有Node2D
和Control
两个子类,Node2D
用于制作2D游戏对象,可以实现调整位置、旋转、缩放、倾斜等属性;Control
用于制作游戏界面;CanvasLayer
:图层节点,用于创建不同的图层;对于CanvasItem
类节点,上层的对象在绘制时会覆盖下层的对象(这时无法通过Z index
修改不同图层的层级关系),并且可以阻断传往下层的事件输入(如鼠标点击等)(对于Control
类节点,可以通过mouse
filter属性进行修改阻断与否);对于Node2D
类节点,不同图层的物理对象无法进行物理交互;对于Camera2D
节点,Camera2D
节点仅能影响到自己所在图层上的显示区域,无法影响其他图层;Sprite2D
:图片节点;AnimatedSprite2D
:通过切换图像、显示不同图片来显示动画的节点;可以通过右侧检查器中新建Sprite Frame
导入图片并按规律裁剪,设置动画名称,然后通过play(String)
和stop()
来控制动画的播放和暂停;AnimationPlayer
:动画播放节点;为其添加图片子节点,通过设置在不同时间不同的属性,可以制作简单的线性动画;它还可以实现更复杂的功能,比如在时间轴上设置属性的改变过程,或在某个时间点设置函数的调用;AudioStreamPlayer
:声音节点;PhysicsBody2D
和Area2D
:图片节点可以提供视觉上的游戏对象,却无法在游戏中产生”触觉”;想要让游戏中出现可以感知的,且占据一定空间体积的物理对象,就要借助Body类和Area类节点;RayCast2D
和ShapeCast2D
:射线投射检测和形状投射检测,可以检测获取2D世界内的Body节点和Area节点。这两个节点都拥有一个靶坐标,RayCast2D
会从节点坐标出发,创建一条到靶坐标的射线,检测这条射线上距离节点坐标最近的Body和Area;ShapeCast2D
则会向靶坐标处创建若干连续的Shape以检测距离节点坐标最近的Body和Area;它们都是通过get_collider()
函数来获取碰撞目标的;此外,它们还可以分别通过force_raycast_update()
和force_shapecast_update()
来使此节点立即重新进行一次物理检测,以避免物理计算有延迟导致的实际画面与物理计算不同步;TileMapLayer
:基于2D贴图块的节点,多用于制作游戏的地图;使用内置资源对象TileSet
可以指定图块列表,但只包含一个图块层,想要多层的地图可以使用多个TileMapLayer
实现;
TileMapLayer
节点->右侧检查器新建TileSet
->下方选择TileSet
->新建图集并导入图片->设置分割的图块大小等属性->下方选择TileMap
->选中要绘制的图块->选择画笔工具并在视图界面绘制;TileSet
部分的Physics
Layers选项来添加物理对象,然后打开下方TileSet
->使用选择工具->选择图块->点击出现的Physics选项并展开->然后就可以像CollisionPolygon2D
那样为图块设置物理形状;Terrain Sets
添加地形元素,并选择Terrains
添加地形->下方选择TileSet
->使用绘制工具->绘制属性选择地形->Terrain Sets
选择刚刚创建的地形->然后绘制,给比如草地相关的图块边缘添加掩码->然后选择下方TileMap
->选择地形,并使用直线或矩形工具绘制;Timer
:倒数计时器节点,一般用作技能cd、关卡通关计时等;如果只是想创建一次性的倒数计时器,不想实例化节点,可以使用场景树的函数get_tree().create_timer(时间)
;Viewport
节点是游戏运行时出现的第一个节点,当场景树第一次加载主场景时,游戏程序会先创建一个viewport
节点并将此节点添加到场景树下,然后再将主场景的根节点作为viewport
的子节点添加过去。也就是说,场景树下的节点都是第一个viewport
节点的子节点。(在新版本已经改为Window
节点作为根节点,Window
是Viewport
的子节点,所以基本用法是一样的)
Viewport
节点可以在屏幕中创建一个不同的窗口或在另一个窗口中创建子窗口。Viewport
节点的Camera2D/Camera(2D摄像机/3D摄像机)子节点,可以调整游戏的显示区域,也会改变游戏世界中监听游戏音效的位置坐标。常见应用有:双人游戏的分屏效果(将两个subviewport
的world_2d
属性设置为同一个,再通过设置不同的Camera2D
节点来显示不同的位置)、获取屏幕画面截图、获取鼠标位置、输入事件的处理等。
输入事件的处理(最常用功能)
_input
和_unhandled_input
都是输入事件处理函数,一个输入时间被创建时,会在节点中进行传播,此时节点中的_input
函数将根据传播的顺序依次被调用。如果在_input
传播中,输入时间未被处理掉,则在节点中再进行一次此事件的传播,此时_unhandled_input
自动被调用。
比如,判断鼠标左键按下:
func _input(event: InputEvent) -> void:
if event is InputEventMouseButton :
if event.button_index == MOUSE_BUTTON_LEFT :
if event.pressed == true :
print("鼠标左键1")
# 这里要启用SubViewportContainer的Stretch属性,才能使它的SubViewport子节点占满,然后下面的代码才能生效
$SubViewportContainer/SubViewport.set_input_as_handled()
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton :
if event.button_index == MOUSE_BUTTON_LEFT :
if event.pressed == true :
print("鼠标左键2")
比如,创建鼠标点击事件:
func _ready() -> void:
var event = InputEventMouseButton.new()
= MOUSE_BUTTON_LEFT
event.button_index = true
event.pressed $SubViewportContainer/SubViewport.push_input(event)
PhysicsBody2D
和Area2D
:用于制作在游戏中可以被感知的,且占据一定空间体积的物理对象。Body类节点主要用于制作游戏中的物理对象,而Area节点主要用于检测感知这些物理对象、施加额外的物理属性、修改区域内的音轨情况等。它们两者均继承自CollisionObject2D
,CollisionObject2D
节点是2D世界中物理对象的基类,他可以容纳任意数量的2D碰撞形状,这些2D形状用于定义Body代表的实体形状和Area节点所规定的范围。
常用的PhysicsBody2D
类:
StaticBody2D
:用于制作相对静态的物理对象,比如静止的墙壁或移动方式较为固定的平台等;RigidBody2D
:用于制作拥有质量、重力缩放、受力情况等多种物理属性,可以用于制作不受代码控制而能够自由移动的物理对象;CharacterBody2D
:内置了许多与移动相关或位置相关的函数,拥有位置用于制作受代码控制的物理对象;CollisionObject2D
的Layer
属性代表节点在第几层,而Mask
属性表示当前节点可以和第几层的节点发生碰撞。节点可以同时在不同的层,也可以同时和不同层的节点发生碰撞。Layer
和Mask
的id都是2的指数。
要使Body类和Area类节点可以发生碰撞,需添加子节点比如CollisionShape2D
或CollisionPolygon2D
.
碰撞检测
碰撞检测代码如下(要被检测碰撞的物体可以通过self.add_to_group("group_collision")
来设置对应分组,分组名是自定义的),主要是通过get_overlapping_areas()
和get_overlapping_bodies()
函数进行检测:
# 检测碰撞的代码要放在物理处理的虚函数中
func _physics_process(delta: float) -> void:
for i in get_overlapping_areas():
if i.is_in_group("group_collision"):
= i.position.x + 1 i.position.x
对应的节点结构如下:
|--Area2D
| |--Sprite2D (用于显示图片)
| |--CollisionShape2D (用于设置碰撞区域,一般是把图片的范围包裹住)
Control
是所有界面类节点的父节点。Control
类节点提供了输入处理(_gui_input
)、拖拽、鼠标移动、语言翻译转换、快捷切换界面节点、主题(Theme)、界面提示文本等相关的属性与虚函数。
# _gui_input函数的事件与Control类节点的范围绑定,只有当比如鼠标移动到Control类节点范围内才会触发_gui_input函数
# 此外,_gui_input函数还与Control类节点的可见性、层次关系(渲染顺序)等绑定
# 可在右侧检查器中找到Control的Mouse选项,把Filter设置为Stop,这样上层的Control类节点就会截断事件,下层重叠的Control类节点就不会触发_gui_input函数
func _gui_input(event: InputEvent) -> void:
print(event)
常用的Control类节点:
Button
:按钮;通过右侧检查器绑定信号对应的函数来处理点击事件;TextureButton
:带图片的按钮;通过设置texture_click_mask
属性,可以自定义可点击的区域;Label
:用于显示文字;TextureRect
:用于显示图片,类似Node2D下的Sprite2D节点,不过它是Control的子节点;比如想让图片占满父节点的大小,可以将Layout
Mode改为Anchors,然后将Anchors Preset改为Custom,将出现的Anchors
Points选项的Left、Top改为0,Right、Bottom改为1,其实这里的左、上、右、下指的是当前节点的锚点相对于父节点大小的百分比;TextureProgressBar
:带图片的进度条;也可以用于制作血条等;VideoStreamPlayer
:用于播放视频;LineEdit
:单行文本框;TextEdit
:多行文本框;SpinBox
:用于输入数值的单行文本框;ColorRect
:可以设置单色背景的矩形;Container
:当新的Control
类节点加入成为Container
节点的子节点时,Container
类节点会发出pre_sort_children
信号,然后Container
节点会根据代码设置(遍历所有子节点并通过fit_child_in_rect
函数规定各子节点的大小、位置)的布局方式,排布子节点;常用的Container
类节点有:HBoxContainer
、VBoxContainer
、GridContainer
、ScrollContainer
等;