前段时间我用 9 天时间做了一个网页联机卡牌游戏 Neo Card Party,前前后后提交了 160 多次,经历了 6 次架构重写。这篇文章就当是个开发记录,分享一下这个过程。

Neo Card Party 主界面

第 0 天:边缘计算的惨痛教训

一开始我想用 EdgeOne Pages(边缘函数平台)来做联机,结果被坑得很惨。KV 存储限制多、边缘函数跑游戏逻辑各种水土不服。

折腾了半天,最后结论是——边缘函数不适合做实时联机游戏。果断放弃。

第 1 天:283 行的起点

真正的起点其实特别简单——一个叫 ASCLLuno 的单 HTML 文件,283 行代码,用纯 ASCII 字符在浏览器里渲染卡牌。没有框架、没有构建工具、没有后端,就是一个文件。

从下午 4 点到 6 点,2 个小时就做出了一个能玩的 UNO 界面。当时的感觉是:这东西有戏。

但这个版本的问题也很明显——单页面、无路由、无状态管理、无后端、无联机。就是个玩具。

第 2~4 天:走在错误的路上狂奔

接下来做了一个叫 myasclluno 的项目,开始了第一次”正经”重构。这次选择了多页面终端风(Cyber-Terminal)架构

myasclluno 主界面

3 天时间,60 多次提交,经历了:

  • 3 次架构重写:多页面 → 前后端分离 → Canvas 渲染
  • 20+ 次联机调试:等待大厅、重复创建房间、连接失败、聊天系统重构了 3 次
  • 1 个文件暴涨到 1522 行:所有 HTML/CSS/JS 塞一起,完全失控

典型的”屎山越堆越高”——每次遇到瓶颈就换架构,但每次换架构只是把问题推迟了。

为什么这么挣扎?后来复盘发现 8 个致命问题:

  1. 游戏逻辑写了两遍 — 前端一套规则、服务端一套规则,修 bug 要改两个文件
  2. 页面膨胀失控 — 没有组件化、没有模块系统
  3. 零测试 — 全靠手工点点点
  4. 没有构建工具 — 没有 npm、没有 TypeScript、没有热更新
  5. Canvas 和 DOM 两套代码同时维护
  6. 联机系统残缺 — 没有掉线重连、没有玩家持久化、没有结算流程
  7. 移动端靠 hack — 比例缩放能显示,但交互坐标全是补丁
  8. 代码规范建立太晚 — 大量代码已经写死,改不动了

最后一天(第 4 天凌晨 2:46)做完最后一次视觉升级后,我做了个决定——放弃整个项目,从零开始用正确的方式重写

第 5~9 天:真正的成功

新项目叫 myUNO(后来改名为 Neo Card Party)。这次从第一天就用了正确的工程实践:

Neo Card Party 游戏界面

从零开始,不修旧代码

没有在旧项目的废墟上修补,而是直接开新仓库。第一天就用 Vite + TypeScript SPA,单页面应用 + Hash 路由 + View 接口。这是现代 Web 游戏前端的正确基础。

最关键的决策:共享协议包

吸取了之前前后端规则写两遍的教训,抽了一个 shared 包——GameAction 类型、UnoCard 接口、计分函数全部一处定义、两处引用。前后端契约由 TypeScript 类型系统保证一致,编译期就能发现不匹配。这个决策后来被证明是整个项目最正确的选择。

测试先行

旧项目零测试、全靠手工点。新项目第一天就加了测试文件,后来服务端拆出了 4 个可测试模块,每个都有配套测试。测试覆盖了出牌合法性、多人流程等核心场景,联机 bug 大幅减少。

正确的分层

服务端不再是单文件包揽一切,而是拆成了职责清晰的 4 个模块:

  • 规则适配器 — 判断出牌合法性
  • 私有牌状态 — 处理手牌和牌堆
  • 公共状态投影器 — 同步客户端可见信息
  • 动作控制器 — 处理出牌/摸牌/跳过/选色

房间只负责生命周期和消息路由,具体逻辑委托给这 4 个模块。

响应式状态管理

用 Proxy 实现响应式状态,所有状态变更自动触发事件,UI 自动更新。联机场景下服务端推送的状态只需写到 state,所有相关 UI 自动同步。

一些有意思的数字

指标 旧项目 (myasclluno) 新项目 (myUNO)
开发天数 4 天 5 天
提交数 60+ 165+
测试文件 0 10+
架构重构次数 3 次(救火式) 0 次(一次成型)
最终状态 被放弃 成功发布

5 天 165 次提交,没有一次架构重构——不是因为代码写得更好,而是因为吸取了所有教训,从第一天就用正确的工程实践来构建。

学到的几件事

  1. MVP 先行:从单 HTML 文件起步快速验证,比一开始就想”完美架构”重要得多
  2. 架构演进而非预设:每次重构都是因为碰到了真实瓶颈,而不是为了用某个技术
  3. 前后端契约最重要:shared 包解决规则分裂,但依赖管理是持续的挑战
  4. 联机是复杂度倍增器:等待大厅和重连花了最多调试时间
  5. 运营系统是另一个游戏:邮件、兑换码、排行榜结算的工作量不亚于核心玩法
  6. 移动端不是缩小版:横屏游戏的交互坐标需要从底层适配,不能靠事后打补丁

最后

这个项目从 5 月 2 日的边缘函数实验开始,到 6 月 11 日发布公共版,中间经历了无数次失败和重来。但回头看,那些”失败”的项目其实都不是白费的——每一次踩坑都让下一个版本变得更好。

如果你也对做游戏感兴趣,欢迎到 GitHub 看看,也欢迎 star 和支持!

在线体验:uno.songzhearen.cn


项目时间线:2026-05-02 ~ 2026-06-12,跨越 4 个仓库,165+ 次提交