尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

用Python从零实现人狗大作战小游戏:AI追踪与代码全解析

用Python从零实现人狗大作战小游戏:AI追踪与代码全解析 之前一直觉得 Python 只是用来写爬虫、处理表格、做数据分析的工具直到在公司内部看到一个同事用几十行代码写出了一个人狗追逐的小游戏才发现原来 Python 做游戏开发也能这么有意思。那种“狗越追越近、人拼命逃跑”的紧张感配合终端里不断刷新的地图居然真的能让人玩上瘾。本文就从零开始拆解这个“人狗大作战”小游戏的完整开发流程包含游戏逻辑设计、核心代码实现、AI 追踪算法、异常处理、打包成 exe 的完整步骤以及新手最容易踩的坑。如果你是刚开始学 Python 的初学者这篇文章能帮你把random、列表、循环、函数这些零散知识点串起来如果你已经有一些 Python 基础也可以重点看第 3 节的敌人 AI 寻路逻辑和第 6 节的代码工程化优化这部分对后续做更复杂的游戏项目非常有帮助。1. 背景与核心概念1.1 这个游戏到底在玩什么“人狗大作战”本质上是一个回合制追逐游戏。玩家控制“人”在地图上移动电脑控制“狗”自动追踪玩家。玩家需要在规定回合内到达安全出口或者坚持足够多的回合不被狗追上而狗每回合都会根据玩家的位置向玩家靠近一步直到抓住玩家。听起来简单但真正实现起来你至少要处理下面几个核心问题地图如何用代码表示玩家和狗的初始位置如何生成玩家如何输入移动指令狗如何自动追踪玩家游戏胜负条件如何判断玩家输入非法字符时怎么办这些问题分别对应 Python 中的列表操作、随机数、条件判断、循环、函数封装和异常处理正好覆盖了 Python 入门阶段最重要的知识点。这也是为什么这个游戏非常适合用来提升对 Python 的兴趣——知识点没有一个是孤立的全部服务于“做一个能玩的东西”。1.2 为什么推荐用 Python 做小游戏很多人的第一反应是做游戏不是应该用 Unity 或者 Godot 吗为什么要用 Python其实 Python 在游戏开发中有自己的独特定位。对于学习阶段来说Python 的语法接近自然语言不需要理解复杂的指针、内存管理等底层概念可以让你把注意力集中在“游戏逻辑”本身。更重要的是Python 社区有非常丰富的游戏开发库比如pygame可以做图形界面游戏turtle可以做简单的绘图动画而本文先不引入任何第三方库只用 Python 自带的random库和标准输入输出就能实现一个终端版本的追逐游戏。这样做有两个好处环境门槛低只要有 Python 环境就能跑。代码可读性强每一行都能看懂适合逐行分析和修改。当你理解了终端版本的实现思路后后续切换到pygame做 2D 图形版会发现逻辑几乎可以复用只是把“输出字符地图”换成“绘制图片方块”而已。1.3 游戏的基本规则设计在设计代码之前先把规则写清楚。这里的规则不需要多复杂但必须明确否则后续写代码时容易逻辑混乱。本游戏设定的基本规则如下地图大小为 5×5 网格。玩家用字符P表示狗用D表示出口用E表示。玩家先行动每回合可以向上、下、左、右四个方向移动一格。玩家移动后狗根据玩家位置自动移动一格。如果狗移动后和玩家位于同一格游戏失败。如果玩家到达出口游戏胜利。如果玩家坚持 15 回合仍未失败也判定胜利。这个规则可以随时调整。比如把地图扩大、增加狗的数量、增加障碍物这些都是游戏设计层面的扩展不影响核心代码框架。2. 环境准备与版本说明2.1 安装 Python本游戏不依赖任何第三方库只需要 Python 3.6 及以上版本即可。如果你还没有安装 Python可以参考下面的步骤。Windows 系统打开 Python 官网下载页面。下载 Windows 安装包建议选择 64 位版本。安装时务必勾选“Add Python to PATH”。安装完成后打开命令行输入python --version验证。macOS 系统python3 --versionLinux 系统以 Ubuntu/Debian 为例sudo apt update sudo apt install python3验证安装python --version如果输出类似下面的内容说明安装成功Python 3.10.122.2 IDE 或编辑器推荐写这种小游戏用轻量级编辑器就够了。常用的选择有VS Code免费插件丰富支持 Python 调试配置好 Python 环境后非常顺手。PyCharmJetBrains 出品功能强大社区版免费适合长期学习 Python。终端 系统自带文本编辑器最轻量但不推荐新手使用因为缺少语法高亮和错误提示。本教程的代码不依赖 IDE 特性用任何编辑器都可以。重点是代码本身。2.3 项目结构说明为了后续扩展方便项目文件建议按下述结构组织dog_game/ ├── main.py # 游戏主程序 ├── game_functions.py # 游戏核心函数可选拆分 └── README.md # 项目说明本文为了演示方便先使用单个main.py文件。当代码量增大后再拆分成多个模块。3. 核心语法与关键实现思路在写完整代码之前需要把几个核心知识点单独拎出来讲清楚。3.1 二维列表表示地图地图本质是一个二维网格。在 Python 中最自然的表示方式是“列表的列表”也就是二维列表。map_data [ [ , , , , ], [ , , , , ], [ , , , , ], [ , , , , ], [ , , , , ] ]每个位置可以用map_data[row][col]访问。这里的row是行号从 0 开始col是列号从 0 开始。为什么不用一维列表因为二维列表在逻辑上更贴近地图的物理结构。当玩家输入“向上移动”时我们只需要把玩家的行号减 1输入“向右移动”时把列号加 1。这种坐标操作非常直观。# 玩家当前位置 player_row 2 player_col 2 # 向上移动 player_row - 1 # 向右移动 player_col 1但需要注意直接对玩家坐标加减后必须判断新坐标是否越界。比如玩家已经在第 0 行再向上移动就会变成 -1这是非法的。3.2 random 随机数生成初始位置玩家和狗的初始位置不应该写死否则每次游戏都一样乐趣也会减少。这时使用random模块生成随机坐标。import random player_row random.randint(0, 4) player_col random.randint(0, 4) dog_row random.randint(0, 4) dog_col random.randint(0, 4)但这里有一个问题玩家和狗的初始位置可能会相同或者狗正好出生在出口位置。这样游戏一开始就结束了体验不好。所以在生成坐标后需要加一层判断如果玩家位置、狗位置、出口位置三者有重叠就重新生成。def generate_position(exclude_positions): while True: row random.randint(0, 4) col random.randint(0, 4) if (row, col) not in exclude_positions: return row, col这个方法的核心思路是把已经占用的位置传给函数函数在生成随机坐标后检查是否冲突如果冲突就重新生成。这是一个非常通用的“避免随机冲突”方案在后续开发中会经常用到。3.3 玩家输入处理与边界检查玩家输入通常使用input()函数。我们需要约定输入指令的含义w或W向上s或S向下a或A向左d或D向右q或Q退出游戏拿到输入后先统一转换为小写再判断方向。与此同时还要检查移动后是否越界move input(请输入移动方向 (w/a/s/d): ).lower() new_row, new_col player_row, player_col if move w: new_row - 1 elif move s: new_row 1 elif move a: new_col - 1 elif move d: new_col 1 elif move q: print(游戏已退出) break else: print(无效输入请重新输入) continue # 判断是否越界 if new_row 0 or new_row 5 or new_col 0 or new_col 5: print(无法移动到边界外请重新输入) continue这个逻辑的要点是先计算出“如果移动新位置是什么”再统一判断合法性而不是直接修改玩家坐标。这样可以避免移动到非法位置后还需要回滚的麻烦。3.4 狗 AI 的最简实现曼哈顿距离追踪狗要怎么追踪玩家最简答的方式是“曼哈顿距离”追踪也就是分别比较行号和列号向玩家所在方向靠近一步。具体逻辑# 如果狗的行号小于玩家的行号狗向下移动 if dog_row player_row: dog_row 1 # 如果狗的行号大于玩家的行号狗向上移动 elif dog_row player_row: dog_row - 1 # 行号相同则尝试在列方向上靠近 else: if dog_col player_col: dog_col 1 elif dog_col player_col: dog_col - 1这个逻辑实现的就是“先走行方向对齐再走列方向对齐”。理解起来非常简单而且效果已经足够好狗会以最短的直线路径向玩家逼近。如果想增加游戏难度可以加入“狗每次移动时有一定概率走斜线”的逻辑。不过二维网格中“斜线”需要同时改变行和列这里先不展开。3.5 游戏主循环结构终端游戏本质上是一个“渲染-输入-更新-渲染”的循环。这个循环会一直执行直到游戏结束。while True: # 1. 绘制当前地图 # 2. 检查游戏状态 # 3. 获取玩家输入 # 4. 更新玩家位置 # 5. 更新狗位置 # 6. 检查是否结束游戏循环的写法非常固定关键在于顺序不能乱。如果先更新狗位置再获取玩家输入玩家就会“慢一步”游戏难度会变大。本文采用“玩家先动狗再动”的顺序保证规则明确。4. 完整实战案例人狗大作战接下来是本文的核心部分逐步写出完整的游戏代码。请先创建项目文件夹然后在文件夹中创建main.py文件。4.1 创建项目结构mkdir dog_game cd dog_game touch main.pyWindows 下可以使用mkdir dog_game cd dog_game type nul main.py4.2 完整代码下面是main.py的完整代码。为了方便阅读先写完整版本再逐段解释。import random # 定义地图大小 MAP_SIZE 5 # 定义最大回合数 MAX_ROUND 15 def generate_position(occupied_positions): 在地图范围内生成一个未被占用的随机位置 while True: row random.randint(0, MAP_SIZE - 1) col random.randint(0, MAP_SIZE - 1) if (row, col) not in occupied_positions: return row, col def draw_map(player_pos, dog_pos, exit_pos): 根据玩家、狗、出口的位置绘制当前地图 for row in range(MAP_SIZE): line for col in range(MAP_SIZE): if (row, col) player_pos: line P elif (row, col) dog_pos: line D elif (row, col) exit_pos: line E else: line . print(line) print() def move_player(current_pos, direction): 根据方向移动玩家返回新位置如果非法返回原位置 row, col current_pos if direction w: row - 1 elif direction s: row 1 elif direction a: col - 1 elif direction d: col 1 else: return current_pos # 检查是否越界 if row 0 or row MAP_SIZE or col 0 or col MAP_SIZE: return current_pos return row, col def move_dog(dog_pos, player_pos): 狗向玩家方向移动一步 dog_row, dog_col dog_pos player_row, player_col player_pos if dog_row player_row: dog_row 1 elif dog_row player_row: dog_row - 1 elif dog_col player_col: dog_col 1 elif dog_col player_col: dog_col - 1 return dog_row, dog_col def main(): print( 人狗大作战 ) print(规则P 代表你D 代表狗E 代表出口。) print(使用 W/A/S/D 移动坚持 15 回合或到达出口即可获胜。) print(输入 Q 退出游戏。\n) occupied_positions set() # 生成玩家位置 player_pos generate_position(occupied_positions) occupied_positions.add(player_pos) # 生成出口位置 exit_pos generate_position(occupied_positions) occupied_positions.add(exit_pos) # 生成狗位置 dog_pos generate_position(occupied_positions) occupied_positions.add(dog_pos) round_count 0 while True: draw_map(player_pos, dog_pos, exit_pos) print(f当前回合: {round_count 1} / {MAX_ROUND}) # 检查是否到达出口 if player_pos exit_pos: print(恭喜你成功到达出口游戏胜利) break # 检查回合数 if round_count MAX_ROUND: print(你坚持了足够多的回合游戏胜利) break # 获取玩家输入 direction input(请输入移动方向 (w/a/s/d): ).lower() if direction q: print(游戏已退出) break # 玩家移动 new_player_pos move_player(player_pos, direction) if new_player_pos player_pos: print(无法移动请重新输入方向。) continue player_pos new_player_pos # 玩家到达出口判断 if player_pos exit_pos: draw_map(player_pos, dog_pos, exit_pos) print(恭喜你成功到达出口游戏胜利) break # 狗移动 dog_pos move_dog(dog_pos, player_pos) # 检查狗是否抓到玩家 if dog_pos player_pos: draw_map(player_pos, dog_pos, exit_pos) print(你被狗抓住了游戏结束。) break round_count 1 if __name__ __main__: main()4.3 代码分段讲解这段代码看上去不长但包含了多个重要设计。下面逐个模块讲解。生成随机位置generate_position函数接收一个集合参数occupied_positions集合中存储的是已经被占用的坐标。函数通过while True不断生成随机位置直到生成一个没有被占用的坐标。def generate_position(occupied_positions): while True: row random.randint(0, MAP_SIZE - 1) col random.randint(0, MAP_SIZE - 1) if (row, col) not in occupied_positions: return row, col这里使用集合而不是列表是因为集合的in判断效率更高。虽然在这个小地图中体验不出差别但这是一个好的编程习惯。绘制地图draw_map函数使用两层循环遍历地图的每个格子根据坐标内容决定输出什么字符。def draw_map(player_pos, dog_pos, exit_pos): for row in range(MAP_SIZE): line for col in range(MAP_SIZE): if (row, col) player_pos: line P elif (row, col) dog_pos: line D elif (row, col) exit_pos: line E else: line . print(line) print()这里有个小技巧先把一行内容累积到字符串line中最后统一print(line)而不是每打印一个字符就换行。这样做可以保证每行字符显示在一行中。玩家移动move_player函数接收当前坐标和方向返回新坐标。如果移动越界则返回原来的坐标。def move_player(current_pos, direction): row, col current_pos if direction w: row - 1 elif direction s: row 1 elif direction a: col - 1 elif direction d: col 1 else: return current_pos if row 0 or row MAP_SIZE or col 0 or col MAP_SIZE: return current_pos return row, col狗移动move_dog函数是游戏 AI 的核心。它的逻辑是先比较行号如果狗在玩家上方就向下走在下方就向上走只有在行号相同时才比较列号。def move_dog(dog_pos, player_pos): dog_row, dog_col dog_pos player_row, player_col player_pos if dog_row player_row: dog_row 1 elif dog_row player_row: dog_row - 1 elif dog_col player_col: dog_col 1 elif dog_col player_col: dog_col - 1 return dog_row, dog_col这种 AI 实现虽然简单却产生了真实的“追击感”。玩家在逃跑时需要提前预判狗的位置否则很容易被逼到角落。主函数与游戏循环main()函数负责初始化游戏状态并执行游戏循环。循环内的顺序如下绘制当前地图。检查玩家是否到达出口。检查回合数是否超出上限。获取玩家输入。更新玩家位置。检查玩家是否到达出口。更新狗位置。检查狗是否抓到玩家。回合数加 1。4.4 运行与验证在终端中运行python main.py首次运行后界面会输出类似下面的内容 人狗大作战 规则P 代表你D 代表狗E 代表出口。 使用 W/A/S/D 移动坚持 15 回合或到达出口即可获胜。 输入 Q 退出游戏。 D . . . . . . . P . . . . . . . . E . . . . . . . 当前回合: 1 / 15 请输入移动方向 (w/a/s/d):此时输入s玩家向下移动狗也会同步移动。如果运气好几步内就能到达出口如果运气差可能会被狗逼到角落。多玩几局后你对“坐标变化”和“循环控制”的理解会非常深刻。4.5 预期结果说明游戏的胜负分支包括玩家到达出口输出“恭喜你成功到达出口游戏胜利”回合数超过 15输出“你坚持了足够多的回合游戏胜利”狗抓住玩家输出“你被狗抓住了游戏结束。”玩家主动退出输出“游戏已退出”当你运行代码后可以根据输出判断代码逻辑是否正确。如果发现不管怎么输入都报错优先检查input().lower()这一步是否拼写正确。5. 常见问题与排查思路5.1 常见报错对照表问题现象常见原因解决思路ModuleNotFoundError: No module named random项目里创建了名为random.py的文件删除或重命名 local 文件避免覆盖标准库ValueError: too many values to unpack (expected 2)坐标变量与返回值数量不匹配检查generate_position是否同时返回 row 和 col输入方向后不移动move_player返回原位置但玩家没有区分“原地不动”和“移动到了原位置”在调用处判断new_player_pos player_pos游戏直接胜利或直接失败初始位置生成时没有排除出口或狗的位置检查generate_position的冲突排除逻辑地图显示错位Windows 终端字符宽度问题将字符间隔调整为两个空格或使用英文标点运行闪退使用了input()但在 IDE 中运行方式不对在终端命令行中运行不要直接双击 py 文件5.2 为什么地图一直不更新一个常见的误区是在draw_map中直接print了地图但在循环开始前没有清屏。终端输出会一直累积导致旧地图叠在新地图上。解决方式有两种方式一不清屏每次输出后空一行。这种方式最简单代码可读性也最好适合学习阶段。方式二调用系统清屏命令。在 Windows 上是os.system(cls)在 Linux/macOS 上是os.system(clear)。需要注意跨平台兼容封装成一个函数import os def clear_screen(): os.system(cls if os.name nt else clear)在游戏循环开始处调用clear_screen()即可。但清屏后用户看不到历史记录各有优劣。5.3 狗为什么不追我如果狗在原地不动首先检查move_dog函数是否在循环中被调用。常见错误是少写了dog_pos move_dog(dog_pos, player_pos)这一步或者把狗移动后没有赋值回dog_pos。另外要注意狗移动的方向判断顺序是“先列后行”还是“先行后列”。本文实现的是“先行后列”如果修改成“先列后行”狗的行为会有细微差别但都能实现追踪效果。5.4 输入 q 无法退出这个问题多半是因为输入时使用了中文输入法input()接收到的可能是中文符号。建议在提示信息中明确要求“请切换到英文输入法”。另外可以在判断前对输入做标准化处理direction input(请输入移动方向 (w/a/s/d): ).strip().lower()加上.strip()可以去除首尾空格避免用户误打空格导致判断失败。6. 扩展优化与工程化建议当核心功能跑通后就可以考虑扩展了。扩展的方向有很多下面列几个性价比比较高的优化点。6.1 增加障碍物地图全是空白区域玩久了会觉得单调。可以在地图上随机生成一些障碍物用字符#表示。玩家和狗都不能穿越障碍物。具体实现时在初始位置生成后再随机选择几个空白位置变成障碍物。玩家移动时判断目标位置是否是障碍物如果是则无法移动。狗移动时同样需要判断。这会让游戏策略性大大提升因为障碍物会改变狗的追击路径。6.2 增加多只狗当玩家掌握了基本操作后一只狗已经不够刺激。可以让游戏支持多只狗例如把dog_pos从单个坐标变成坐标列表。dog_positions [(1, 2), (3, 4), (0, 1)]每回合每只狗依次移动如果任意一只狗追上玩家游戏结束。这个扩展需要把move_dog的调用改成循环并且每只狗在移动时都要判断是否与玩家相遇。6.3 加入计分和关卡系统可以设计一个简单的计分系统玩家每成功存活一回合加 10 分到达出口额外加 50 分。连续游玩时分数累计。再配合关卡系统每过一关地图变大、狗的数量增加。score 0 level 1这样游戏的耐玩性会成倍提升也能帮助你练习“状态管理”和“模块化设计”。6.4 打包成 exe如果想把游戏分享给没有安装 Python 的朋友可以使用PyInstaller打包成 exe 文件。这个操作对于很多初学者来说非常有成就感。首先安装 PyInstallerpip install pyinstaller然后在项目目录下执行pyinstaller -F main.py打包完成后在dist目录下可以找到main.exe。双击即可运行无需安装 Python 环境。需要注意如果使用了os.system(cls)清屏打包后的 exe 在部分精简 Windows 环境下可能找不到cls命令建议打包前把清屏逻辑改成不依赖外部命令的方式。6.5 代码结构调整建议当代码量增长到几百行后建议拆分模块。比如把地图生成、角色移动、AI 逻辑、游戏循环分别放到不同文件中dog_game/ ├── main.py # 游戏入口 ├── map.py # 地图生成与绘制 ├── characters.py # 玩家与狗的逻辑 ├── ai.py # 狗 AI 策略 └── config.py # 常量配置模块拆分后修改 AI 逻辑时不需要动游戏主循环新增道具时也不需要改动地图生成代码。这是从“脚本”走向“项目”的关键一步。6.6 安全与健壮性建议在实际项目中还需要考虑下面几点所有用户输入必须做有效性校验不能让非法输入导致程序崩溃。对随机初始位置做冲突检测避免角色重叠导致开局即结束。使用if __name__ __main__:保护主逻辑方便导入模块进行单元测试。写一个简单的测试用例比如验证狗在同行时能否正确靠近玩家。7. 总结与下一步学习路线通过“人狗大作战”这个小项目你覆盖了 Python 中非常重要的几个知识点二维列表操作、随机数生成、函数设计与模块化思维、循环控制、输入校验、坐标运算、简单的 AI 算法以及游戏循环的基本框架。这些知识不仅在游戏开发中有用在爬虫、数据分析、自动化脚本等领域同样会反复出现。如果接下来想继续深入学习建议按以下顺序推进先试着自己调整游戏参数比如扩大地图、增加回合数、增加狗的数量观察游戏行为的变化。把游戏从终端版迁移到pygame图形版体会“逻辑层”和“表现层”如何分离。学习 Python 单元测试给move_player和move_dog函数写测试用例。尝试加入存档功能用 JSON 或文本文件保存最高分和游戏进度。学习 Git把项目托管到远程仓库管理不同版本的游戏迭代。这个项目最大的价值在于每一行代码都是可运行、可验证、可修改的。遇到报错不用慌按着报错信息回看对应函数基本都能定位问题。多做几次这样的排错练习你对 Python 的信心就会完全不同。如果本文对你有帮助可以收藏备用后续我会继续分享从终端游戏到 pygame 图形游戏的升级教程包括地图编辑、精灵动画、碰撞检测这些更进阶的内容。
返回列表