乐于分享
好东西不私藏

三个后端 + AI + 三周 = 一款上线的像素 H5 游戏

三个后端 + AI + 三周 = 一款上线的像素 H5 游戏
一、先看我们的成果

在正式开始之前,先看看我们的游戏效果

这是一款像素风的 H5 水族箱养成游戏。玩家可以养鱼、喂食、繁殖、钓鱼、潜水、收集图鉴、装饰水族箱,体验四季天气和昼夜循环。技术栈是 Phaser 4 + React 19 + Vite 8,前端代码近 5 万行,后端约 7000 行,完整的客户端-服务端架构。

接下来说一个可能让你觉得反直觉的事实:

做这个项目的三个人,全是后端工程师。我们没有一个人有前端开发经验。

前端近 5 万行代码中,AI 生成占比超过 95%。从游戏框架选型、像素美术资产、UI 组件到动画效果,基本都是靠 AI 交付的。

核心工具是 内部编码 Copilot(底层模型 Claude Opus 4.6),辅以内部 AI 生图平台 公司内部AI设计平台 以及外网像素动图生成平台 PixelLab。

这篇文章不是来科普游戏开发的。我们想分享的是:作为后端工程师,我们是怎么利用 AI 跨过前端和美术这两道墙的。

二、为什么我们敢接这个活

后端工程师做前端不是什么新鲜事,但前端 ≠ 写几个页面。我们面对的是:

  • 游戏框架:Phaser 4,一个完整的 2D 游戏引擎,Scene 管理、粒子系统、Tween 动画……我们从零开始

  • 像素美术:16 种鱼的多阶段精灵动画、装饰物、季节岛屿贴图——没有美术资源外包,全靠 AI 生成

  • 复杂的游戏系统:养成 + 繁殖 + 经济 + 钓鱼 + 潜水 + 图鉴 + 成就 + 季节 + 昼夜,彼此联动

  • UI 框架:React 19,弹窗、商店、图鉴、设置、新手引导等全套界面

  • 国际化:自建 i18n,中日双语

  • 客户端-服务端架构:后端数据持久化、防作弊、配置下发——这个游戏后续会作为电商平台的组件上线,不能只靠 localStorage

  • 音效系统:全部通过 Web Audio API 程序化合成,包括 BGM——没有一个外部音频文件

以往要做这样一个项目,一个后端如果想自学前端从零开始,大概需要半年的学习周期。而这次,AI 改变了"能做什么"的边界——我们的角色从"写代码"变成了"定义需求、设计架构、验收产出"。AI 成了我们临时招进来的前端工程师 + 美术设计师。

三、四个最难的跨界场景

下面是我们遇到的四个最难的技术挑战。每一个放在以前,都足以让一个后端工程师放弃。

3.1 架构层:Phaser 和 React 怎么协作?

项目从第一天就面临一个架构决策:游戏画布和 UI 界面用什么方式组织?

如果全用 Phaser 做 UI,开发效率低(Phaser 不是为复杂表单和列表设计的)。如果全用 React 做游戏,性能扛不住(DOM 不适合逐帧渲染像素动画)。

最终方案是分层:React 管 UI,Phaser 管游戏画布,两者通过 EventBridge 事件系统通信。

这个架构对后端工程师来说其实非常亲切——它本质就是服务拆分 + 事件驱动。React 和 Phaser 就是两个微服务,EventBridge 就是消息队列。我们不需要理解 React 的 fiber reconciliation 原理,也不需要懂 Phaser 的 Scene 生命周期全貌,只需要定义好事件协议,然后让 AI 分别去填充两侧的实现。

看一下 EventBridge 的实际事件定义(简化):

