在前面的单元里,我们学会了两种判断结构:单分支 if(“如果……就……”)和 双分支 if-else(“如果……否则……”)。它们在面对只有“是/否”两种情况时非常有效。
但真实的战场往往更加多变。想象一下英雄在森林中守卫宝箱时,他每一刻都要在脑海里回答这样一连串问题:
这三种情况是互相排斥(Mutually Exclusive)的:同一时刻,英雄只能选择其中一个动作。由于双分支只能处理两种情况的对立(A 或 B),当面对三种甚至更多情况(A 或 B 或 C)时,就需要引入本单元的主角:多分支选择结构(Multi-Branch Selection)。
| 结构 | 逻辑描述 | 适用场景 |
|---|---|---|
单分支 if | 成立就做,不成立就跳过 | 只处理一种特殊情况 |
双分支 if-else | 非此即彼,二选一 | 处理对立的两种情况 |
多分支 if-elif-else | 按重要性排队,多选一 | 处理三种及以上的互斥情况 |
Python 使用 elif(是 else if 的缩写,读作“else if”)把多个条件像链条一样串起来。一个完整的多分支结构骨架如下:
if 条件A: 动作1 # 当条件A成立时执行 elif 条件B: 动作2 # A不成立、但B成立时执行 elif 条件C: 动作3 # A、B都不成立、C成立时执行 else: 动作4 # 上面全都不成立时执行(兜底)
这三个关键词各司其职,且有着严格的数量限制:
| 关键词 | 作用 | 数量要求 |
|---|---|---|
if | 开头第一个条件,必须由它开启分支 | 有且只有 1 个 |
elif | 中间的“否则如果”,负责检查额外的条件 | 可以有 0 个、1 个或多个 |
else | 最后的兜底分支,前面全失败时执行它 | 最多 1 个,可省略 |
if、elif、else 这三个关键词必须对齐在同一列(同级缩进),它们各自下属的代码块需要统一向右缩进(通常 4 个空格)。缩进层级一旦混乱,Python 将无法识别这些分支属于同一个整体。
通过对比双分支和多分支代码,你能更直观地看到 elif 的作用:
# 只能区分两件事:顺劈斩 OR 普攻敌人 if hero.isReady("cleave"): hero.cleave() else: hero.attack(enemy) # 可以区分三件事:顺劈斩 OR 普攻敌人 OR 趁机打宝箱 if hero.isReady("cleave"): hero.cleave() elif distance < 5: hero.attack(enemy) else: hero.attack("Chest")
这是多分支最容易出错、也是最考验战术智慧的地方:Python 是自上而下逐条检查条件的,一旦命中,立刻停止。
程序运行到多分支结构时,会严格执行以下流程:
if 的条件。如果成立,就执行它的动作,然后跳过后续所有的 elif 和 else,整个多分支结构结束。if 不成立,程序才会往下看第一个 elif。如果成立,执行后直接结束。if 和所有 elif 都不成立,才会执行 else。else 里。如果你把 elif distance < 5 写在了 isReady 之前,当敌人贴脸时,英雄就会优先普攻,即使劈斩好了也不会放!
多分支的条件中经常用到小于号 <、大于号 > 等比较运算符。但在 Python 中有一个不可动摇的规则:
< 和 > 只能用来比较两个“数字”! 绝对不能拿一个物体(比如敌人对象)直接和数字比大小。
很多新手英雄会写出这样看似合理、实则报错的代码:
# ❌ 错误!findNearestEnemy 返回的是一个"敌人对象",它不是一个数字! if hero.findNearestEnemy() < 5: hero.attack(enemy)
为什么错?这就好比你在问:“这只食人魔 小于 5 吗?”——毫无逻辑可言。正确的方法是先量出距离(数字),再用距离和 5 做比较:
| 方法 | 返回的是什么类型? | 能用 < 比较吗? |
|---|---|---|
findNearestEnemy() | 一个对象(比如某个食人魔) | ❌ 不能 |
isReady("cleave") | 一个布尔值(True / False) | ❌ 不能 |
distanceTo(enemy) | 一个数字(距离多少米) | ✅ 必须可以 |
为了让条件判断更加聪明,本单元正式为你引入两个极具实战价值的新工具:一个管时间(冷却),一个管空间(距离)。
强力技能(如劈斩 cleave)非常厉害,可以一次击中周围一圈敌人,但它有冷却时间(Cooldown)。用过一次后,要等秒表走完一圈才能再次释放。
hero.isReady("cleave") 专门用于检查技能状态,它本身就会回答“是”(True)或“否”(False):
if hero.isReady("cleave"): # 因为它返回 True/False,可直接作为 if 条件 hero.cleave(enemy) # 好了就劈!
hero.distanceTo(目标) 是解决上文“比较陷阱”的钥匙。它会计算英雄坐标与目标坐标之间的实际直线距离,并返回一个准确的数字(米)。
enemy = hero.findNearestEnemy() if enemy: distance = hero.distanceTo(enemy) # 先拿到数字 if distance < 5: # 再比较数字大小 hero.attack(enemy)
本关的精髓在于用代码将战术优先级具象化。我们把三个动作按优先级填入多分支的骨架:
"Chest" 的绝佳时机。while True: enemy = hero.findNearestEnemy() if enemy: distance = hero.distanceTo(enemy) if hero.isReady('cleave'): hero.cleave(enemy) # 第一优先:群攻输出 elif distance < 5: hero.attack(enemy) # 第二优先:近战保命 else: hero.attack("Chest") # 第三优先:安全时开箱(注意引号!)
这一关是一个概念挑战(Concept Challenge)。食人魔困住了一只树精,你需要在击退敌人的同时攻击大门释放它。仔细观察你会发现,这一关的结构和“发狂的矮人”完全一模一样!
宝箱和门,本质上都是“静态目标(需要持续攻击完成任务)”。你只需要把上一个关卡 else 分支里的 "Chest" 换成 "Door",这套“三段式”战术模板就成功迁移到了新的场景中。
学完上面两个实战关卡,我们可以提炼出一套通用的“多分支决策法”,它不仅能用在游戏里,更是真正程序员每天都在使用的思维工具:
if,次重要的放在 elif。else 里。< 或 > 的地方,两边是不是都是实实在在的数字。| 报错或异常现象 | 可能的错误原因 |
|---|---|
| 英雄从不使用劈斩 | isReady 判断写得太靠后,被前面的条件(如距离判断)抢先截胡了。 |
| 程序报错说“不能进行比较” | 使用了 < 比较“敌人对象”或者“True/False”,没有提前算出数字距离。 |
| 代码总是报缩进错误 (IndentationError) | if、elif、else 这三个词没有左侧对齐,或者内部的动作忘记了缩进。 |
| 兜底动作(如开宝箱)永远不执行 | 前面的条件太容易满足了,导致程序根本走不到 else 这个阶梯。 |
| 英雄还没开完箱子就被打死 | 由于条件缺失或判定优先级过低,没有及时察觉并反击近身的敌人。 |
恭喜你完成边际森林第四单元!你从“二选一”成功升级到了掌控“多选一”,掌握了编程中极其重要的优先级决策工具。一起来回顾一下核心收获:
| 核心概念 | 关键点 | 实战应用 |
|---|---|---|
多分支 if-elif-else | 处理三个及以上的互斥情况 | 兼顾打怪、放技能与开箱任务 |
| 条件命中即停 | 自上而下逐条检查,命中第一个就停止 | 高优先级的判断必须放前面 |
距离 distanceTo | 返回距离数字,必须用数字和数字比大小 | 精准把握敌人的远近威胁 |
冷却 isReady | 返回 True/False,直接做条件 | 保证大招冷却完毕第一时间释放 |
| 三段式战术模板 | 强力技能 → 近敌自保 → 完成目标任务 | 发狂的矮人、释放树精 |
能把混乱的战场排练得井井有条,才是真正的指挥官。
继续前进,给森林中的敌人点颜色看看吧!🌲🗡️
CodeCombat 编程知识 · 边际森林:第四单元
CodeCombat教研团队