Game Boy 自制游戏开发实战:从地图构建到碰撞检测 📅 发布时间:2026/9/7 13:29:03 👁 浏览次数: 如果你想在 Game Boy 上跑一款自己写的游戏《黑城堡 2》这类项目是最适合拆开看的实践案例。它的价值不在于画面有多炫而在于把一台只跑 8 位代码、屏幕只有 160x144、总内存以 KB 计算的掌机当成目标平台逼你把玩法、资源、代码和发布路径全部压缩到最小闭环里。如果你正在学游戏开发想脱离 PC 网页和手机 App 的舒适区或者纯粹想做一个能放进模拟器甚至烧录到实机卡带里的作品这篇文章会告诉你一条真实可走的路。我不打算把《黑城堡 2》包装成什么神作也不会给你画一张“从此靠在复古游戏圈出名”的大饼。我只按自己制作自制 Game Boy 游戏的实际流程把项目定位、工具链、最小可玩原型、资源限制、编译测试和排错思路完整讲一遍。你看完至少要能回答三个问题我的游戏在 GB 上能不能跑地图和角色应该怎么拆踩坑时先查哪里1. 先给《黑城堡 2》定一个能落地的范围1.1 它到底是动作游戏还是解谜游戏没有范围的项目十个有九个死在“想做的事太多”。自制 Game Boy 游戏尤其如此。GB 的机能摆在那里CPU 主频约 4.19 MHz画面分辨率 160x144一屏能同时显示的对象和 tile 都有限。你不可能在这里做无缝大地图加实时物理引擎加复杂粒子特效。《黑城堡 2》这个标题天然带两件事一是“黑城堡”说明场景要围绕城堡展开二是“2”说明它应该有前作留下的世界观或机制延续。但作为自制游戏最合理的落地方式偏动作解谜玩家控制一个小勇士在城堡房间之间穿行击败敌人、找到钥匙、打开门、触发机关、最终进入 BOSS 房间。我建议把“解谜”部分控制在开关门、移动石块、按顺序激活机关这个级别。不要想金币、装备、技能树、多武器切换、对话分支这些内容那是另一款游戏的体量。1.2 为什么先做单屏房间而不是长卷轴这是很多新手最容易搞错的地方。不少人一上来就想做“全城堡连续滚动地图”结果被地图拼接、镜头边界、敌人刷新和碰撞回退折磨到弃坑。GB 的性能约束下连续卷轴不是不行但需要处理的地图分块逻辑会成倍增加。更稳的起点是“房间制”。每个房间固定 160x144画面一次只显示一个区域。角色走到房间边缘的门或楼梯时切换整个房间数据。这样做有三个好处地图数据可以按房间独立存储单个房间尺寸小省 ROM 也省内存。碰撞判断只需要处理当前房间不会遇到“地图边缘 vs 镜头边缘”的复杂交叉。调试时一眼就能定位问题要么画面切了但数据没更新要么数据更新了但角色坐标没重设链路比连续卷轴短得多。对《黑城堡 2》来说我甚至建议早期版本只做 3 个房间一个入口大厅、一个敌人房间、一个 BOSS 房间。能把这 3 个房间跑通整个项目的骨架就立住了。之后再往里面加房。2. 准备工具链GB 自制项目的三类必备件2.1 ROM 编译工具GBDK、RGBDS、GB Studio 怎么选做 Game Boy 自制游戏工具链选型基本决定了你的开发体验。主流的三种选择是 GBDK、RGBDS 和 GB Studio它们的定位区别很大。GBDK 是目前最常用的 C 开发工具链。它允许你用类 C 语言写逻辑然后编译成 GB ROM。好处是对不熟悉汇编的开发者友好开发效率高坏处是 C 抽象会带来一些额外开销而且碰到底层问题时光看 C 不容易定位。如果你只想用一个月做到可玩原型GBDK 是性价比最高的选择。注意不要默认自己电脑上已经装好安装时先确认当前版本的依赖要求不同分支对主机系统有差异。RGBDS 是汇编语言工具链适合想彻底掌控每一个字节的人。它的优点是可玩性上限高能精细控制内存和时间缺点也一样明显写 500 行汇编可能只为处理一个菜单。除非你的目标就是练汇编或挑战极限否则我第一次做《黑城堡 2》时会绕过它。GB Studio 是可视化开发工具类似 RPG Maker 的思路。你不写代码而是通过界面放置事件、场景和对话。它适合做剧情驱动的简单 RPG但对动作战斗、复杂碰撞、自定义敌人 AI 这些需求会越做越别扭。如果你只是想做“一个城堡里跑来跑去开门开宝箱的演示”GB Studio 很轻松但你想做“有攻击判定、有敌人巡逻、有状态切换的动作解谜”GBDK 更合适。我自己的经验是把《黑城堡 2》当成学习项目时直接用 GBDK编译命令简单循环和数据结构的写法和 C 几乎一样社区示例也多。等你能熟练处理地图切换和碰撞了再决定要不要碰汇编。2.2 测试工具模拟器、真机烧录卡、调试输出和只做网页游戏不同Game Boy 自制项目不能“刷新浏览器”就完事。你至少需要三样东西模拟器。这是你 90% 时间使用的测试环境。选模拟器时不要只看能不能跑要看它有没有 tile viewer、内存查看器、断点调试和帧步进功能。你在开发中遇到的“画面花屏”“按键延迟”“精灵闪烁”很多都要靠这些工具才能看清。烧录卡 / flash cart。它能把编译出来的 ROM 文件写到实体卡带里插入真机运行。自制游戏领域常见的做法是买一块可复写烧录卡价格不一但功能逻辑都类似连接电脑、写入 ROM、插入 GB 或兼容掌机。调试日志。GB 上没有标准 stdout但你可以用模拟器的调试窗口输出信息或者把调试信息编码成屏幕上的特定颜色块。我的习惯是先把角色坐标、当前房间号、当前帧动作编号显示在画面上方一行不显眼的位置这样卡住时一眼就能读到状态。不要一上来就买设备。先把模拟器配合 GBDK 的示例工程跑起来确认工具链可用再决定要不要上实机。实机测试是发布前的最后一道门不是开发初期的必需品。3. 从地图块和角色碰撞开始最小可玩原型怎么做3.1 地图和碰撞从 8x8 到整屏 160x144Game Boy 的画面基础单位是 8x8 像素的 tile。一个 160x144 的屏幕横向 20 个 tile竖向 18 个 tile总共 360 个 tile。背景地图就是由这些 tile 编号拼接出来的二维数组。做《黑城堡 2》的时候我建议先把地图抽象成两层一层是视觉层负责显示什么图形另一层是碰撞层负责判断能不能走。视觉层用普通 tile 编号碰撞层用一个 20x18 的数组每个元素表示“空地、墙、门、岩浆、钥匙点”等状态。角色移动时不要拿角色贴图去和视觉层撞也不要想当然地从“地图图片颜色”判断而是直接读取碰撞层数组里对应格子的值。为什么这么做因为在 GB 上地图砖块和碰撞逻辑必须解耦。你可能想要一个看起来像柱子但实际可以穿过的装饰或者看起来像平地但走过去会触发陷阱的房间。如果碰撞和显示绑在一起做这些细节时会非常痛苦。地图数据要尽量压缩。20x18 的二维数组直接存一个房间 360 字节看起来不多但几十个房间加多方向翻版文件就会膨胀。常见的做法是把连续相同 tile 的横排做行程压缩或者直接用手写地图编辑器导成压缩格式。这一步等房间多了之后一定要做早期几个房间不必纠结。3.2 角色移动和按键映射GB 有方向键、A、B、Start、Select 一共 8 个主要输入。自制游戏里按键映射建议遵循直觉方向键控制移动。A 键负责上下文交互开门、推动石块、检查宝箱。B 键负责攻击或跳跃二选一。Start 键打开暂停菜单。Select 键预留可以用作切换物品或地图信息。移动逻辑不要做成“按下方向键直接改变角色像素坐标”而是“先判断是否满足移动条件再更新坐标”。对于房间制游戏我建议按 tile 锁定移动角色位置不是 0 到 159 的任意像素而是某个 tile 格内的偏移。这样碰撞判断简单动画也更稳定。如果你想让操控更顺滑可以做半格偏移但第一版不必。处理顺序很关键。每一帧的角色更新应该是读取输入 - 计算目标位置 - 检测目标位置的碰撞层 - 如果可通行则更新坐标 - 检测房间边缘的门事件 - 绘制角色。不要倒过来先画再判断碰撞会导致角色卡进墙里一帧再被弹出来视觉上就是抖动。3.3 一个最小可玩帧的逻辑顺序写代码前先画一下单帧逻辑。下面是一个只针对房间制动作解谜的伪代码示例具体语法取决于你选的语言while (game_running) { read_input(); if (player_attack_pressed) { spawn_attack_hitbox(); play_attack_sound(); } target_x player.x player.velocity_x; target_y player.y player.velocity_y; if (is_walkable(target_x, target_y)) { player.x target_x; player.y target_y; } check_door_trigger(player.x, player.y); check_item_pickup(player.x, player.y); update_enemies(); update_bullets(); draw_background_tiles(); draw_objects(); draw_player_sprite(); wait_for_vblank(); }这里真正的难点不是循环本身而是 update_enemies 和 update_bullets 的状态切换。敌人巡逻、敌人受伤、敌人死亡、玩家攻击、玩家无敌帧这些状态如果混在一起很快就会出现“角色明明站在墙边却被敌人打飞”的奇怪 bug。我的建议是给敌人和玩家都单独设计状态变量例如玩家有 idle、move、attack、hurt、dead 五种状态每帧必须先处理状态切换再计算结果。一个小技巧所有会移动的对象包括玩家、敌人、子弹都要有一个“速度”字段。不要直接写“角色每帧移动 3 像素”因为不同状态动画帧率不同直接写死容易出现“时快时慢”。速度字段配合统一的计时器来更新后续调试会轻松很多。4. 精灵、背景和音效在 160x144 画面上做取舍4.1 四个灰阶和双调色板Game Boy 没有彩色屏幕显示的是四种灰阶从最暗到最亮大约对应 0、1、2、3 四级。这看起来是很硬的限制但它也带来了一个好处你不必纠结色相只需要关注明度层次和可读性。实际做《黑城堡 2》的画面时我会把背景和精灵分开处理。GB 上有独立的背景调色板和精灵调色板每个调色板可以定义四色。也就是说背景画面最多同时出现灰阶 0 到 3精灵也最多同时出现灰阶 0 到 3但两套调色板可以不同配置。你要关心的第一件事不是美术风格而是阵营辨识度。城堡墙壁用中暗色地面用中亮色玩家角色精灵用“几乎全黑 最亮高光”敌人用“中灰”层次。这样玩家在低分辨率的画面上也能一眼区分“我能走的”“我不能走的”“我是谁”“谁会打我”。四个灰阶听起来简单实际规划时依然会翻车。最常见的问题是把背景调色板里所有颜色都排在灰阶 1 和 2导致地图看起来灰成一片。最好在开发早期就固定一套“灰到黑的场景层次表”边缘墙最深地面中等可交互物体最亮背景装饰只占 1 个灰阶。4.2 tile 和 sprite 的限制GB 的一个精灵sprite默认是 8x8 或 8x16 像素。一个屏幕上同时能显示的精灵数量有限而且同一行内也有严格限制。超限时你会看到角色突然消失半截或敌人闪烁。这不是随机 bug而是硬件限制。所以《黑城堡 2》里我并不会让每个敌人都是一个独立 sprite 就完事。相反我会提前统计一屏里最多可能出现多少个“需要独立活动”的对象玩家 1 个敌人最多 4 个飞来的子弹 2 个掉落的钥匙 1 个受击特效 1 个。如果远低于硬件上限那我可以放心把特效做成独立精灵如果已经接近或超出就要考虑把某些特效直接画在背景 tile 上或者用“共享精灵”的方式复用。背景层面的 tile 也是稀缺资源。GB 的显存里能同时保存的 tile 图案数量有限你不能让每个房间的所有图形都完美无缺地一次性加载。通用做法是分房间加载进入新房间时只把该房间用到的 tile 写入显存退出后切换到下一组。最开始做 3 个房间的规模时还能做成全量加载房间数一多就必须做分房 tile 管理。判断标准很简单如果某个房间的 tile 数超过了显存容量画面会随机出现乱码或重复图案这时你要做的不是加大显存而是精简 tile 或拆分房间。4.3 音效与音乐两条路都别指望复杂混音Game Boy 的声音是方波、三角波、噪声等合成声没有采样播放能力。你不可能在 GB 上直接丢入一段 MP3必须通过 tracker 软件写音乐数据再转换成 ROM 里的音效序列数据。《黑城堡 2》这种动作游戏音效至少需要覆盖几个场景开关键音、门打开音、宝箱开启音、怪物被打音、玩家受伤音。我的建议是先从简单的噪声音和短方波音做起不要追求完整 BGM。短音效很容易做出手感而一首完整的背景音乐在没有经验的情况下极容易变成“听不见的噪音背景”。你可以考虑用类似 tracker 思路的工具来排音轨但第一版我只推荐给玩家一个极简循环主旋律加一两条音效。如果一个音效的播放会让角色移动明显掉帧赶紧降低音效播放频率或者在攻击动画中延迟一拍再播放。声音在 GB 上不只是娱乐它会影响玩家对动作判定的感知所以“打击反馈音”比环境氛围音重要得多。5. 编译、模拟器测试和实机烧录验证5.1 编译流程和 ROM 大小检查用 GBDK 或 RGBDS 构建 ROM本质上就是把源代码和资源文件编译打包成一个 .gb 文件。这个流程不要当作“最后一步”而是在写完第一段代码时就建立起来。我的做法是维护一个 Makefile 或构建脚本至少包含编译 C 源码为目标文件。将图片转换工具导出的 tile 和地图数据链接进来。把音效数据写入 ROM。输出最终 .gb 文件。自动输出编译警告和 ROM 大小。ROM 大小是必须盯的指标。初学阶段不要追求“一定要塞进 32KB 卡带”之类的极限但至少要确认输出文件没有超出你的测试环境支持的上限。模拟器通常能直接运行各种 ROM 大小可到了实机烧录卡上超出范围可能无法写入或根本无法启动。每次编译后最好顺手记录 ROM 体积变化。如果上一次是 80KB改了几个房间后飙到 200KB你要意识到地图资源、tile 资源或代码段可能超了需要优先压缩数据。不要等发布前才发现。5.2 在模拟器上验证什么模拟器能跑通不代表真机没问题。但模拟器能帮你过滤掉 90% 的逻辑错误。在模拟器上我会按这个顺序验证启动验证。冷启动能不能进入标题画面或第一场景有没有直接白屏黑屏。输入验证。每个按键按下时角色是否有对应动作键盘是否冲突。碰撞验证。沿着墙走一遍看角色会不会卡死、穿墙、抖动。房间切换验证。每到门位置背景和碰撞层是否切换正确玩家出生点是否合理。敌人验证。敌人移动、攻击、受伤、死亡四个状态是否按预期切换。帧率验证。画面是否明显卡顿是否有对象闪烁。这里我最容易犯的错是“只在模拟器里按键跑两分钟就发布”。模拟器的稳定帧率掩盖了很多计时问题。你要特地打开帧步进逐帧看角色和敌人的位置变化确认每一帧里没有“瞬移”或“跳越”现象。5.3 实机烧录需要注意的事把 ROM 写到烧录卡再插到真机第一次能亮起画面那种感受和模拟器完全不一样。但实机测试不是“炫一下”而是暴露真问题的开始。可能遇到的差异包括刷新时序差异。模拟器画面刷新方式和真机硬件时序不完全一致某些特效在模拟器上正常实机上会出现撕裂或闪烁。感觉“好像不影响”也要重视因为玩家会注意到。按键扫描抖动。真机的按键存在弹跳和长按误触问题模拟器上不会体现。你需要确认长按方向键时角色不会反复抖动。烧录卡兼容性。不同烧录卡对 ROM 头字段、存档类型、SRAM 的支持不同。如果你的游戏用到存档功能一定要在实机上验证“退出后重新进入”是否保存成功。第一次上实机前别加新功能。只跑最小可玩原型重点看启动、移动、切房间、攻击判定这四个核心环节。实机上每发现一个问题回到模拟器里构造对应场景修完后再烧录验证。6. 常见问题与排查顺序6.1 花屏、乱码、崩溃怎么查“花屏”这个词在自制 GB 项目里非常宽泛。可能是地图 tile 加载不全可能是调色板数据写错也可能是内存越界写到显存区域。我的排查顺序是先看现象出现在什么时机。是固定某个房间花还是切换房间后花还是角色移动到特定位置才花。再看 tile viewer。模拟器自带视图里当前显存中实际存放的 tile 图案是什么。如果 tile 图案本身是乱的问题在资源加载如果 tile 图正常但屏幕显示乱问题在地图数据或 VRAM 写入。最后查地图编码。手动把一个房间的碰撞层和视觉层分别打印出来看看数组边界是否正确。数组越界在 C 里不会总是立刻奔溃经常会表现为“屏幕某块区域出现不属于当前房间的图案”。不要一看到花屏就把锅丢给工具链。自制项目很多花屏都来自资源索引写错比如地图文件里写了一个超出 tile 总量的编号或者角色精灵图片处理时把空字节算进了有效数据。6.2 按键失效和角色穿墙按键失效先不要改解析代码。先确认你是“物理按键没触发”还是“触发了但逻辑没反应”。方法是用模拟器的调试输出把每次按键读取到的状态打印出来。如果打印显示按键已经被读取但角色没动那就是状态机里的移动条件不满足比如当前玩家处于 attack 状态时不响应移动输入。这个设计本身没问题但如果 attack 状态没有正确结束就会出现典型的“方向键按了半天角色站在原地”的情况。角色穿墙的原因通常不是“碰撞代码写得少”而是碰撞检测用的坐标和绘制用的坐标不一致。假如绘制角色时用的是 float 坐标碰撞检测时把 float 转成 int 并向下取整偏了一两像素角色就可能在紧贴墙壁时卡进墙缝。解决方法是统一坐标类型和取整规则并且碰撞检测的目标位置要考虑角色尺寸不能只检测角色原点。GB 上我建议始终用整数像素坐标帧内只做整数运算省内存也避免这种“看得见但撞不上”的怪问题。6.3 卡在房间切换和 ROM 超载房间切换卡死是常见问题。现象多半是走到门位置时“会暗一下”然后永久黑屏或卡在相同画面。这是没有正确装载新房间资源导致的。排查顺序从数据流去看玩家进入门事件 - 程序读取新房间 ID - 清理旧 tile - 写入新 tile - 重建碰撞层 - 设定玩家出生坐标 - 渲染背景。任何一环遗漏屏幕上就可能一直显示旧地图或者背景全空。我的建议是新房间加载函数里加一个调试标志位每完成一步就改变屏幕上某个固定位置的颜色卡在哪个颜色就知道是哪一步没做。ROM 超载是另一个问题。如果你的构建脚本开始报地址越界或提示无法分配空间先不要急着写更短的代码。最优先做的事是检查资源数据里有没有重复内容。同一个门 tile 在不同房间重复存了很多份或者音效数据被链接两次这类冗余比代码体积更容易顶爆 ROM。使用资源检查工具把输入文件大小列出来通常一眼就能看到哪个文件异常大。6.4 排查顺序清单自制 GB 游戏的问题排查我按这个顺序走能解决绝大多数问题看现象报错、花屏、卡屏、按键无反应、速度异常先记录复现方式。看输入文件路径是否正确图片格式是否符合工具要求地图数据是否越界。看环境工具链版本和系统是否匹配模拟器配置是否限制内存或显示。看资源tile 是否重复地图索引是否指向正确答案精灵数量是否超限。看逻辑状态机是否卡死碰撞检测坐标是否统一房间加载步骤是否完整。最后才怀疑工具本身先查社区文档确认你用的版本是否有已知限制。这套顺序能避免你在“地图编码错误”时去重写整段 AI 代码也能避免在“模拟器不支持某项特性”时把自己折磨到凌晨三点。7. 一个更稳的开发路线建议如果你现在正打算动手做《黑城堡 2》或类似的自制 GBA 项目我建议按下面这条路线走。第一周不写玩法。先把工具链跑通编译一个显示“HELLO”或者简单移动方块的最小 ROM确认模拟器能运行、能调试、能看到循环日志。这个阶段看起来不酷但它决定了整个项目是否可推进。第二周到第三周做单人房间。一个地图一个角色一个敌人。不需要敌人有多智能只要它会移动并且在碰到玩家时造成伤害。核心目标是验证“碰撞层 - 角色状态 - 绘制”这个链路没有断裂。第四周做房间切换。两个房间之间有门走进去房间数据能正常加载玩家位置正确不会闪退。这个阶段你要开始规划地图资源的分房管理。之后才逐步加内容更多敌人、交互物件、音效、菜单、存档。每加一个功能先在模拟器上跑稳定再考虑实机验证。不要一口气把“完整版设想”倒进第一个可运行版本否则你会分不清哪里是全新 bug哪里是上一版遗留问题。如果只是学习模拟器加 GBDK 默认配置就够用。如果你想把这个作品放到实机上长期玩那就尽早买一块烧录卡每完成一个里程碑就实机测一次。实机第一次跑通的感觉比模拟器上跑一百次都有说服力也会让你对“为什么必须理解硬件限制”有更真实的体会。《黑城堡 2》这类项目的终点不是做一个多复杂的游戏而是完整地回答一遍“在严苛平台上做出一个小游戏”的全过程。等你走完一遍再回头看网页游戏或手机游戏很多“为什么卡顿”“为什么占内存”“为什么资源要压缩”的问题你会有完全不同的理解。那时候你做的不只是《黑城堡 2》的续作而是更成熟的技术判断力。