Godot 4 GDScript 教程 / 函数与 Lambda
函数与 Lambda
函数是 GDScript 的核心构建单元。Godot 4 中函数系统得到了显著增强,引入了 Lambda 表达式、Callable 对象和 await 协程,让代码组织更加灵活。本章详细介绍 GDScript 2.0 的函数系统。
1. 函数定义
1.1 基本语法
# 基本函数
func say_hello() -> void:
print("Hello, Godot!")
# 带参数的函数
func greet(name: String) -> void:
print(f"Hello, {name}!")
# 带返回值的函数
func add(a: int, b: int) -> int:
return a + b
# 多参数带默认值
func spawn_enemy(
enemy_type: String,
position: Vector2 = Vector2.ZERO,
level: int = 1,
is_boss: bool = false
) -> void:
print(f"生成 {enemy_type} 于 {position} Lv.{level}" + (" [BOSS]" if is_boss else ""))
# 调用示例
func _ready() -> void:
say_hello()
greet("Player")
var result := add(10, 20)
print(f"10 + 20 = {result}")
spawn_enemy("slime", Vector2(100, 200))
spawn_enemy("dragon", Vector2(500, 300), 10, true)
1.2 参数传递
# 值类型参数(int, float, bool, String, Vector2 等)是值传递
func modify_value(val: int) -> void:
val = 999 # 不会影响原始变量
# 引用类型参数(Node, Array, Dictionary 等)是引用传递
func modify_array(arr: Array) -> void:
arr.append(4) # 会影响原始数组
# 但是 arr = [1,2,3] 这样赋值不会影响原始数组
func _ready() -> void:
var x := 10
modify_value(x)
print(x) # 输出: 10 (未改变)
var numbers := [1, 2, 3]
modify_array(numbers)
print(numbers) # 输出: [1, 2, 3, 4] (已修改)
1.3 返回值
# 单返回值
func get_health() -> int:
return health
# 多返回值(使用 Array)
func get_position_and_velocity() -> Array:
return [position, velocity]
# 使用 Dictionary 返回多个值
func get_stats() -> Dictionary:
return {
"health": health,
"mana": mana,
"attack": attack
}
# 使用命名元组模式
func get_transform_info() -> Array:
var pos := Vector2(100, 200)
var rot := 45.0
var scl := Vector2(2, 2)
return [pos, rot, scl]
func _ready() -> void:
# 解构多返回值
var info := get_position_and_velocity()
var pos: Vector2 = info[0]
var vel: Vector2 = info[1]
# Dictionary 方式
var stats := get_stats()
print(f"HP: {stats['health']}, MP: {stats['mana']}")
2. 静态函数
2.1 static 函数
class_name MathUtils
# 静态函数 - 不需要实例即可调用
static func clamp_vector2(value: Vector2, min_val: float, max_val: float) -> Vector2:
return Vector2(
clampf(value.x, min_val, max_val),
clampf(value.y, min_val, max_val)
)
static func lerp_angle_deg(a: float, b: float, t: float) -> float:
return rad_to_deg(lerp_angle(deg_to_rad(a), deg_to_rad(b), t))
static func random_point_in_circle(radius: float) -> Vector2:
var angle := randf() * TAU
var dist := sqrt(randf()) * radius
return Vector2(cos(angle), sin(angle)) * dist
static func ease_out_quad(t: float) -> float:
return 1.0 - (1.0 - t) * (1.0 - t)
static func ease_in_out_cubic(t: float) -> float:
if t < 0.5:
return 4.0 * t * t * t
else:
return 1.0 - pow(-2.0 * t + 2.0, 3) / 2.0
# 使用静态函数
func _ready() -> void:
var clamped := MathUtils.clamp_vector2(Vector2(1000, 500), 0, 800)
var random_pos := MathUtils.random_point_in_circle(200.0)
var eased := MathUtils.ease_out_quad(0.5)
2.2 static 变量
class_name GameSettings
# 静态变量 - 全局共享
static var master_volume: float = 1.0
static var music_volume: float = 0.8
static var sfx_volume: float = 1.0
static var difficulty: String = "normal"
static func apply_settings() -> void:
AudioServer.set_bus_volume_db(
AudioServer.get_bus_index("Master"),
linear_to_db(master_volume)
)
static func get_difficulty_multiplier() -> float:
match difficulty:
"easy": return 0.75
"normal": return 1.0
"hard": return 1.5
"nightmare": return 2.0
return 1.0
⚠️ 注意: 静态函数不能访问实例变量或调用非静态函数。静态变量在所有实例间共享。
3. Lambda 表达式
3.1 基本 Lambda
# Lambda 表达式(匿名函数)
var greet = func(name: String) -> void:
print(f"Hello, {name}!")
# 调用 Lambda
func _ready() -> void:
greet.call("Player")
# 立即调用
func(x: int) -> int:
return x * 2
.call(5) # 返回 10,但结果无法获取(Godot 4.x 限制)
# 存储 Lambda
var multiply = func(a: int, b: int) -> int:
return a * b
var result: int = multiply.call(3, 4)
print(f"3 * 4 = {result}")
3.2 Lambda 作为回调
# Lambda 用作回调函数
extends Node
func _ready() -> void:
var timer := Timer.new()
timer.wait_time = 1.0
timer.autostart = true
add_child(timer)
# 使用 Lambda 连接信号
var counter := 0
timer.timeout.connect(func():
counter += 1
print(f"计时器触发: {counter}次")
if counter >= 5:
timer.stop()
)
# Lambda 作为函数参数
func process_items(items: Array, callback: Callable) -> void:
for item in items:
callback.call(item)
func _process_items_example() -> void:
var names := ["Alice", "Bob", "Charlie"]
process_items(names, func(name: String) -> void:
print(f"处理: {name}")
)
3.3 Lambda 捕获变量
# Lambda 可以捕获外部变量(闭包)
extends Node
func create_counter(start: int = 0) -> Callable:
var count := start
var counter = func() -> int:
count += 1
return count
return counter
func create_multiplier(factor: int) -> Callable:
return func(value: int) -> int:
return value * factor
func _ready() -> void:
# 计数器闭包
var counter := create_counter(10)
print(counter.call()) # 11
print(counter.call()) # 12
print(counter.call()) # 13
# 乘法器闭包
var double := create_multiplier(2)
var triple := create_multiplier(3)
print(double.call(5)) # 10
print(triple.call(5)) # 15
💡 提示: Lambda 表达式可以捕获外部变量形成闭包,这在创建回调和高阶函数时非常有用。
4. Callable 对象
4.1 Callable 基础
extends Node
# 创建 Callable
var callable_1: Callable = my_function # 从函数创建
var callable_2: Callable = Callable(self, "my_function") # 显式创建
var callable_3: Callable = func(): pass # Lambda
func my_function() -> void:
print("函数被调用")
# Callable 方法
func _ready() -> void:
# 调用
callable_1.call()
# 检查
print(callable_1.is_valid()) # true
print(callable_1.get_method()) # "my_function"
print(callable_1.get_object()) # self (节点引用)
# 带参数调用
var add = func(a: int, b: int) -> int:
return a + b
print(add.call(3, 4)) # 7
# 绑定参数
var add_5 = add.bind(5)
print(add_5.call(10)) # 15 (10 + 5)
4.2 Callable 与信号
extends Node
signal health_changed(old_value: int, new_value: int)
signal died
@onready var timer: Timer = $Timer
func _ready() -> void:
# 连接信号使用 Callable
health_changed.connect(_on_health_changed)
died.connect(func(): print("角色死亡"))
# 绑定额外参数
health_changed.connect(_on_health_changed_with_context.bind("玩家"))
# 一次性连接
died.connect(_on_died_once, CONNECT_ONE_SHOT)
# Lambda 连接
var damage_count := 0
health_changed.connect(func(old_val: int, new_val: int):
if new_val < old_val:
damage_count += 1
print(f"受伤次数: {damage_count}")
)
func _on_health_changed(old_value: int, new_value: int) -> void:
print(f"生命值变化: {old_value} -> {new_value}")
func _on_health_changed_with_context(
old_value: int, new_value: int, context: String
) -> void:
print(f"{context} 生命值: {old_value} -> {new_value}")
func _on_died_once() -> void:
print("只执行一次的死亡处理")
5. 信号与 Callable
# 信号连接的新语法
extends CharacterBody2D
signal damaged(amount: int)
signal healed(amount: int)
signal level_up(new_level: int)
@onready var area: Area2D = $HurtBox
@onready var anim_player: AnimationPlayer = $AnimationPlayer
func _ready() -> void:
# 新 Callable 语法
area.body_entered.connect(_on_body_entered)
area.area_entered.connect(_on_area_entered)
# Lambda 连接
damaged.connect(func(amount: int):
print(f"受到 {amount} 点伤害")
)
# 信号链 - 一个信号触发另一个信号
healed.connect(func(_amount: int):
# 治疗时播放特效
anim_player.play("heal_effect")
)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("enemies"):
var damage: int = body.get("attack_damage") if body.has_method("get") else 10
take_damage(damage)
func _on_area_entered(area: Area2D) -> void:
if area.is_in_group("health_pickup"):
heal(25)
area.queue_free()
func take_damage(amount: int) -> void:
damaged.emit(amount)
func heal(amount: int) -> void:
healed.emit(amount)
6. await 协程
6.1 基本 await
# await 等待信号
extends Node
@onready var timer: Timer = $Timer
func start_countdown(seconds: int) -> void:
for i in range(seconds, 0, -1):
print(f"倒计时: {i}")
await get_tree().create_timer(1.0).timeout
print("开始!")
# 等待动画完成
func play_attack_animation() -> void:
var anim_player: AnimationPlayer = $AnimationPlayer
anim_player.play("attack")
await anim_player.animation_finished
print("攻击动画完成")
# 等待自定义信号
signal loaded
func load_data() -> void:
print("开始加载...")
# 模拟加载
await get_tree().create_timer(2.0).timeout
loaded.emit()
func wait_for_load() -> void:
await loaded
print("数据加载完成")
6.2 await 与协程模式
extends CharacterBody2D
# 协程式移动
func move_to(target: Vector2, duration: float) -> void:
var start_pos := global_position
var elapsed := 0.0
while elapsed < duration:
var t := elapsed / duration
global_position = start_pos.lerp(target, t)
elapsed += get_process_delta_time()
await get_tree().process_frame
global_position = target
# 序列化动作
func perform_combo() -> void:
print("连招开始")
await attack("slash")
await get_tree().create_timer(0.2).timeout
await attack("thrust")
await get_tree().create_timer(0.3).timeout
await attack("uppercut")
print("连招完成")
func attack(anim_name: String) -> void:
var anim_player: AnimationPlayer = $AnimationPlayer
anim_player.play(anim_name)
await anim_player.animation_finished
# 带超时的等待
func wait_with_timeout(seconds: float) -> bool:
await get_tree().create_timer(seconds).timeout
return true
# 使用自定义信号的协程
signal input_received(input_vector: Vector2)
func wait_for_input() -> Vector2:
var input := await input_received
return input
6.3 await 注意事项
# ⚠️ await 的常见陷阱
# 陷阱 1: await 后对象可能已被释放
func bad_example() -> void:
await get_tree().create_timer(1.0).timeout
# 节点可能已从场景树移除
queue_free() # 可能报错
# 正确做法:检查 is_instance_valid
func good_example() -> void:
await get_tree().create_timer(1.0).timeout
if not is_instance_valid(self):
return
queue_free()
# 陷阱 2: 多个 await 的执行顺序
func multiple_awaits() -> void:
# 这两个是顺序执行的
await get_tree().create_timer(1.0).timeout
print("1秒后")
await get_tree().create_timer(1.0).timeout
print("2秒后")
# 陷阱 3: await 在 for 循环中
func loop_with_await() -> void:
for i in range(5):
await get_tree().create_timer(0.5).timeout
print(f"步骤 {i + 1}")
⚠️ 注意: 使用 await 时,代码在信号发出后才会继续执行。如果节点在等待期间被释放,会导致错误。始终检查 is_instance_valid(self)。
7. 函数式编程模式
7.1 数组的函数式操作
# map - 转换每个元素
func _ready() -> void:
var numbers := [1, 2, 3, 4, 5]
# 平方每个数
var squared := numbers.map(func(x: int) -> int:
return x * x
)
print(squared) # [1, 4, 9, 16, 25]
# 转换为字符串
var strings := numbers.map(func(x: int) -> String:
return f"Item-{x}"
)
print(strings) # ["Item-1", "Item-2", ...]
# filter - 过滤元素
var even := numbers.filter(func(x: int) -> bool:
return x % 2 == 0
)
print(even) # [2, 4]
# reduce - 聚合
var sum := numbers.reduce(func(acc: int, x: int) -> int:
return acc + x
, 0)
print(sum) # 15
# any - 是否存在满足条件的元素
var has_large := numbers.any(func(x: int) -> bool:
return x > 3
)
print(has_large) # true
# all - 是否所有元素满足条件
var all_positive := numbers.all(func(x: int) -> bool:
return x > 0
)
print(all_positive) # true
7.2 自定义高阶函数
# 高阶函数:接受或返回函数的函数
class_name FunctionalUtils
static func pipe(value: Variant, functions: Array[Callable]) -> Variant:
"""管道:按顺序应用多个函数"""
var result := value
for fn in functions:
result = fn.call(result)
return result
static func compose(functions: Array[Callable]) -> Callable:
"""组合:创建一个依次执行多个函数的新函数"""
return func(value: Variant) -> Variant:
var result := value
for fn in functions:
result = fn.call(result)
return result
static func debounce(callback: Callable, delay: float, node: Node) -> Callable:
"""防抖:延迟执行,重复调用时重新计时"""
var timer: SceneTreeTimer
return func(args: Variant = null) -> void:
timer = node.get_tree().create_timer(delay)
timer.timeout.connect(func():
callback.call(args)
, CONNECT_ONE_SHOT)
# 使用示例
func _ready() -> void:
var double = func(x: int) -> int: return x * 2
var add_one = func(x: int) -> int: return x + 1
var to_string = func(x: int) -> String: return str(x)
# 管道
var result := FunctionalUtils.pipe(5, [double, add_one, to_string])
print(result) # "11" (5 -> 10 -> 11 -> "11")
# 组合
var transform := FunctionalUtils.compose([double, add_one])
print(transform.call(5)) # 11
8. get_method_list 内省
# 方法内省:在运行时获取对象的方法信息
extends Node
func _ready() -> void:
# 获取所有方法
var methods := get_method_list()
# 打印方法信息
for method in methods:
print(f"方法: {method['name']}, 参数: {method['args'].size()}, 返回: {method['return']['type']}")
# 检查方法是否存在
if has_method("custom_function"):
call("custom_function")
# 搜索特定方法
var process_methods := methods.filter(func(m: Dictionary) -> bool:
return m["name"].begins_with("_process")
)
print(f"找到 {process_methods.size()} 个 process 方法")
func custom_function() -> void:
print("自定义函数被调用")
# 动态调用方法
func call_method_on_children(method_name: String, args: Array = []) -> void:
for child in get_children():
if child.has_method(method_name):
child.callv(method_name, args)
9. 游戏开发场景
场景:行为树系统
class_name BehaviorTree
extends Node
## 行为树节点类型
enum Status { SUCCESS, FAILURE, RUNNING }
## 行为树节点
class BTNode:
var name: String
var children: Array[BTNode] = []
func tick(blackboard: Dictionary) -> Status:
return Status.FAILURE
## 行为节点(执行动作)
class BTAction extends BTNode:
var action: Callable
func _init(action_name: String, action_callable: Callable) -> void:
name = action_name
action = action_callable
func tick(blackboard: Dictionary) -> Status:
return action.call(blackboard)
## 条件节点
class BTCondition extends BTNode:
var condition: Callable
func _init(cond_name: String, cond_callable: Callable) -> void:
name = cond_name
condition = cond_callable
func tick(blackboard: Dictionary) -> Status:
return Status.SUCCESS if condition.call(blackboard) else Status.FAILURE
## 序列节点(所有子节点都成功才成功)
class BTSequence extends BTNode:
var current_child: int = 0
func tick(blackboard: Dictionary) -> Status:
while current_child < children.size():
var status := children[current_child].tick(blackboard)
match status:
Status.FAILURE:
current_child = 0
return Status.FAILURE
Status.RUNNING:
return Status.RUNNING
Status.SUCCESS:
current_child += 1
current_child = 0
return Status.SUCCESS
## 选择节点(任一子节点成功就成功)
class BTSelector extends BTNode:
var current_child: int = 0
func tick(blackboard: Dictionary) -> Status:
while current_child < children.size():
var status := children[current_child].tick(blackboard)
match status:
Status.SUCCESS:
current_child = 0
return Status.SUCCESS
Status.RUNNING:
return Status.RUNNING
Status.FAILURE:
current_child += 1
current_child = 0
return Status.FAILURE
10. 扩展阅读
上一章: 05 - 类型化 GDScript 下一章: 07 - 信号系统