Appearance
GDScript 节点与场景操作
访问子节点
通过路径访问
gdscript
$NodePath$ 是 get_node() 的简写形式,用于获取当前节点的子节点。
gdscript
$Sprite2D
$AnimationPlayer
$"My Child Node"路径表示法
| 语法 | 说明 |
|---|---|
$Child | 获取名为 Child 的直接子节点 |
$Child/SubChild | 获取 Child 下的 SubChild 节点 |
%Node | 获取唯一节点(用 unique_name 设置过的节点) |
脚本所在节点相关
gdscript
get_node(".") # 获取自身
get_parent() # 获取父节点
get_tree().root # 获取根节点(场景树的最高层)
get_tree().current_scene # 获取当前正在运行的场景获取多个子节点
gdscript
get_children() # 返回所有子节点的数组
get_child_count() # 返回子节点数量
get_node_or_null("Child") # 安全获取,不存在返回 null
has_node("Child") # 检查是否存在指定节点动态增删节点
添加节点
gdscript
var new_node = Node2D.new()
add_child(new_node) # 添加为子节点删除节点
gdscript
remove_child(node) # 从父节点移除(节点仍在内存中)
node.queue_free() # 延迟释放(当前帧结束后释放,安全)
node.free() # 立即释放(危险,可能出错)实例化场景
instance() 是实例化 PackedScene 的标准方法:
gdscript
var packed_scene = preload("res://path/to/scene.tscn")
var instance = packed_scene.instance()
add_child(instance)instance() 会返回一个根节点类型的新节点树,自动设置好父子关系。
动态增删节点示例
gdscript
extends Node2D
var bullet_scene = preload("res://Bullet.tscn")
func spawn_bullet():
var bullet = bullet_scene.instance()
bullet.position = Vector2(100, 100)
add_child(bullet)
func remove_bullet(bullet):
remove_child(bullet)
bullet.queue_free()实例化场景的其他方式
使用 add_child 时设置参数
gdscript
var instance = packed_scene.instance()
add_child(instance, true) # 第二参数 true 表示 "inherit_owner"克隆节点(不推荐)
gdscript
var copy = duplicate()
add_child(copy)检测输入
动作定义
在 Project > Project Settings > Input Map 中定义动作名称和对应的按键。
输入检测函数
gdscript
is_action_pressed("move_right") # 按住时返回 true
is_action_just_pressed("jump") # 刚按下那一帧返回 true
is_action_just_released("jump") # 刚释放那一帧返回 true键盘按键直接检测
gdscript
Input.is_key_pressed(KEY_ESCAPE)
Input.is_mouse_button_pressed(BUTTON_LEFT)鼠标输入
gdscript
Input.getMouseMotion() # 获取鼠标移动量
Input.get_globalMousePosition() # 获取鼠标全局位置
get_viewport().get_mouse_position() # 获取当前视口内的鼠标位置摇杆/手柄
gdscript
Input.get_joy_axis(0, JOY_AXIS_0) # 获取手柄摇杆值
Input.is_joy_button_pressed(0, JOY_BUTTON_A) # 手柄按钮检测输入示例
gdscript
func _physics_process(delta):
var velocity = Vector2.ZERO
if Input.is_action_pressed("move_right"):
velocity.x += 1
if Input.is_action_pressed("move_left"):
velocity.x -= 1
if Input.is_action_pressed("jump"):
velocity.y -= 1
position += velocity.normalized() * speed * delta资源路径
常用路径
| 路径 | 说明 |
|---|---|
res:// | 项目根目录(项目资源文件夹) |
res://path/to/file | 绝对路径,从项目根开始 |
user:// | 用户数据目录(可写,用于存档等) |
获取路径的方法
gdscript
get_path() # 获取当前节点所在场景中的路径
get_scene_file_path() # 获取当前场景的 .tscn 路径
ResourceLoader.load("res://...") # 动态加载资源路径操作
gdscript
get_path().get_base_dir() # 获取所在目录
get_path().get_file() # 获取文件名(含扩展名)
get_path().get_extension() # 获取扩展名动态加载资源
gdscript
var texture = load("res://assets/image.png")
var scene = load("res://path/to/scene.tscn")
# preload 在脚本解析时执行(编译期)
var icon = preload("res://icon.png")访问项目设置中的自定义路径
gdscript
ProjectSettings.get("custom_setting/path")实用技巧
节点路径缓存(性能优化)
gdscript
onready var sprite = $Sprite2D
# 或在 _ready() 中缓存
var sprite: Sprite2D
func _ready():
sprite = get_node("Sprite2D")安全地检查和获取节点
gdscript
if has_node("Child"):
var child = get_node("Child")
# 安全使用 child
var node = get_node_or_null("OptionalChild")
if node:
# node 存在跨场景访问
gdscript
get_tree().root.get_node("Main/Player")
get_tree().current_sceneget_tree() 场景树操作
get_tree() 返回 SceneTree 对象,是场景管理的核心。
常用属性
| 属性/方法 | 说明 |
|---|---|
root | 获取根视口,是所有场景的最上层节点 |
current_scene | 获取当前正在运行的主场景 |
paused | 获取/设置游戏暂停状态 |
场景切换
gdscript
get_tree().reload_current_scene() # 重新加载当前场景
get_tree().change_scene("res://path/to/scene.tscn") # 切换到指定场景场景切换示例
gdscript
func go_to_game():
get_tree().change_scene("res://Game.tscn")
func restart_level():
get_tree().reload_current_scene()异步场景切换
gdscript
func _ready():
var result = get_tree().change_scene("res://Game.tscn")
if result == OK:
print("场景切换成功")暂停与恢复
gdscript
get_tree().paused = true # 暂停游戏
get_tree().paused = false # 恢复游戏遍历场景树
gdscript
get_tree().get_nodesInGroup("enemies") # 获取所有在 "enemies" 分组的节点
get_tree().call_group("enemies", "die") # 调用分组中所有节点的 "die" 方法
get_tree().notify_child_processed(node) # 通知子节点已处理分组管理
gdscript
# 添加节点到分组
add_to_group("players")
add_to_group("enemies")
# 从分组移除
remove_from_group("players")
# 检查节点是否在分组中
is_in_group("players")全局节点访问
gdscript
get_tree().root.get_node("Main/Player") # 通过绝对路径访问
get_tree().getFirstNode() # 获取第一个匹配节点发出信号
gdscript
get_tree().emit_signal("game_over") # 发出全局信号
get_tree().broadcast("game_over") # 向所有节点广播获取根视口
gdscript
get_tree().root # 获取根视口
get_viewport() # 获取当前脚本所在节点的视口帧同步
gdscript
get_tree().processFrame # 等待下一帧(idle process)
get_tree().physicsProcessFrame # 等待下一物理帧(physics process)quit 退出游戏
gdscript
get_tree().quit() # 退出游戏注意事项
节点生命周期
- 不要在
_init()中访问节点:节点尚未加入场景树,$和get_node()可能返回null _ready()是最早可安全访问子节点的时机:此时节点已完全加入场景树queue_free()是安全的删除方式:在当前帧结束后释放,避免在物理处理中立即删除导致的崩溃
节点路径访问
$和get_node()只能在场景树内使用:独立运行的脚本(如main()函数)无法使用- 路径是相对于脚本所在节点:使用相对路径时注意当前节点的位置
- 节点名称区分大小写:
$Sprite2D和$sprite2d是不同的节点 %唯一名称只在当前场景内唯一:跨场景使用可能导致冲突
实例化与删除
- 实例化后必须
add_child():仅instance()不会显示节点 - 删除节点前先检查存在性:已删除的节点调用
queue_free()不会有问题,但free()会报错 - 父子关系影响生命周期:子节点会随父节点一起删除或隐藏
- 避免循环引用:大量节点动态创建/删除时注意内存管理
场景切换
change_scene()是同步的:场景加载完成前游戏会卡住,大型场景建议用change_scene_to()异步加载- 切换场景不会自动清理旧场景:需确保旧场景的节点已正确释放,否则可能导致内存泄漏
reload_current_scene()会丢失未保存的状态:做好数据持久化
输入检测
_input()比 Input.is_action_pressed 更早触发:但_input()每帧可能多次调用_physics_process()固定帧率:不受delta影响的物理计算应放在这里paused = true会阻塞_physics_process():但_input()和_process()默认仍会响应
资源加载
preload()在解析脚本时执行:路径必须是硬编码的常量,不能动态拼接load()在首次调用时缓存:后续调用返回同一对象,需注意状态共享- 资源路径使用正斜杠
/:Godot 内部会自动转换,但跨平台时需注意
分组与全局访问
- 分组名称建议统一命名规范:如
"enemies"、"players"、"ui"等 call_group()会遍历所有节点:大量节点时注意性能,可使用call_group_flags()- 避免过度依赖
get_tree().root跨层级访问:会增加耦合度