// bridge/EventBridge.tsexport const Events = {  // Phaser → React(状态同步)  COINS_CHANGED'coins-changed',  FISH_COUNT_CHANGED'fish-count-changed',  SPECIES_ICONS_READY'species-icons-ready',  FISHING_CATCH_RESULT'fishing-catch-result',  CHALLENGE_STATE_CHANGED'challenge-state-changed',  // React → Phaser(用户操作)  BUY_FISH'buy-fish',  DROP_FOOD'drop-food',  UPGRADE_TANK'upgrade-tank',  UPGRADE_BOAT'upgrade-boat',  SELECT_THEME'select-theme',  REQUEST_SCREENSHOT'request-screenshot',  // Dev Panel(开发调试)  DEV_SET_COINS'dev-set-coins',  DEV_ADD_FISH'dev-add-fish',  DEV_SET_ALL_FISH_STATS'dev-set-all-fish-stats',  // ...60+ 个事件};

本质上跟后端定义 RPC 接口没什么区别。

AI 在这里的角色:当我们把"React 管 UI、Phaser 管画布、事件桥通信"这个架构意图表达清楚后,让 AI 去实现具体的事件定义、React 的状态订阅、Phaser 的事件监听。后端工程师做的是 review 解耦是否干净、依赖方向是否单向——这些恰好是后端最擅长的判断。

3.2 像素美术:我们不会画图,怎么办?

这是整个项目中最"跨界"的部分。三个后端工程师,没人会画画,但游戏需要 16 种鱼的多阶段动画精灵、装饰物、季节岛屿、UI 图标等大量视觉资产。

我们的方案是:用 AI 生图 + AI 写代码,把美术工作变成一条可复现的流水线。

鱼的动画精灵:AI设计生图平台 → PixelLab → Sprite Sheet

鱼是游戏里最核心的视觉元素。每种鱼有 3 个成长阶段(鱼苗 / 幼鱼 / 成鱼),每个阶段是一套多帧动画。工作流:

  1. 用内部 AI 设计生图平台生成多张高清静态设计稿—— 一次出多张,从中选最符合整体风格的一张。

  1. 把选中的静态图作为风格锚点,配合具体的提示词(描述动作、帧数、视角等),在 PixelLab 上生成像素风动图关键帧

  1. 导出为 PNG Sprite Sheet,Phaser 按帧播放动画

最终在代码里,每种鱼的精灵配置是这样的:

// game/data/constants.ts(简化){  nameKey: 'fish.clownfish.name',  price: 60,  priceType: 'coins',  coinRate: 0.8,  rarity: 'rare',  waterType: 'saltwater',  spriteConfig: {    textureKey: 'clownfish_orange',    sheetPath: 'assets/clown_fish.png',    frameWidth: 64,    frameHeight: 64,    frameCount: 9,    frameRate: 8,    stageSheets: {      fry:      { sheetPath: 'assets/clown_fish_S.png'/* ... */ },      juvenile: { sheetPath: 'assets/clown_fish_M.png'/* ... */ },    },  },}

每种鱼 3 个成长阶段各一张 sprite sheet(S/M/成鱼),16 种鱼共 48 张 PNG,全部由 AI 生图工具链产出。

装饰物 & UI 图标:代码化渲染

对于珊瑚、海草等装饰物,我们走了另一条路——让 AI 直接生成代码,把像素图表达为 PixelRun 数据结构(每一层颜色对应一组像素块坐标),运行时用 Canvas API 逐层绘制:

// game/data/coralReference.ts(片段)export const coralReference: CoralReferenceArt = {  width45,  height: 48,  layers: [    { color0x6a1a2c, runs: [{ x31, y: 9, w: 1, h: 1 }, ...] },    { color0x8b2942, runs: [{ x30, y: 10, w: 2, h: 1 }, ...] },    // ... 数十层颜色,每层数十到数百个像素块  ],};

好处是:装饰物可以在代码里 diff、版本管理、批量调色,AI 可以精确"把某一层的颜色从蓝色改成深蓝色"。

UI 层的图标(金币、珍珠、鱼、书本等)同样是代码绘制——用离屏 Canvas 在 6~12px 的小网格上逐像素画,导出 data URL,通过 CSS image-rendering: pixelated 整数倍放大。

