第七章:控制流程
"程序的力量在于能够根据不同情况做出不同的决策。"
控制流程是编程的核心概念之一,它让程序能够根据条件执行不同的代码、重复执行某些操作。本章将详细介绍GDScript中的所有控制流程语句。
7.1 条件语句
7.1.1 if语句
最基本的条件判断:
var health = 100
if health > 0:
print("角色存活")
7.1.2 if-else语句
两种情况的选择:
var health = 0
if health > 0:
print("角色存活")
else:
print("角色死亡")
7.1.3 if-elif-else语句
多条件判断:
var score = 85
if score >= 90:
print("优秀")
elif score >= 80:
print("良好")
elif score >= 70:
print("中等")
elif score >= 60:
print("及格")
else:
print("不及格")
7.1.4 嵌套条件
var is_alive = true
var health = 30
if is_alive:
if health < 25:
print("生命危险!")
elif health < 50:
print("生命值较低")
else:
print("状态良好")
else:
print("已死亡")
7.1.5 条件表达式(三元运算符)
# 基本形式
var status = "存活" if health > 0 else "死亡"
# 嵌套使用
var grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "F"
# 在函数调用中使用
print("存活" if health > 0 else "死亡")
# 赋值中使用
var damage = base_damage * 2 if is_critical else base_damage
7.1.6 条件判断的简写
# 检查非空/非零
var player = get_player()
if player: # 等同于 if player != null
player.move()
# 检查数组非空
var items = []
if items: # 等同于 if items.size() > 0
print("有物品")
else:
print("背包空空")
# 检查字符串非空
var name = ""
if name: # 等同于 if name != ""
print("有名字")
# 布尔取反
var is_dead = false
if not is_dead: # 等同于 if is_dead == false
print("还活着")
7.2 match语句(模式匹配)
match语句是GDScript的强大特性,类似其他语言的switch,但功能更强。
7.2.1 基本匹配
var command = "attack"
match command:
"attack":
print("执行攻击")
"defend":
print("执行防御")
"heal":
print("执行治疗")
_: # 默认情况(类似default)
print("未知命令")
7.2.2 多值匹配
var key = KEY_W
match key:
KEY_W, KEY_UP:
move_up()
KEY_S, KEY_DOWN:
move_down()
KEY_A, KEY_LEFT:
move_left()
KEY_D, KEY_RIGHT:
move_right()
_:
pass
7.2.3 模式匹配
# 数组模式匹配
var input = [1, 2, 3]
match input:
[]:
print("空数组")
[var x]:
print("单元素:", x)
[var x, var y]:
print("两个元素:", x, y)
[var x, ..]:
print("以", x, "开头的数组")
_:
print("其他情况")
# 字典模式匹配
var data = {"type": "enemy", "health": 100}
match data:
{"type": "player", ..}:
print("这是玩家")
{"type": "enemy", "health": var h}:
print("敌人,生命值:", h)
_:
print("未知类型")
7.2.4 绑定变量
var result = [true, 42]
match result:
[true, var value]:
print("成功,值为:", value)
[false, var error]:
print("失败,错误:", error)
7.2.5 守卫条件(when)
var number = 15
match number:
var n when n < 0:
print("负数")
var n when n == 0:
print("零")
var n when n < 10:
print("个位数")
var n when n < 100:
print("两位数")
_:
print("更大的数")
7.2.6 枚举匹配
enum State { IDLE, RUNNING, JUMPING, FALLING }
var current_state = State.JUMPING
match current_state:
State.IDLE:
play_animation("idle")
State.RUNNING:
play_animation("run")
State.JUMPING:
play_animation("jump")
State.FALLING:
play_animation("fall")
7.3 循环语句
7.3.1 for循环
遍历数组:
var fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
print(fruit)
遍历字典:
var player = {"name": "Hero", "health": 100, "mana": 50}
# 遍历键
for key in player:
print(key, ": ", player[key])
# 遍历键(显式)
for key in player.keys():
print(key)
# 遍历值
for value in player.values():
print(value)
范围循环:
# range(end) - 从0到end-1
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# range(start, end) - 从start到end-1
for i in range(2, 5):
print(i) # 2, 3, 4
# range(start, end, step) - 带步长
for i in range(0, 10, 2):
print(i) # 0, 2, 4, 6, 8
# 倒序
for i in range(5, 0, -1):
print(i) # 5, 4, 3, 2, 1
遍历字符串:
var text = "Godot"
for char in text:
print(char) # G, o, d, o, t
遍历节点:
# 遍历所有子节点
for child in get_children():
print(child.name)
# 遍历分组中的节点
for enemy in get_tree().get_nodes_in_group("enemies"):
enemy.take_damage(10)
带索引遍历:
var items = ["剑", "盾", "药水"]
# 方法1:使用range
for i in range(items.size()):
print(i, ": ", items[i])
# 方法2:手动计数
var index = 0
for item in items:
print(index, ": ", item)
index += 1
7.3.2 while循环
# 基本while循环
var count = 0
while count < 5:
print(count)
count += 1
# 游戏循环示例
var hp = 100
while hp > 0:
hp -= 10
print("受到伤害,剩余HP:", hp)
print("死亡!")
# 无限循环(谨慎使用)
while true:
process_input()
if should_exit:
break
7.3.3 循环控制
break - 跳出循环:
for i in range(10):
if i == 5:
break # 跳出循环
print(i) # 0, 1, 2, 3, 4
# 在嵌套循环中
for i in range(3):
for j in range(3):
if i == 1 and j == 1:
break # 只跳出内层循环
print(i, j)
continue - 跳过本次迭代:
for i in range(5):
if i == 2:
continue # 跳过2
print(i) # 0, 1, 3, 4
# 跳过空值
for item in items:
if item == null:
continue
process(item)
pass - 空语句占位:
for i in range(5):
pass # 暂时不做任何事
if condition:
pass # TODO: 待实现
7.4 迭代器和生成器
7.4.1 自定义迭代器
class_name CustomRange
var start: int
var end: int
var current: int
func _init(p_start: int, p_end: int):
start = p_start
end = p_end
func _iter_init(_arg) -> bool:
current = start
return current < end
func _iter_next(_arg) -> bool:
current += 1
return current < end
func _iter_get(_arg):
return current
# 使用
var my_range = CustomRange.new(5, 10)
for i in my_range:
print(i) # 5, 6, 7, 8, 9
7.4.2 常用迭代模式
# 枚举迭代
var items = ["a", "b", "c"]
for i in range(items.size()):
var item = items[i]
print("索引 %d: %s" % [i, item])
# 反向迭代
for i in range(items.size() - 1, -1, -1):
print(items[i])
# 同时迭代两个数组
var names = ["Alice", "Bob"]
var scores = [100, 85]
for i in range(min(names.size(), scores.size())):
print(names[i], ": ", scores[i])
7.5 异常处理
GDScript没有传统的try-catch,但提供了其他错误处理机制。
7.5.1 assert断言
func divide(a: float, b: float) -> float:
assert(b != 0, "除数不能为零!")
return a / b
# 在调试模式下,assert失败会暂停执行
# 在发布版本中,assert会被忽略
7.5.2 错误检查模式
# 返回值检查
func load_data(path: String) -> Dictionary:
if not FileAccess.file_exists(path):
push_error("文件不存在:" + path)
return {}
var file = FileAccess.open(path, FileAccess.READ)
if file == null:
push_error("无法打开文件:" + path)
return {}
var data = file.get_as_text()
return JSON.parse_string(data)
# 使用
var data = load_data("config.json")
if data.is_empty():
print("加载失败")
7.5.3 Result模式
# 使用数组返回结果和错误
func safe_divide(a: float, b: float) -> Array:
if b == 0:
return [false, "除数不能为零"]
return [true, a / b]
# 使用
var result = safe_divide(10, 0)
if result[0]:
print("结果:", result[1])
else:
print("错误:", result[1])
# 使用字典
func try_operation() -> Dictionary:
if some_error:
return {"success": false, "error": "操作失败"}
return {"success": true, "value": computed_value}
7.6 实用控制流模式
7.6.1 状态机
enum State { IDLE, PATROL, CHASE, ATTACK }
var current_state: State = State.IDLE
func _process(delta: float) -> void:
match current_state:
State.IDLE:
process_idle(delta)
State.PATROL:
process_patrol(delta)
State.CHASE:
process_chase(delta)
State.ATTACK:
process_attack(delta)
func change_state(new_state: State) -> void:
# 退出当前状态
match current_state:
State.CHASE:
stop_chase_sound()
current_state = new_state
# 进入新状态
match new_state:
State.ATTACK:
play_attack_animation()
7.6.2 事件循环
var event_queue: Array = []
func add_event(event: Dictionary) -> void:
event_queue.append(event)
func process_events() -> void:
while not event_queue.is_empty():
var event = event_queue.pop_front()
handle_event(event)
func handle_event(event: Dictionary) -> void:
match event.type:
"damage":
take_damage(event.amount)
"heal":
heal(event.amount)
"buff":
apply_buff(event.buff_type, event.duration)
7.6.3 条件执行链
# 使用and短路
func try_attack() -> void:
can_attack() and has_target() and perform_attack()
# 使用or短路(默认值模式)
func get_config_value(key: String) -> Variant:
return config.get(key) or default_values.get(key) or null
# 链式条件检查
func validate_player() -> bool:
if not player:
return false
if not player.is_alive:
return false
if player.is_stunned:
return false
return true
# 简化版本
func validate_player_v2() -> bool:
return player and player.is_alive and not player.is_stunned
7.6.4 递归
# 阶乘
func factorial(n: int) -> int:
if n <= 1:
return 1
return n * factorial(n - 1)
# 斐波那契
func fibonacci(n: int) -> int:
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# 递归遍历节点树
func find_all_sprites(node: Node) -> Array[Sprite2D]:
var result: Array[Sprite2D] = []
if node is Sprite2D:
result.append(node)
for child in node.get_children():
result.append_array(find_all_sprites(child))
return result
# 注意:递归深度过大会导致栈溢出
# 考虑使用迭代或尾递归优化
7.7 性能考虑
7.7.1 循环优化
# 避免在循环中重复计算
# 不推荐
for i in range(items.size()):
for j in range(items.size()): # 每次都调用size()
pass
# 推荐
var size = items.size()
for i in range(size):
for j in range(size):
pass
# 避免在循环中修改被迭代的数组
# 不推荐
for item in items:
if should_remove(item):
items.erase(item) # 危险!
# 推荐
var to_remove = []
for item in items:
if should_remove(item):
to_remove.append(item)
for item in to_remove:
items.erase(item)
# 或使用filter
items = items.filter(func(item): return not should_remove(item))
7.7.2 条件优化
# 将最可能为true的条件放前面
if common_case:
# 常见情况
elif rare_case:
# 罕见情况
# 使用短路求值避免不必要的计算
if simple_check and expensive_check():
pass
# 缓存重复使用的条件结果
var is_valid = check_validity() # 只调用一次
if is_valid:
do_something()
if is_valid:
do_something_else()
本章小结
本章我们全面学习了GDScript的控制流程:
- 条件语句:if/elif/else、三元运算符
- match语句:模式匹配、多值匹配、守卫条件
- 循环语句:for循环、while循环、range函数
- 循环控制:break、continue、pass
- 迭代器:自定义迭代器、常用迭代模式
- 错误处理:assert、错误检查、Result模式
- 实用模式:状态机、事件循环、递归
- 性能优化:循环和条件的优化技巧
控制流程让程序能够做出决策和重复执行,是编程的核心。下一章我们将学习函数与方法,让代码更加模块化和可复用。
上一章:变量与数据类型
下一章:函数与方法