第七章:控制流程

第七章:控制流程

"程序的力量在于能够根据不同情况做出不同的决策。"

控制流程是编程的核心概念之一,它让程序能够根据条件执行不同的代码、重复执行某些操作。本章将详细介绍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的控制流程:

  1. 条件语句:if/elif/else、三元运算符
  2. match语句:模式匹配、多值匹配、守卫条件
  3. 循环语句:for循环、while循环、range函数
  4. 循环控制:break、continue、pass
  5. 迭代器:自定义迭代器、常用迭代模式
  6. 错误处理:assert、错误检查、Result模式
  7. 实用模式:状态机、事件循环、递归
  8. 性能优化:循环和条件的优化技巧

控制流程让程序能够做出决策和重复执行,是编程的核心。下一章我们将学习函数与方法,让代码更加模块化和可复用。


上一章:变量与数据类型

下一章:函数与方法

← 返回目录