翻车故事:从"撞运气"到"可控产出"

这条工作流不是一步到位的。

第一次翻车:AI 生图阶段的失控。 用 AI生图平台 生成设计图的早期,我们发现同样的描述词换一个种子或换一个模型,出来的结果可能差距巨大。一只"可爱的小金鱼"生成出来可能是写实油画风、可能是 3D 渲染风、也可能是抽象卡通风。每次都像抽卡,完全不可控。

我们摸索出的方法:尽可能增加有效输入。具体来说:

  • 给更多的画面描述(不是"一条鱼",而是"像素风、16-bit、白色背景、侧面朝右、圆润的小金鱼、9 帧游动动画")

  • 给一张样例图作为锚点——让 AI 在已有的风格基础上修改,而不是从零创作

  • 一次性生成多张,用抽卡模式选最中意的那张

本质上就是:约束越多,AI 越稳定。自由度越高,漂移越严重。

第二次翻车:装饰物代码化阶段的膨胀。 把参考图丢给 我们的编码 Copilot 让它生成 PixelRun 绘制代码,第一版跑起来画面还行,但很快发现问题:代码文件异常膨胀,运行时偶有卡顿。

打开生成的代码一看——AI 把参考图里每一个色阶都忠实还原成了独立的 fillStyle 调用,几百种颜色,每帧渲染要切换上百次 fillStyle。我们让 AI 自己优化("帮我合并相近颜色"),改了几轮效果有限,因为它每次从同一张高分辨率参考图出发,总是会再次还原那些细节。

最终解法不是修补 AI 的输出,而是改我们的输入:先把参考图清洗(去掉白底、投影等非游戏元素),再量化到一套受控的小调色盘(32-48 色),然后才把"干净的、低色数的"图喂给 AI。从此代码产出又准又轻。

这件事让我们总结出一条核心原则:

和 AI 协作时,控制输入比修补输出更有效。

3.3 资产复用:Phaser 渲染的鱼怎么用到 React 图鉴里?

游戏画面里鱼是 Phaser 用精灵图渲染的。图鉴界面是 React 弹窗。问题来了:图鉴里要展示每种鱼的图标,怎么把 Phaser 的渲染结果给到 React 用?

一开始我们打算在 React 侧单独维护一套鱼的缩略图。但一想——鱼种有 16 种、每种有成长阶段变化、后续还会扩展——维护两套资源是工程灾难。

最终的方案是直接复用 Phaser 的渲染结果。核心代码(简化):

// GameScene.ts — generateSingleSpeciesIcon()private generateSingleSpeciesIcon(speciesIndexnumberspeciesFishSpecies): void {  // 1. 在屏幕外创建临时鱼实例,设为成鱼阶段  const tempFish = new Fish(this, species, -9999, -9999);  tempFish.growthStage = 'adult';  tempFish.draw(0);  // 2. 用 RenderTexture 捕获画面  const rt = this.add.renderTexture(006464);  rt.draw(tempFish.sprite ?? tempFish.graphics);  // 3. snapshot 异步导出为 data URL  rt.snapshot((imageHTMLImageElement) => {    const canvas = document.createElement('canvas');    canvas.getContext('2d')!.drawImage(image, 00);    this.speciesIcons[speciesIndex] = canvas.toDataURL('image/png');    tempFish.destroy();    rt.destroy();  });}// 4. 通过 EventBridge 传给 ReacteventBridge.emit(Events.SPECIES_ICONS_READYthis.speciesIcons);

React 侧的图鉴组件直接监听这个事件:

// CollectionModal.tsxconst [speciesIcons, setSpeciesIcons] = useState<Record<numberstring>>({});useEffect(() => {  const handleIcons = (iconsRecord<numberstring>) => setSpeciesIcons(icons);  eventBridge.on(Events.SPECIES_ICONS_READY, handleIcons);  return () => { eventBridge.off(Events.SPECIES_ICONS_READY, handleIcons); };}, []);// 渲染:<img src={speciesIcons[index]} width={48} height={48} />

