
新手引导--- 2 使用技巧
本篇文章将介绍godot编辑器中一些基础的gdscript使用技巧。
不过说一千道一万,只有实践出真章,在了解其中原理之后多加练习,自然熟能生巧。
开始吧
godot编辑器的内置节点基于Node
类,但godot有很多不属于Node
子类的其他类。言归正传,使用gdscript在godot中进行对象访问时,使用Node
类访问是最直观明了的访问方式。
Node
类的访问与节点的路由寻址相关,我们可以使用一系列节点进行尝试。创建好根节点main
之后,ctrl
+a
并选择创建Node2D
子节点,命名为0
。鼠标选中0
节点,按ctrl
+d
进行快捷同级复制
。
也可以通过ctrl
+c
之后,按ctrl
+v
进行快捷创建连续子节点
。
例子中我已经给根节点main
添加好脚本,接下来将演示节点访问。
访问节点时,我们如果想了解是否访问成功或了解访问的内容,就需要将访问到的信息打印到屏幕上。使用godot内置的print()
函数是很好的办法,当然也可以使用与之相关的衍生函数。
extends Node2D
func _ready() -> void:
print_tree()
pass
以上代码将在编辑器输出信息框打印出如下内容:
其中第一行的 .
代表场景的根节点main
节点上自身,因为脚本附加在main
节点上。
之后打印的内容对应0-5
这6个main
节点的子节点,以及它们的所有子节点名称。可以发现print_tree()
将打印脚本附加的节点名称及其子节点名称。详情可以右键点击该函数 - 查找符号
查看详情。
需要注意的是,这些名称不是绝对名称,如果将脚本附加在main
的子节点,或对main
的子节点创建一个新的脚本再使用print_tree()
,则结果不一定相同。
在使用同一个脚本时,由于3
节点只包括自身,所以只打印 “ .
”,而main
正常打印。
如果给5
节点也加载main.gd
脚本,则会打印“ .
”以及它的子节点名称。可以看到一连串“5
”比main
节点打印的“5
”少一个,这里请自行思考。
我们获取节点的名称,目的还是要通过名称访问节点的变量属性、函数以及它的子节点等信息。godot允许我们通过将节点拖拽到代码区进行节点访问,但是在跨多个脚本使用时,这种方式并不可靠。
不过godot提供了从项目根节点寻址的方法,而所有场景节点都在项目根节点上搭建。
这个方法就是get_tree().root.get_node()
。
其中get_tree()
可以理解为访问整个项目的目录,.root
代表访问root
节点,这个节点是godot内置的默认根节点,.get_node()
代表(按名称)访问某个节点。
extends Node2D
func _ready() -> void:
get_tree().root.print_tree()
pass
解除其他节点的main.gd
脚本后,运行以上代码,可以得到如下结果:
get_tree().root.print_tree()
代表打印root节点的所有子节点名称,需要注意的是,使用load().instantiate()
方法多次加载同一个场景文件时,godot引擎为保证资源的唯一性和易用性,新加载的场景的根节点名称会发生变化。
在实际使用过程中,由于所有场景节点都归于root的子节点,于是,除了根据名称访问,还能够通过节点的归属关系访问。
godot内置的get_children()
方法是获取某个节点的所有下一级
子节点的方法。该方法获取到的是子节点本身数据并将它们按标签存放到列表,而不仅仅是获取子节点的名称。
extends Node2D
func _ready() -> void:
print(get_tree().root.get_children())
pass
运行得到:
可以知道root节点的下一级子节点只有main
节点。由于.get_children()
方法得到的是一个数组,那么就可以通过访问数组的方法访问其中的节点。数组的查找位置从0开始,由于数组中只有main
节点,所以它是第一个,也就是第0位。
extends Node2D
func _ready() -> void:
print(get_tree().root.get_children()[0])
pass
得到:
可以看到输出的内容两边少了[]
,此时该节点已经被提取访问。
其实我们可以再加一个.get_children()
。
extends Node2D
func _ready() -> void:
print(get_tree().root.get_children()[0].get_children())
pass
可以得到main
节点的所有下一级子节点:
再尝试提取访问5
节点,可以根据它的位置5,或者末尾-1进行访问。
extends Node2D
func _ready() -> void:
print(get_tree().root.get_children()[0].get_children()[5])
#print(get_tree().root.get_children()[0].get_children()[-1])
pass
不过如果除了5
之外还要访问其他节点,如果节点特别多,语句就会特别长,句式也会变得很麻烦。这时候可以尝试使用变量进行中继,或者同时使用get_node()
方法混合访问。
extends Node2D
func _ready() -> void:
var root = get_tree().root
var main_node = get_tree().root.get_children()[0]
print(main_node.get_children()[5])
print(main_node.get_node("5"))
print(root.get_node('main/5'))
pass
以上代码会打印3条一样的信息,都表示获取到5
节点,虽然get_node()
对单双引号不敏感,但是在实际使用时建议使用双引号。
访问到节点之后可以获取节点属性,可以参考属性检查器中的内容。
点开下拉按钮之后,一般情况下可以根据属性名称,通过get()
方法获取属性值。属性的名称可以通过拖拽获取。
也可以通过文档查找。
选中先前的代码,按ctrl
+ k
注释/取消注释。添加代码并打印。
extends Node2D
func _ready() -> void:
var root = get_tree().root
var main_node = get_tree().root.get_children()[0]
#print(main_node.get_children()[5])
#print(main_node.get_node("5"))
#print(root.get_node('main/5'))
var node_5 = main_node.get_node("5")
print(node_5.get("position"))
pass
得到5
节点相对其父节点main
的位置。
如果想打印5
节点相对root
的位置,请使用global_position
。
简洁的打印全局位置属性的语法是:print(node_5.global_position)
。这同样对position
属性适用。
设置属性值可以通过set()
方法,如设置position
属性:set("position", Vector2(100,100))
。Vector2(100,100)
代表设置的目标值,position
属性的数据类型为Vector2
,因此设置的目标值的数据类型也应一致。
简洁的语法是:.position = Vector2(100,100)
。如果要单独设置x值或y值,可以使用.position.x = 100
,.position.y = 100
进行单独设置。
一些可能有用的例子
get_children()[0]
的功能也可以通过get_child(0)
实现。
获取上一级节点可以使用get_parent()
,如果访问结果不为空,这个方法同样可以多次使用,访问上上一级、上上上一级、上上上上一级……。
数据类型
godot中的数据类型有很多,比如上文中提到的[]
,这是数组结构常用的标志符号。有时候调试需要根据数据类型进行调整,godot中有typeof()
方法可以返回类型值,但并不直接返回类型名称,可以通过type_string()
将其转化为类型名称。
例举一个使用方法:type_string(typeof(1))
。
输出int
。
输出与调试器
输出
显示打印的内容,与print()
方法及其衍生或引用方法联动。print()
方法可以插入到代码中打印文本,可以通过观察该文本是否成功被打印,推断代码是否执行到这一行。类似的可以通过打断点实现(下图中红点),只要在代码左侧点击即可断点/取消断点。
不过,print()
方法还可以打印句柄或变量,以此观察代码运行过程中的步骤或数据是否正常。
调试器
中包含godot内置的报错打印以及一些功能检查工具。
如果项目不需要,除了打印错误,其他功能其实很多时候用不上。
脚本中的self
脚本中的self对所有引用该脚本的节点来说都代表“自身
”的意思。将main.gd
脚本绑定到几个节点,尝试运行。
得到:
由于3个节点同步调用一个脚本,由于资源优先级不同,打印的次序可能会不同,本文不做介绍。
注册树
当godot项目成功运行时,都会在tree
中注册默认主场景作为当前聚焦的场景。相关的方法可见get_tree().current_scene
。
此外,tree
中还会同时注册一些内置函数,供运行过程使用,如延时函数create_timer()。为方便演示,给main
添加一个Button
控件,并连接点击信号。
示例代码如下:
extends Node2D
var root
func _ready() -> void:
root = get_tree().root
pass
func _on_button_pressed() -> void:
#打印当前场景
print(get_tree().current_scene)
#等待0.5秒后执行接下来的代码
get_tree().create_timer(0.5).timeout
#获取main场景的资源,并存到new_变量中
var new_ = load("res://main.tscn").instantiate()
#root节点中加入new_,也就是main场景
root.add_child(new_)
#打印root节点的所有下一级子节点
print(root.get_children())
pass
调整按钮的大小和位置,运行结果如下:
每次点击按钮,都会打印两行信息,连续点击则连续打印。
可以发现,实际已经添加了其他节点,但get_tree().current_scene
一直是main
节点。
如果我们想要切换当前节点,就需要关闭main
节点,或跳转到其他节点。但是使用get_tree().current_scene.queue_free()
移除current_scene
之后,项目并不会自动切换主场景。
如果再次调用get_tree().current_scene.queue_free()
,则会因为current_scene
已经被移除,无法访问,弹出错误。因此在移除current_scene
之后应使用新场景补充。
由于脚本直接绑定在场景根节点,也可以使用self.queue_free()
移除自身的方式避开以上情况,不过需要注意移除默认主场景之后如果不重新设置current_scene
,则current_scene
变为空<Object#null>
。
可以看到打印内容中名字为main
的节点在不断出现,但其后的标识已经改变。
社区示例
以上内容例举了一些基本的使用技巧,此外可以在godot社区中进行学习。godot开源项目中还包含许多基础的样例,可以进行下载借鉴。同时,godot社区中还有一些资源可以进行参考使用,在学习使用过程中也可以通过视频学习加深印象。
godot官方文档(中文)
godot官方样例
资源库网站
教程和资源
总结
本文所展示的内容并不多,仅作为入门使用参考,在实际项目中,往往需要考虑许多影响因素。但万变不离其中,godot的节点控制
和路由访问
,以及可视化编辑控件
的界面是它的重要特点,根据这些特点进行探索,或许会有助于熟悉和使用。
本文到此结束。