Godot 4.x平台跳跃游戏开发:从TileMap到敌人AI完整实战 📅 发布时间:2026/9/1 14:38:33 👁 浏览次数: 很多第一次用 Godot 做游戏的同学卡住的位置往往不是“不会写代码”而是瓦片地图、角色物理、敌人 AI 这三块没有一个统一的落地流程。这三块单独看都不难放到同一个项目里就会互相牵扯角色和地面碰撞没对齐、瓦片画完了发现没有碰撞体、敌人写好了却只会原地发呆。这篇文章会以“2026 新版教程”的标准工作流带你从零搭出一个可运行的平台跳跃 Demo核心覆盖三件事TileMap 瓦片地图、敌人 AI、以及两小时内的实战交付路径。先说结论这是一个完全面向本地初学者的 Godot 2D 项目不需要高端显卡不需要额外美术素材不需要把 GDScript 学得很深。你需要准备的只有一台能跑 Godot 4.x 的电脑以及两个小时的连续时间。文章会给出完整的节点结构、GDScript 脚本、瓦片碰撞配置步骤、敌人巡逻与追击的写法、常见报错排查表最后再补一份用于跑通的验证清单。这篇文章适合正在学 GDScript、想做第一个完整小游戏、或者想从单一示例里把“角色 地图 AI”串起来的读者。如果你已经有 Unity 的 2D 基础切换到 Godot 的 TileMapLayer 工作流也可以把这篇文章当作快速对照手册。1. 核心能力速览能力项说明项目定位2D 平台跳跃小游戏完整开发流程核心玩法移动 / 跳跃 / 重力、瓦片地图关卡、敌人巡逻与追击、重生与终点开发工具Godot 4.x 官方编辑器开发语言GDScript核心节点CharacterBody2D、TileMapLayer、Camera2D、Area2D、RayCast2D运行平台Windows / macOS / Linux 本机运行可导出到 HTML5、Android、iOS硬件要求2D 游戏场景对显卡压力很低集显即可流畅运行是否需要美术不需要可以使用占位色块和基础图集前置要求了解 GDScript 基础语法例如变量、函数、信号学习产出可运行的平台跳跃 Demo以及可复用的工程模板完成时间按 2 小时实战节奏安排可以在时间内完成到可玩状态这套能力组合和微信小游戏里常见的“跑酷 平台跳跃”玩法很像但更侧重开发层面的可调试性。Godot 的 2D 工作流把瓦片编辑、物理碰撞、状态机拆分都做在编辑器内部不需要额外插件这也是很多初学者选择它做第一个游戏引擎的原因。2. 适用场景与使用边界平台跳跃是 2D 游戏里最适合做“第一个 Demo”的品类因为它把输入、物理、地图编辑、敌人逻辑全部覆盖到了而且每一种都有非常直观的验证方式。你做完之后不仅是拿到了一个跳得动的角色还学会了一套通用的“角色控制 场景搭建 AI 状态切换”的工程结构后面做俯视角 RPG、Roguelike、弹幕游戏都会沿用这份经验。这个项目适合的人群非常明确正在学 Godot 但只写过“控制台打印”级别代码的初学者做过小项目但没系统整理过 TileMap、角色、敌人之间协作关系的开发者想快速产出游戏原型、用来验证关卡手感或比赛 Demo 的学生和个人开发者从 Unity 转 Godot想对照 2D 组件体系的旧 2D 开发者。它不适合的场景是你已经需要商业级美术表现、复杂角色动画状态机、物理水效果、多人大厅同步。这些不是平台跳跃教程的范围强行塞进来会把 2 小时实战拖成 2 个月的工程。我们的边界就是先把一个可以玩、可以死、可以赢的闭环跑通。需要特别提醒的是教程里使用的所有美术素材无论是色块还是图集都建议使用你自己绘制、引擎自带占位素材、或者明确标注了开源许可的素材。不要直接使用游戏、动画、商业素材包中的图片和音效去做公开分发的项目。敌人 AI 部分如果涉及“检测玩家位置”“追踪目标”本质上是一种游戏逻辑设计不涉及真实人脸、声音或用户隐私但如果你的玩家角色来自网络抓取素材仍然要确认授权。游戏导出后如果放到公网或应用商店必须确认角色形象、背景素材、音效的版权合规这一点没有例外。3. 环境准备与编辑器设置3.1 安装 Godot 官方版本Godot 是一个开源引擎直接去官方下载标准版和 .NET 版都可以。本教程使用 GDScript不需要 .NET 版下载标准版即可。安装非常简单Windows 下解压 zip 后直接运行 exemacOS 下把 app 拖到 ApplicationsLinux 下大多数发行版可以直接运行二进制文件。不需要额外安装 Python 或 Node 运行时Godot 自带了完整的 GDScript 和导出工具链。环境项要求操作系统Windows 10 / 11、macOS 12、主流 Linux 发行版Godot 版本建议 4.3 及以上本文工作流使用 TileMapLayer 节点内存8GB 以上4GB 也可以运行但编辑器切场景会卡磁盘空间项目本身很小编辑器解压后约 1GB 左右显卡集显即可不需要独立显卡3.2 创建项目与推荐目录结构打开 Godot点击“新建项目”选择空模板目录名建议全英文例如platformer_demo。项目创建后先建好目录结构避免中途素材乱放platformer_demo/ ├── scenes/ ├── scripts/ ├── assets/ │ ├── sprites/ │ ├── tilesets/ │ └── audio/ └── project.godot目录不一定非要照抄但 scenes、scripts、assets 这三层是推荐保留的。Godot 的导入器会记录每个资源的导入配置目录一旦改路径import 文件也要跟着迁移所以前期规划好能省很多麻烦。3.3 版本选择建议标题里的“2026 新版教程”核心依据是 Godot 4.x 自 4.3 开始把原来的 TileMap 节点重构为 TileMapLayer并持续改进了瓦片集编辑体验。如果你打开新版本发现某些面板名字和截图不一样优先检查两件事一是节点类型是 TileMap 还是 TileMapLayer二是导入设置里是否勾选了正确的 2D 纹理预设。我建议直接用当前官网最新稳定版而不是 beta 版本。教程示例都是基于稳定 API 写的换 beta 版容易遇到编辑器插件和脚本 API 变化。如果你已经有旧项目想迁移到新版建议按官方迁移文档逐条处理节点名和属性名的变化老版本的 TileMap 工程不能直接升到 4.3节点名称需要手动调整。4. 平台跳跃角色控制器实战4.1 输入映射配置进入“项目设置 - 输入映射”添加三个动作move_left、move_right、jump。注意Godot 自带了一些ui_left/ui_right/ui_accept动作但项目实际建议定义自己的动作名方便后续做按键自定义和手柄支持。移动动作建议同时绑键盘方向键和 WASD跳跃建议绑空格键。如果使用手柄再加手柄左摇杆 X 轴和 A 键。这里给出一段project.godot里的输入映射示例实际操作时也可以在编辑器面板里添加[input] move_left{ deadzone: 0.5, events: [Object(InputEventKey,physical_keycode:65)] } move_right{ deadzone: 0.5, events: [Object(InputEventKey,physical_keycode:68)] } jump{ deadzone: 0.5, events: [Object(InputEventKey,physical_keycode:32)] }这段配置是一个参考格式键位码需要按你本机的 Godot 版本和按键布局确认。更稳妥的方式是在项目设置的可视化界面里点“添加”并按下按键让编辑器生成键位映射避免手动写physical_keycode出错。手柄输入则直接选择“设备”、“按钮”并按下对应按键即可。4.2 角色场景搭建创建一个Player.tscn根节点类型选CharacterBody2D。下面是推荐的子节点结构Player (CharacterBody2D) ├── CollisionShape2D (CapuleShape2D 或 RectangleShape2D) ├── Sprite2D 或 AnimatedSprite2D └── Camera2DCollisionShape2D 是必须的否则角色不会和地面碰撞。Sprite2D 在测试阶段可以直接用引擎自带的icon.svg或者画一个简单的矩形色块。Camera2D 作为角色子节点时把它的Enabled属性设为 true这样角色移动时镜头会自动跟随。4.3 移动与跳跃脚本给根节点挂上Player.gd代码如下extends CharacterBody2D export var speed: float 220.0 export var jump_velocity: float -380.0 export var gravity: float 980.0 func _physics_process(delta: float) - void: # 重力 if not is_on_floor(): velocity.y gravity * delta # 水平输入 var direction : Input.get_axis(move_left, move_right) if direction ! 0.0: velocity.x direction * speed else: velocity.x move_toward(velocity.x, 0.0, speed) # 跳跃 if Input.is_action_just_pressed(jump) and is_on_floor(): velocity.y jump_velocity move_and_slide()几个关键点is_on_floor()依赖move_and_slide()的计算结果所以跳跃判断放在移动之后。jump_velocity是负值因为 2D 世界里 y 轴正方向朝下。move_toward会让角色停止输入时平滑减速而不是瞬间停住手感更接近传统平台跳跃游戏。如果角色在斜坡上跑动出现抖动可以把 Motion Mode 调为Floating或者给斜坡增加单独的物理层处理初学阶段先不做斜坡平地验证即可。4.4 验证角色控制按下 F5 运行场景如果看到角色可以左右移动、跳跃、落地第一步就通过了。这里有一个很容易忽略的细节场景里必须有带碰撞体的地面玩家才能在按下空格时触发is_on_floor()。如果直接运行后发现角色一直掉落先检查地面是否挂了StaticBody2D或TileMapLayer的物理层碰撞。5. 瓦片地图实战5.1 什么是游戏瓦片地图游戏里的瓦片地图和很多人搜索过的“高德地图瓦片”是同一个思想的不同分支把大世界拆成固定大小的格子用一小批瓦片素材拼接出完整关卡。Godot 的 TileMapLayer 就是干这件事的节点它把大量独立的方块合并成一张网格由引擎统一渲染性能和内存开销都比逐个用StaticBody2D拼地面低得多。瓦片地图在平台跳跃里承担三件事地面与墙壁的物理碰撞、场景视觉表现、以及后续关卡设计的底图。学会了它你也就掌握了 Godot 做弹幕游戏、俯视角 RPG、Roguelike 地图生成的底层手段因为这些游戏的场景同样可以拆成格子。5.2 创建 TileSet 与瓦片素材导入先准备一张瓦片图集最简单的方式是用代码生成一张纯色格子图或者找一张明确开源许可的 16x16 图集。图片导入 Godot 后右键选择“作为 TileSet 新建资源”或者创建 TileMapLayer 后在 Inspector 里新建 TileSet。核心参数如下参数推荐值说明Tile Size16 x 16 或 32 x 32决定瓦片在关卡里的物理尺寸Texture图集图片导入时预设选择 2D 纹理Physics Layer开启一个给瓦片配置碰撞形状Collision Shape矩形覆盖整个瓦片地面标准做法在 TileSet 编辑器的“图集”选项卡里把瓦片分割好然后为需要碰撞的瓦片绘制物理形状。这里最常见的错误是只看渲染不做碰撞导致角色直接穿地板。5.3 用 TileMapLayer 拼关卡场景里新建TileMapLayer把刚才创建的 TileSet 分配给节点。编辑器上方会出现瓦片绘制工具栏支持以下操作铅笔工具一笔刷一个瓦片填充工具快速铺满整个区域矩形工具一次性画一个矩形地面橡皮擦删除瓦片。绘制时建议先画地面层再画平台层最后补充装饰。装饰瓦片不需要开物理层避免影响角色移动。如果你需要在代码里动态生成瓦片示例代码如下onready var tile_map: TileMapLayer $TileMapLayer var source_id : 0 var atlas_coords : Vector2i(1, 0) # 图集里第 1 行第 0 列的格子 func _ready() - void: tile_map.set_cell(Vector2i(5, 3), source_id, atlas_coords)set_cell是 4.3 TileMapLayer 的写法。如果是老版本 TileMap方法名是set_cell(cell, source_id, atlas_coords)参数顺序稍有不同使用前先查看你当前版本的 API 文档。5.4 瓦片物理层与瓦片集给瓦片添加碰撞时检查顺序是TileSet 里是否创建了 Physics Layer - 瓦片是否勾选了该层的碰撞形状 - 碰撞形状是否覆盖了瓦片有效像素。很多新手把碰撞形状画得很小只覆盖瓦片中心导致角色边缘悬空跑动时会有轻微卡顿。一个平台跳跃细节值得重视地面瓦片和墙壁瓦片建议使用不同的物理材质。地面瓦片的摩擦系数可以高一点墙壁如果希望角色能够贴墙下滑可以单独设置。Godot 的物理材质直接在 TileSet 的瓦片物理层属性里配置不需要额外脚本。绘制完成后把地图左下角对齐到世界的 (0,0) 附近方便后面设置镜头边界。5.5 摄像机跟随与镜头边界角色开了 Camera2D 后默认会完全跟随但平台跳跃关卡通常希望镜头不超出关卡边界。设置方法是在 Camera2D 的属性里打开Limit Smoothed并把Limit Left / Right / Top / Bottom四个值按关卡范围填写。也可以让镜头稍微提前于角色移动方向提升手感extends Camera2D export var look_ahead: float 100.0 onready var player: CharacterBody2D $../Player func _process(_delta: float) - void: if player null: return var direction : Input.get_axis(move_left, move_right) position.x move_toward(position.x, direction * look_ahead, 8.0)这个脚本放在 Camera2D 节点上会在角色前进方向做一个轻微偏移。注意position是相对于父节点 Player 的所以这里的偏移量不会改变全局坐标只影响镜头视角。6. 敌人 AI 实战6.1 敌人状态划分平台跳跃里的敌人不需要完整的机器学习只需要一个最简单的状态机巡逻、追击、切换回巡逻。用一个枚举变量保存当前状态用match分支处理不同逻辑这是 Godot 里最直观的 AI 写法。创建Enemy.tscn根节点CharacterBody2D子节点如下Enemy (CharacterBody2D) ├── CollisionShape2D ├── Sprite2D ├── DetectionArea (Area2D) │ └── CollisionShape2D └── FloorRay (RayCast2D)DetectionArea 用于检测玩家是否进入范围FloorRay 用于检测脚下的平台边缘。6.2 巡逻逻辑敌人先在给定范围内来回走。范围用两个数值记录起始 x 坐标和允许偏移的距离。代码如下extends CharacterBody2D enum State { PATROL, CHASE } export var patrol_speed: float 80.0 export var chase_speed: float 160.0 export var walk_range: float 2.0 export var gravity: float 980.0 var state: State State.PATROL var direction: float 1.0 var patrol_origin_x: float 0.0 onready var player: CharacterBody2D get_node(../Player) onready var detection_area: Area2D $DetectionArea onready var floor_ray: RayCast2D $FloorRay func _ready() - void: patrol_origin_x global_position.x floor_ray.target_position Vector2(direction * 16.0, 24.0) detection_area.body_entered.connect(_on_detection_area_body_entered) detection_area.body_exited.connect(_on_detection_area_body_exited) func _physics_process(delta: float) - void: if not is_on_floor(): velocity.y gravity * delta match state: State.PATROL: velocity.x direction * patrol_speed _check_patrol_bounds() State.CHASE: var diff : player.global_position.x - global_position.x direction sign(diff) velocity.x direction * chase_speed move_and_slide()6.3 玩家检测DetectionArea 连接的信号有两个body_entered和body_exited。当玩家进入检测区域敌人从巡逻状态切到追击玩家离开后切换回巡逻并回到自己的巡逻区间。注意这里的body需要判断是不是玩家避免敌人被其他敌人或子弹触发。func _on_detection_area_body_entered(body: Node2D) - void: if body.is_in_group(player): state State.CHASE func _on_detection_area_body_exited(body: Node2D) - void: if body.is_in_group(player): state State.PATROL为了让判断更可靠记得在 Player 的根节点里把它加入player组。组是 Godot 里非常轻量的分类机制比用if body.name Player这种方式更安全因为你可以放心改场景名而不影响逻辑。6.4 追击逻辑与平台边缘检测追击的核心是一行代码根据玩家和敌人的 x 坐标差值取符号得到移动方向。如果玩家在右侧sign(diff)为 1在左侧为 -1。然后把方向乘上追击速度。平台边缘检测需要处理一个问题敌人追到平台尽头会直接掉下去。解决思路是巡逻时用 RayCast2D 向下检测如果前方没有地面就掉头。可以扩展一下脚本func _check_patrol_bounds() - void: if global_position.x patrol_origin_x - walk_range: direction 1.0 _update_ray_direction() elif global_position.x patrol_origin_x walk_range: direction -1.0 _update_ray_direction() if not floor_ray.is_colliding(): direction * -1.0 _update_ray_direction() func _update_ray_direction() - void: floor_ray.target_position Vector2(direction * 16.0, 24.0)RayCast2D 的target_position是相对本节点的坐标所以方向变了以后要同步更新。这里24.0是向下的检测距离数值需要根据角色大小调整。如果检测距离太小平台边缘判断会失效如果太大可能会把下面一层的地面也算成当前平台让敌人不会在悬崖边回头。6.5 敌人与玩家碰撞敌人和玩家都是 CharacterBody2D物理层上会直接相撞。平台跳跃的常见处理是玩家从上方踩到敌人敌人死亡玩家弹跳玩家从侧面撞到敌人玩家受伤。最简单的方式是用两组 Area2D 分开处理。在敌人身上再加一个HitBox命中的是player组碰撞后进入死亡逻辑同时在玩家身上加一个AttackArea检测到敌人时执行弹跳和击杀。这里先不展开全部代码核心思路是不要让“死亡判定”和“伤害判定”共用一个碰撞体否则踩敌人和撞敌人会同时触发。7. 两小时实战节奏安排7.1 0-20 分钟项目与角色前 20 分钟只做一件事让角色动起来。创建项目、配置输入、搭建 Player 场景、挂上移动跳跃脚本、铺一块临时地面。这个过程不建议纠结美术一个色块就够。角色能走能跳项目就已经进入可调试状态。7.2 20-50 分钟瓦片地图从第 20 分钟开始进入瓦片地图。创建 TileSet、导入图集、配置物理碰撞、用 TileMapLayer 画出第一关的雏形。先画一条从起点到终点的轴线再在轴线上加高低平台和坑。镜头边界在这个阶段顺便设置好。7.3 50-90 分钟敌人 AI第 50 分钟开始写敌人。先做巡逻再挂 DetectionArea 做追击最后处理边缘检测。测试标准是敌人能在平台区间内来回走玩家进入范围后敌人加速追过来玩家离开范围后敌人回到巡逻。7.4 90-120 分钟收尾与导出最后半小时做三件事加掉坑重生的逻辑加到达终点的判定再导出一次到 HTML5 或本机 exe。很多人会低估导出这一步实际上第一次导出经常会遇到素材丢失、字体缺失、图标不合法等问题提前留出时间非常必要。8. 调试与性能观察8.1 常用调试工具Godot 编辑器右下角的“调试”菜单里有几个非常实用的面板调试工具作用远程场景树运行时实时查看节点状态帧分析器查看每个函数的耗时性能监视器查看 FPS、对象数、绘制调用可视碰撞形状运行时显示碰撞体和射线可视导航显示导航网格和路径敌人 AI 出问题时第一件事是打开可视碰撞形状确认 DetectionArea 的范围和 RayCast2D 的射线方向。很多 AI 异常不是逻辑写错了而是碰撞区域没有覆盖到位。8.2 帧率与绘制调用2D 游戏卡顿最常见原因是绘制调用过多。TileMapLayer 的优势在于会把一整层瓦片合成一次绘制但如果你在地图上放了几百个独立的 Sprite2D性能就会明显下降。角色和敌人数量少时不会感觉明显但关卡大了以后建议这样做把背景装饰合并成一张大图或者用 TileMapLayer 的第二层来画同一个材质、同一个图集的敌人尽量共用纹理图集减少不必要的阴影、光效、屏幕特效。8.3 显存与内存观察Godot 2D 项目对显存占用非常低一般不会成为瓶颈。更值得关注的是纹理占用的内存如果导入一张 4K 分辨率的大图放在 UI 或背景里即使画面看不到细节GPU 也会按完整分辨率加载。检查方法是选中资源看导入设置的“纹理大小”和“压缩模式”不需要高清的场景建议把纹理压缩开成Lossless或VRAM Compressed。这个部分每个人机器不同实际占用水平需要在你自己的项目里打开性能监视器确认。9. 常见问题与排查方法问题现象可能原因排查方式解决方案角色从地面上直接掉下去TileMapLayer 没有配置物理层或瓦片没画碰撞打开可视碰撞形状检查瓦片物理形状在 TileSet 里为瓦片添加 Physics Layer 和碰撞形状按跳跃键没反应输入映射没添加或角色不在 floor 状态检查 Input Map 和is_on_floor()调试输出添加动作名称确认地面有碰撞体角色跑起来很滑速度变化过于平滑或地面摩擦太小看velocity.x的收敛过程调整move_toward的最大减速值或增加地面摩擦敌人不会巡逻状态机没有进入 PATROL或walk_range为零打印当前 state 和 position初始化patrol_origin_x确认 range 大于 0敌人卡在墙角敌人撞墙后方向没有翻转查看is_on_wall()状态增加撞墙掉头逻辑镜头不跟随或超出关卡Camera2D 没有设为当前或边界没设置检查 Camera2D 的 Enabled 和 Limit 属性勾选 Enabled设置 Limit Left/Right/Top/Bottom导出后图片变成空白资源导入设置或路径问题检查导出日志和资源引用重新导入资源确认路径中没有中文名和特殊字符项目报错“Cannot call method set_cell on null”脚本里$TileMapLayer路径写错检查节点路径用onready和正确的节点名TileSet 编辑面板空白没有在 Inspector 里创建 TileSet 资源检查 TileMapLayer 的 TileSet 属性新建 TileSet 并分配图集排查的第一步永远是看 Debugger 输出。Godot 的报错信息会直接给出脚本文件名、行号和具体原因比盲目改代码快很多。如果报错信息看不懂把关键词复制到引擎文档里搜大多数情况下官方文档都有对应的 FAQ 章节。10. 最佳实践与使用建议第一次做平台跳跃项目不需要追求大而全。先跑通最小闭环再逐步加功能。下面的建议能直接减少后续维护成本角色、敌人、瓦片地图、UI 放在不同场景里用实例化方式加载。不要把整关都堆在一个场景里。用组件化节点组织逻辑。玩家的输入处理、移动逻辑、动画切换、伤害判定拆成独立脚本挂了对应节点上后面改一个功能不会牵连其他部分。使用分组代替字符串比较。判断玩家、敌人、子弹时用is_in_group(player)比用节点名更稳定。碰撞层要有规划。地面、敌人、玩家、触发器分到不同物理层避免所有东西都互相碰撞。给 TileMapLayer 留一个“装饰层”。装饰层不参与物理画花花草草不会影响角色移动也不会增加碰撞计算量。关卡里所有关键位置使用 Marker2D 标记。出生点、敌人巡逻点、终点检查点都做成节点而不是在代码里写死坐标方便后续调整。使用绝对路径时要谨慎。尽量用onready和相对路径避免场景结构变动后引用失效。批量测试时不要直接用鼠标频繁重开场景。用命令行运行指定场景例如godot --path . res://scenes/Level.tscn脚本改完可以快速看运行效果。如果你的项目后续要发布到公开平台还有几个工程化事项需要提前处理。第一导出前检查所有素材授权第二项目设置里选择合适的图标和启动画面避免使用默认图标第三如果担心脚本被反编译Godot 导出面板里提供脚本加密选项但 Demo 阶段建议先关闭方便自己调试和定位问题。11. 总结与下一步这个项目最值得尝试的点是它用两个小时的工程量覆盖了游戏开发里最常遇到的三个模块角色物理控制、基于瓦片地图的关卡设计、带状态切换的敌人 AI。完成之后你拥有的不是一个只能截图的静态页面而是一个可以反复调整手感和关卡结构的可玩闭环。最先应该验证的功能是角色的移动跳跃手感。把speed、jump_velocity、gravity三个参数反复调几轮理解它们如何影响跳跃高度和落点这是平台跳跃的核心体验。最容易踩的坑是瓦片碰撞配置这一步出问题会让前面所有角色控制脚本看起来都是坏的所以调试时记得先打开可视碰撞形状确认物理层。后续可以继续扩展的方向很多给敌人增加巡逻路径点、加入多段跳或二段跳、设计带检查点的关卡重开流程、导出到 Web 平台做联机对战原型。你已经会了瓦片地图和 AI 状态机后面的扩展基本都是在这两个基础上加新的状态和节点。先把 Player.gd 和敌人巡逻脚本跑通再把 TileMapLayer 从纯地面扩展到完整关卡。剩下的问题会在调试过程中越来越清楚。