增加新鱼种时只需要加一套精灵图,图鉴里自动就有了对应图标,零额外维护成本。

AI 在这里的角色:整个 RenderTexture + snapshot + EventBridge 的链路代码,是我们把需求描述清楚后让 AI 一次性生成的。我们做的是 review 异步时序是否正确(snapshot 是异步的,多个鱼种要串行处理)、临时对象是否及时销毁、精灵图未加载时的 fallback 机制。

3.4 游戏感:让画面"活"起来

对后端来说,最不在舒适区的可能就是"让东西看起来自然"这件事。逻辑对不对很好验证,但"看起来好不好"是感性判断。

我们在"游戏感"上花了大量时间,这些细节叠加起来才让一个技术 demo 变成了一个让人想留下来的游戏。

太阳与月亮:我们做了精致的天体系统——太阳和月亮会在一天内沿弧线移动,靠近海平面时直径会变大(模拟大气折射效果),它们在海面上有实时的倒影和光晕。看一下太阳的折射效果实现:

// BackgroundRenderer.ts — drawSun()(核心片段)const sunY = Math.round(skyHeight * 1.03 - Math.sin(progress * Math.PI) * arcHeight);const baseSunRadius = Math.round(Math.min(width, skyHeight) * 0.06);// 大气折射效果:太阳接近地平线时圆盘变大(最多放大 40%)const horizonProximity = Math.max(0, 1 - Math.abs(sunY - skyHeight) / (skyHeight * 0.3));const refractionScale = 1 + horizonProximity * horizonProximity * 0.4;const sunRadius = Math.round(baseSunRadius * refractionScale);

这几行代码让日出日落时太阳看起来特别大和柔和——这种"从物理现象到代码公式"的转化,恰好是 AI 最擅长的事。

岛屿的沉浸感:水面上的小岛在不同时间段(清晨/正午/黄昏/夜晚)和不同季节使用不同的贴图,加强"这是一个活着的世界"的感受。

彩蛋与生态:偶尔会有鲸鱼从深海浮上来、海鸥掠过水面、飞鱼跃出海面、岛上有飞鸟栖息、天空飘过奇形怪状的云——这些都是纯氛围性的随机事件,它们不影响游戏逻辑,但让玩家觉得每次打开都有新发现,值得多看一会儿。

四季粒子:春天樱花飘落水面、夏天阳光斑驳、秋天落叶漂浮、冬天水面薄冰效果和冰晶粒子。

鱼的游动:不能走直线(太机械),不能纯随机(太癫狂),要有"闲逛 → 发现食物 → 加速抢食 → 满足后减速"的行为节奏。最终实现是随机游走 + 简单避障 + 状态机驱动。

音效全程序化合成:这可能是整个项目最"硬核"的跨界点之一——游戏里所有的声音,包括 BGM(背景音乐),全部通过 Web Audio API 用振荡器程序化合成。没有一个外部音频文件。

BGM 是逐音符定义的旋律数据,由合成器实时演奏:

// BackgroundRenderer.ts — drawSun()(核心片段)const sunY = Math.round(skyHeight * 1.03 - Math.sin(progress * Math.PI) * arcHeight);const baseSunRadius = Math.round(Math.min(width, skyHeight) * 0.06);// 大气折射效果:太阳接近地平线时圆盘变大(最多放大 40%)const horizonProximity = Math.max(01 - Math.abs(sunY - skyHeight) / (skyHeight * 0.3));const refractionScale = 1 + horizonProximity * horizonProximity * 0.4;const sunRadius = Math.round(baseSunRadius * refractionScale);

音效合成器(SoundSynth)还带了低通滤波器(3000Hz 截止)和混响效果,模拟"水下听到的声音"的闷暖质感。这意味着连 BGM 作曲也是和 AI 对话完成的——我们描述"想要清晨感的日式小曲",AI 就把音符一个个写出来。

