前阵子我给自己布置了一个小任务从新建一个空文件夹开始做出一个真正能玩的、带AI敌人的游戏而不是那种只能走迷宫、怪物只会直线巡场的demo。我选择的引擎是Godot目标平台是PC和Web最终花了两周业余时间做出来一个2D俯视角逃脱小游戏地图里有墙壁、有出口玩家控制角色绕迷宫AI敌人会巡逻一旦“看见”玩家就会追击被抓到游戏结束抵达出口则胜利。整个项目用了不到600行GDScript核心AI逻辑大约只占三分之一的代码量但这就是一个完整AI游戏的雏形。这个教程面向两类人一类是刚接触Godot、想完成第一个“有AI机制”项目的新手另一类是做过小地图、但对游戏AI只停留在理论状态机、行为树不知道怎么落地的同学。我不会只贴代码然后让你抄我会把每一步的“为什么这么设计”和“踩到的坑”也写出来。毕竟AI游戏的难点不是引擎API而是搞清楚“电脑控制的角色该怎么思考”。1. 动手前先把游戏范围压到最小一个可运行的AI追赶游戏我见过太多人一开始就想做一个“开放世界多智能体强化学习”的游戏结果写了两周连一个可执行文件都没跑出来。做“带AI的游戏”和做“AI研究项目”是两回事前者要把“可玩”放第一后者才需要纠结算法复杂度。我给自己定的最小目标是一张迷宫地图、一个玩家、一个AI敌人、一个出口。规则一句话——AI敌人会追击玩家玩家跑出迷宫即赢。就这样一个小项目里面已经包含了游戏开发里最常碰到的几个模块场景组织、角色控制、物理碰撞、二维移动、导航寻路、状态切换、游戏状态管理、UI反馈。任何一块漏掉游戏都跑不起来。更重要的是“一个AI敌人”已经足够让你体验AI决策的核心感知、判断、行动。很多人会问我做成敌人巡逻、追人这不就是个很土的游戏AI吗对传统游戏AI从来不是深度学习而是用明确规则让NPC表现出“像人一样的意图”。这个思路今天依然广泛用于各类商业游戏比如《调谐小精灵》《暗黑破坏神》里的怪物。理解了状态机和寻路哪怕以后你要接强化学习、行为树也只是换引擎的装配方式而已。我把项目命名为“MazeRunner”核心参数列出来元素配置引擎版本Godot 4.2视角2D 俯视玩家技能移动WASD/方向键无攻击AI敌人巡逻、警觉、追击三状态导航寻路胜利条件到达出口区域失败条件玩家被敌人触碰地图构建TileMapLayer StaticBody2D先把范围牢牢锁死。如果你中途想加跳跃、敌人血量、技能按键、背包系统请全部砍掉。每增加一个系统教程复杂度就会翻倍。等核心闭环跑通了再考虑扩展。2. 为什么最终选了Godot而不是Unity或Cocos标准答案往往很势利看你发布到哪里。但这篇教程偏重“从零学习”我的选择很明确Godot。我对比过三个常见免费引擎先放结论对比项Godot 4.xUnity (个人版)Cocos Creator授权MIT开源免费订阅制免费版收入门槛免费版可用安装包不到100MB安装轻量安装包数GB启动慢中等依赖Node生态2D渲染极干净内置TileMap、导航依赖插件预制体系统重不错但API有历史包袱脚本语言GDScript/Python风格C#TypeScript桌面/Web导出很顺容易胖侧重微信小游戏学习曲线平缓陡峭适合会TS的人如果你的目标是“游戏先跑起来、搞清楚AI逻辑”Godot天然合适。GDScript语法非常接近Python我一边写教程一边读代码几乎没有任何理解成本。Unity当然可以但你要为“怎么下载Hub”“怎么配Visual Studio”折腾半天Cocos则更适合你已经确定要发微信小游戏而且熟悉TypeScript的场景。我遇到的另一个常见纠结是“Godot 3还是4”。2025年的现在直接选4.x尽量用4.2或更高版本。早期4.0有导航系统API变动4.2之后稳定很多。此外Godot官方文档提供了很完整的2D导航教程这对我们做AI敌人追踪非常关键。课外小提示Godot是开源引擎如果以后你想研究引擎源码或者担心第三方授权它对你更可控。3. 先设计AI的“大脑”有限状态机的巡逻、警觉、追击AI敌人的核心是状态机。它不玄乎就像一个人在办公室的状态坐在工位工作、站起来去倒水离位、听到会议呼叫响应。每个状态只有“当前在做什么、什么时候跳走”两个问题。我给敌人设计三种状态巡逻PATROL在几个设定点之间慢速来回走。如果玩家进入视野转到警觉。警觉ALERT玩家可能在附近敌人加速向最后看到的位置移动眼睛一直盯着视野范围。如果玩家持续可见转到追击如果丢失时间超过阈值转回巡逻。追击CHASE朝玩家当前位置移动。如果玩家连续3秒不可见转回巡逻如果玩家距离小于某个值判定抓住游戏结束。为什么不用行为树行为树更灵活、可配置性更强但需要额外实现节点器和黑板数据。对于单个敌人状态机用枚举和if/else就足够了。等你要做多个行为组合、并行逻辑时行为树是下一步。用状态机起步可以让你把“状态切换”这个思维模型吃透代码也容易调试。我要强调游戏AI的“感知”不追求物理精确。敌人有一个Area2D作为眼睛半径设置了280像素。在这个范围内检测到玩家就进入警觉或追击。但这不够因为如果隔着一堵墙也能感应到玩家会觉得AI开透视。所以更真实一点的做法是用RayCast2D从敌人眼睛位置朝向玩家发射一条射线碰到墙壁则视为“看不到”。这样“视野遮挡”就成了影响AI决策的变量。RAY地图上的敌人如果看不到你但一直往墙后面冲那才是正确的——因为它靠直觉追人而不是靠穿墙。4. 从新建项目到跑通场景代码骨架与关键操作4.1 创建项目和基础场景打开Godot新建项目模板选“2D Scene”。项目名不重要但路径不要带中文。我习惯把项目根目录扔在D盘某个英文路径下Windows导出时中文路径偶尔会出问题。场景树大致如下MainNode2DMazeTileMapLayerPlayerCharacterBody2D CollisionShape2D Sprite2DEnemyCharacterBody2D CollisionShape2D Sprite2D Area2D RayCast2D NavigationAgent2DUICanvasLayer Control不要一开始就把所有节点加满。先做地图再一个角色一个角色加每加一个都跑一次“F6运行”看重置。4.2 用TileMap画迷宫创建TileMapLayer节点给它加一个TileSet资源。我用了自己画的简笔砖块一张32x32的PNG上面一个实心方格子。在TileSet里设定物理层把方格子标记为“碰撞”然后直接用刷子在编辑里画迷宫。画迷宫有个技巧不要画复杂的连通图先画一个9x7的简单网格留几条通道。太复杂的墙体不仅玩家容易迷路AI导航烘焙时也容易出问题。我第一版画了个大号迷宫结果AI寻路死路很长后面马上简化了。4.3 玩家控制脚本玩家的核心代码很简单绑定到Player节点上extends CharacterBody2D export var move_speed: float 120.0 func _physics_process(delta: float) - void: var direction : Input.get_vector(left, right, up, down) velocity direction * move_speed move_and_slide()需要手动在项目设置里定义输入映射left、right、up、down绑定按键。方向键和WASD都绑上不然你自己测试时会觉得别扭。Input.get_vector会自动返回一个归一化向量所以斜着走速度不会变成1.414倍这个细节省事。move_speed我设了120因为这里是迷宫寻路太快会撞墙撞到手疼。4.4 AI敌人脚本的骨架状态机与物理移动敌人脚本里最重要的一段是状态机更新。我一开始只用_physics_process里的一堆if去做结果状态多了非常乱。后来改成这样enum State { PATROL, ALERT, CHASE } export var patrol_points: Array[Node2D] [] # 巡逻点手动拖放到数组 var current_state: int State.PATROL var player_ref: Node2D null var seen_timer: float 0.0 var lost_timer: float 0.0 func _physics_process(delta: float) - void: match current_state: State.PATROL: _do_patrol(delta) State.ALERT: _do_alert(delta) State.CHASE: _do_chase(delta)这种match结构把所有逻辑分文别类后面加新状态只需要加一行匹配和一个函数非常清晰。每个函数里负责“感知 行动”。比如_do_chasefunc _do_chase(delta: float) - void: if _can_see_player(): lost_timer 0.0 nav_agent.target_position player_ref.global_position else: lost_timer delta if lost_timer 3.0: _change_state(State.PATROL) _move_along_navigation(delta)4.5 让敌人沿着导航移动在Godot 4里AI移动不要自己去插值会灾难般卡墙。做法是给敌人加一个NavigationAgent2D节点然后把他的当前位置按固定间隔传给agent然后方向取agent.get_next_path_position()与自己位置之差。实际的移动函数func _move_along_navigation(delta: float) - void: nav_agent.target_position _get_target_position() var next_pos: Vector2 nav_agent.get_next_path_position() var move_dir : (next_pos - global_position).normalized() velocity move_dir * move_speed move_and_slide()注意move_and_slide()是CharacterBody2D自带方法所以敌人也是CharacterBody2D。这样能共享受物理碰撞和防止穿透的特性。NavigationAgent2D负责规划路径但实际移动还是要用手势来。中心思想就是导航代理只给你“下一步要去哪”不替你推人。4.6 用导航遮罩烘焙地图这一步是让AI不穿墙的关键。在Main下添加一个NavigationRegion2D节点然后把你的TileMapLayer拖进它的导航遮罩里。具体操作选中NavigationRegion2D在属性面板的“Navigation”分层里把TileMapLayer添加进对象列表然后在“TileMapLayer”的图层属性中把“Navigation Layer”启用并刷上导航区域。最后在NavigationRegion2D节点上点击“Bake NavigationMesh”绿色区域会覆盖可通行地段。如果不烘焙导航代理会认为全地图都是空地敌人会直线穿过墙壁去找你瞬间变成“穿墙挂”。5. 试玩后我被自己的AI蠢到了避坑与调优的三次迭代第一次跑通时整个游戏非常尴尬玩家站在角落里敌人像一块橡皮糖一样在墙上来回蹭怎么都绕不来偶尔又像开了天眼一样穿过墙冲过来。下面是我记录的三个问题以及对应解决方案。5.1 敌人原地蹭墙导航没更新碰撞层我发现敌人一直在墙边抖动。排查了很久才发现自己给TileMapLayer设置了物理碰撞层但NavigationRegion2D的烘焙区域没有正确排除墙壁。因为NavMesh烘焙时读的是TileMap的物理层如果我的TileSet没有把“Navigation”这一层的墙格标记为“不可通行”烘焙结果就会把墙也当成可走区域。导航代理让AI沿着路径走到墙边但物理碰撞阻挡了移动于是频繁抖动。后来把所有墙壁格子在TileSet的Navigation图层上设为“空白”不赋予导航区域在“物理”层设为“碰撞”重新烘焙敌人终于能绕着走了。5.2 AI“看见”玩家后瞬间失去目标导致状态堕胎第二版试吹敌人有时到了玩家附近但又转过头走。原因是我每次在chase状态里检查是否看到玩家一旦被墙面挡视线就立刻清零chip最后敌人反复在“追-停-追-停”中抽风。我在两个地方加了计时器连续看到玩家0.5秒才进入chasechase状态下连续丢失玩家超过1秒后才标记为“暂时丢失”。这个改动让AI行动平滑了很多也更有“人味”。我的经验总结是游戏AI的感知与决策一定要有像人一样的“滞后感”不要即时而精确。人从视觉信号到反应至少有一两百毫秒的延迟游戏里加一点点延迟会减少机械感。5.3 平衡难度把参数变成可调的表玩家跑得快AI追不上玩家跑得慢AI又太离谱。我把所有速度、感知半径、计时阈值全部用export定义便于随时调整。调整后发现这个结构的乐趣参数空间非常大参数修改效果视野半径半径越大难度指数级上升chase速度玩家速度的0.75倍较合适alert持续时长太长则会变成“永久追”lost_timer阈值越大敌人越容易放弃越小越执着实际手感玩家speed120敌人patrol25alert40chase95视野300这些数值在一张中等迷宫图上体验适中。想要加压迫感把chase速度调到110玩家会全程紧张。6. AI辅助工具在项目里的真实价值不是魔法是脚手架我在开发这游戏的间隙试了多种AI编程助手和AI绘画工具。它们确实能帮上忙但用法需要摸清楚。6.1 让AI生成GDScript骨架我向一款通用AI编程助手输入了一段自然语言需求“请用Godot 4编写一个CharacterBody2D敌人有巡逻、警觉、追击状态使用NavigationAgent2D输入参数包括速度和视野半径。”它直接给了我一份完整的GDScript。那份代码的框架在90%的情况下是能用的但有几个坑它默认我用了Input.get_axis这种旧写法它没有帮我处理Area2D的信号连接它以为move_and_collide比move_and_slide更适合移动实际在角色控制器里前者要手动处理线条碰撞它不知道我的TileSet的物理层需要调整。AI给我省了不少打字功夫基本把状态切换和计数器的套路搭好了但调试依然靠人眼。如果你不懂GDScriptAI生成的代码你没有能力修复。所以我强烈建议AI当成“脚手架”你的大脑永远是“裁判”。写代码前先能读懂它的输出再跑起来。6.2 AI绘画生成素材管用但别追求太精致游戏素材我用了一个AI绘画工具让它“生成一个32x32像素的怪兽卡通图标透明背景”。结果还是很不错的但有两个问题第一生成图片大多不是严格像素风格颜色过渡生硬需要自己缩放后打磨第二有的图背景不是透明仍带个白边。我用在线抠图工具裁掉白边再把缩放到64x64后放进去。AI素材适合快速给原型试配色最终版本如果要商业化还是得手动画或买授权素材。6.3 多轮对话做Debug“print”比AI可靠我遇到一个诡异Bug敌人只在玩家离自己右侧时能看到但左侧贴着鼻子都看不见。起初想让AI帮我找原因但它只会泛泛而谈“检查碰撞层”。最后我用print打印RayCast2D的colliding_with和射线的cast_to方向才发现自己忘了把射线长度翻倍因为玩家作为物理对象在射线方向上占了半个身体宽度。把RayCast2D的长度从80调到120问题立刻解决。Debug时逐条打印局部数值、坐标和碰撞体名字比AI大海捞针更有效。7. 怎样把它导出成Windows、Linux和Web版本如果你的AI游戏只能停留在编辑器里那你会错过很大的乐趣。Godot导出PC和Web很轻松但有一些细节。7.1 添加导出预设在“项目”菜单里打开“导出”添加Windows Desktop、Linux Desktop和Web。Windows预设需要填Windows模板路径首次导出会自动去下载模板。我建议预先把所有模板都下载好避免离线时尬住。Web导出的一个坑默认导出文件是二进制wasm和一堆JavaScript需要用Gzip或Brotli压缩。我在“导出设置”里选了“Compress: gzip”然后在服务器上添加MIME类型application/wasm否则网站访问会失败。如果只是想本机玩用Godot给的测试Web导出就可以了。7.2 关于微信小游戏平台如果你只有这个目标选Cocos更现实我知道很多同学留言问“只上线微信小游戏用Godot还是Cocos”。从技术角度说Godot的Web导出可以再封装一层适配微信小游戏的运行环境但过程极其繁琐需要额外踩“WASM体积过大”“网络分包”“API bridge”这类坑。而Cocos Creator本身就带微信小游戏一键发布流程体积控制和基础库适配都已经做得很完善。如果你唯一目标就是微信小游戏没理由不用Cocos。但如果你只是想纯学习、做独立首发到PC和Web或者你偏爱Python风格Godot完全能打。我用Godot做了几款2D小游戏导出和稳定性都让我安心。8. 从小白到自己实现下一步这个AI游戏还能怎么升级这个游戏做完之后我最大的体感就是AI游戏中最贵的不是代码而是“如何定义行为”的思维方式。一旦状态机写顺了你定义AI的行动边界、参数阈值、触发条件就会非常有条理这是以后做行为树、GOAP目标导向行为规划甚至强化学习的筋骨。几个不扯远但可以立刻落地的优化方向添加多个巡逻点。把patrol_points数组从两个点扩展到五个点敌人按顺序巡逻路线更自然。添加多个敌人。用场景实例化复制几个Enemy每个都独立AI。再给它们加简单协作比如追击时共享玩家位置一个“警备群”的感觉就出来了。做一个“玩家视野”系统。用视锥射线检测敌人说把“玩家能见敌人”与“敌人能见玩家”做一个博弈恐怖游戏的潜入玩法就有了。引入行为树。Godot社区有BehaviorTree插件你可以在状态机基础上升级配置更多行为组合。接入强化学习。如果你的电脑能跑简单环境可以用Godot RL Agents扩展让AI吃强化学习的药。但我建议把这个作为第二项目因为学习成本高而且对于迷宫逃脱规则型AI已经足够有趣。最后再分享个小技巧调整AI手感时我总在项目目录里保留一个tuning.md文件记录每组参数对应的游戏感觉。纯靠记忆调参数两个小时以后你就忘了“视野330速度100”的感觉了。记下来之后再调你会有一种“调音师”的感觉。这个习惯救我很多次。
阅读完成 · 觉得有帮助?