这些效果每一个单独实现都不算太复杂,但对从没接触过游戏开发的后端来说,难的是"不知道该用什么方法"。这里 AI 的价值不是帮你写出你想好了的代码——是帮你找到你不知道的方案。

四、我们沉淀下来的 AI 协作工作流

做完这个项目之后回头看,有几条方法论是我们反复验证过、确实有效的:

4.1 把 AI 当成一位有经验的前端同学,而不是一个补全工具

整个开发过程更像是"对话"而非"指令"。我们不是在写 prompt 让 AI 补全代码,而是在跟一位前端工程师讨论方案。

比如我们不会说"给我写一个 Phaser 的粒子系统",而是说"我想让冬季的水面有薄冰效果,冰面上偶尔有裂纹闪动、有冰晶粒子飘落——你觉得用 Phaser 的粒子系统还是自己画比较合适?"

这个心态转变很关键。当你把 AI 当补全工具时,你需要自己知道要什么;当你把它当同事时,你可以跟它讨论方案。

4.2 截图 + 参考图给 AI 看,让它对齐你的预期

文字描述效果远不如一张图准确。

当游戏画面出现需要调整的地方时,我们的做法是直接截图丢给 AI,并附上参考图告诉它"往哪个方向改"。比如:

"你看现在这个船的船体太小了,不够有存在感。帮我做得更大一些,像这张图里的船这个比例。"(附上一张理想中的船的参考图)

AI 同时拿到了两个信息:当前状态(截图)+ 期望方向(参考图),它能很精确地理解"大"是大到什么程度、哪些比例要调。

这比纯文字描述强得多——"把船做大一点"可以有无数种理解,但"做成这张图里这个比例"是明确的。我们在调整渔船、岛屿、装饰物造型时大量使用这种模式,效率非常高。

4.3 一次只让 AI 修一个问题

这是我们踩坑后总结出的铁律。

早期我们经常一口气甩给 AI 五六个问题:"鱼的碰撞检测有问题 + 水面颜色不对 + 粒子数量太多 + 商店按钮位置偏了 + 音效触发时机不对"。结果 AI 改了这个漏了那个,甚至改 A 的时候把 B 搞坏了。

后来我们严格执行"一次一个问题":提一个 → AI 修复 → 验证通过 → 提下一个。专注单点能让 AI 发挥到最好,并行多个问题会互相干扰。

4.4 先定义"成品长什么样",再让 AI 动手

AI 不做产品决策——这是我们的事。

在让 AI 写任何代码之前,我们会先用自然语言完整描述"这个东西做完应该是什么样"。比如在做经济系统之前,我们先把经济节奏表(从开局到终局的金币产出、鱼数量、里程碑、预计到达时间)全部设计好,然后才让 AI 照着实现。

如果你把"应该怎么设计"这个问题也甩给 AI,它会给你一个泛泛的方案,但大概率不是你想要的平衡感。

4.5 复杂资产用代码生成,而非手工维护

用代码表达资产(而非二进制文件),让 AI 能够复现、能够批量修改、能够 diff 追踪。

装饰物的 PixelRun 数据、UI 图标的像素绘制、BGM 的逐音符旋律定义——这些全部是代码,不是二进制文件。当你手工录制一段音乐时,AI 帮不了你微调第 3 小节的某个音符;但当音乐是一个数据结构时,AI 可以精确地"把第 3 小节的 D4 降半音改成 Db4,节拍从 1.5 秒改为 1 秒"。

4.6 可视化调试面板:让 AI 的改动立刻能验证

我们在游戏里内置了一个 Dev Panel(开发者调试面板),支持:

  • 一键切换季节(实时预览春夏秋冬的粒子效果和岛屿贴图)

  • 一键添加任意鱼种(不受价格和容量限制)

  • 一键调节全鱼属性(心情/饱食/健康设为 0/50/100)

  • 资源随意调节(金币 ±1000,珍珠 ±10)

  • 手动设置日期/时间(预览不同时段的太阳月亮位置)

这个面板本身不复杂,但它让整个 AI 协作循环变快了——改完代码 → 刷新页面 → 用 Dev Panel 快速构造测试场景 → 截图反馈给 AI → 下一轮迭代。没有这个面板,每次验证都要从开局慢慢走流程,迭代效率会指数级下降。

五、AI 帮不上忙的地方

为了保持客观,我们也总结了 AI 在哪些地方帮不上忙或者帮倒忙:

设计决策。 经济体系怎么平衡?繁殖冷却设多久?稀有鱼变异概率多少?这些需要游戏设计直觉和反复体验调整的事情,AI 给不了有效建议。它可以帮你实现任何数值,但不能帮你决定"玩起来爽不爽"。

跨模块的全局架构判断。 当系统间开始互相影响(经济系统影响繁殖 → 繁殖影响图鉴 → 图鉴影响成就 → 成就发放奖励影响经济),哪里需要解耦、哪里需要内聚、哪里需要加限制——这需要对全局有理解。AI 看到的永远是局部上下文。

美术审美的最终拍板。 可以用 AI设计平台 生成十种鱼的造型方案,但哪一版最"好看"、最符合整体风格调性——这个判断只能人来做。我们在 PixelLab 生成每种鱼的动图时,平均要从 5-10 张候选中挑 1 张。选择的标准是"感觉",这不是 AI 能替代的。

平台部署的最后一公里。 我们的前端代码一直在本地开发调试,一切运转良好。但当我们要把它部署到 公司内部的前端部署 平台时,问题来了——本地跑得通的代码,发到平台上各种报错,而这些错误在本地完全无法复现。

我们尝试让 AI 解决这个问题,但遇到了一个死循环:平台上的报错信息需要我们手动复制粘贴给 AI → AI 给出一个修改建议 → 我们改完代码后无法在本地验证,只能重新发布到平台 → 发完再看是否修好,没修好就再把新的报错搬运给 AI → 如此反复。整个调试循环变成了:改代码 → 发布 → 看报错 → 搬运信息给 AI → AI 给方案 → 再改 → 再发布……每一轮都要等构建部署完成,效率极低。

核心困难在于:AI 无法直接观察平台上的运行时环境,也无法自己动手试验。它只能根据我们"搬运"过来的片段信息做推测,而平台环境的差异(构建配置、资源路径、运行时注入等)恰恰是那种需要反复试错才能定位的问题。这跟在本地调试时 AI 可以"改一行 → 刷新 → 看效果 → 再改"的高效循环完全不同。最终这个问题是请前端开发同学帮忙才解决的。对于有平台部署经验的人来说,这些可能是一眼就能看出的环境配置问题,但对我们和 AI 来说,缺乏直接调试手段让整个过程变得异常低效。

这也说明了一点:AI 的能力边界不只取决于问题本身的难度,还取决于反馈回路的效率。当调试循环从秒级(本地热更新)退化到分钟级(构建部署),AI 协作的优势就大打折扣。

六、给所有想跨界的后端同学

在 AI 出现之前,"跨界"的成本是:先花几个月系统学习一门新技术 → 再花几个月在项目中积累经验 → 才能产出一个像样的东西。这个时间成本让大多数人望而却步。

现在 AI 把这个等式改了。我们三个纯后端工程师,用三周时间,做出了一款前端近 5 万行代码、包含完整游戏系统的 H5 游戏并上了预发。这在一年前是不可想象的。

这里面真正稀缺的不是技术能力——AI 可以提供。真正稀缺的是敢开始:敢去接一个完全在舒适区之外的挑战,然后相信 AI 能帮你填补知识缺口。

如果你也是后端,如果你也有过"想做个前端/游戏/App 但觉得自己不会"的想法——试试看。AI 已经把门槛压到了前所未有的低。你需要的只是一个开始的理由。