做联网多人小游戏:技术栈、关键决策、避坑清单

# 做联网多人小游戏:技术栈、关键决策、避坑清单

想做一款联网多人小游戏,第一个要回答的问题不是"前端用什么框架"——是"**一个房间在后端是什么**"。

每个玩家会进一个房间,房间里有几个玩家、一份共享状态、需要实时同步操作。这个房间在你的后端代码里,对应的应该是什么?一行数据库记录?一个 Redis key?一个长连接的会话对象?还是一个独立运行的进程?

独立开发者 Calvin Flegal 做了一款叫 [Migo Games](https://migo.games) 的社交小游戏机(Mac、iOS、网页都能玩)。他给出的答案是:

> "the core game unit, the room, exactly match the back-end process model."(核心游戏单元——房间——刚好对应后端的进程模型)

也就是 **room == process**。一个房间就是后端跑的一个独立进程,玩家进房间相当于连进进程,房间状态就是进程内存里的数据,房间结束进程退出。

这一个匹配带来三个好处:

1. **横向扩展顺**——加房间就是加进程,进程是廉价的,多到几万也没事
2. **故障隔离**——一个房间崩了,崩的是那个进程,别的房间不受影响
3. **消息路由天然**——玩家给"房间"发消息就是给进程发消息,不用额外造路由层

在大多数语言里,"进程"是重型概念——开一个进程要 MB 级内存,开一千个就吃不消。所以 room == process 这个等式只在某些语言里能成立。Calvin 选了能成立的那个。

下面按 Calvin 的实际选择,给出一份"想做一款联网多人小游戏"的实操路线图——技术栈、关键决策、避坑清单都覆盖。文章末尾给原文和 [HN 讨论](https://news.ycombinator.com/item?id=48256053)([原文链接](https://calvinflegal.com/2026/05/24/what-ive-learned-so-far-building-online-mini-games-with-elixir-and-swift.html))的入口。

---

## Migo Games 是什么样的项目

先看清你要做的是什么类型的项目,再选技术栈。

Migo Games 是一款**社交小游戏机**——玩家在 App 里看到一个房间列表,点进去和朋友/陌生人玩简单的迷你游戏。其中一个游戏叫 Arrow,[网页版](https://migo.games)能直接玩。整个 App 在 App Store 上几个 MB——Calvin 特意提到这个数字,对照 Mario 64 的 8 MB(Mario 64 大部分还是音乐)。

这种项目的特征:

- **状态轻**——一局游戏几秒到几分钟,结束就丢
- **延迟敏感**——玩家点击要立刻同步给同房间的人
- **并发房间多**——同时可能有几百几千个房间在跑
- **客户端表现要细腻**——iPhone 上的 haptics、动画、全屏体验是关键

如果你做的是 MMO 那种长持续状态、上千玩家在同一片地图上的游戏,下面的栈不一定适合。但社交迷你游戏、卡牌对战、吃豆人那种 4-8 人小桌——下面这套刚好对应。

---

## 技术栈逐项决策

### 后端:Elixir + Phoenix

Calvin 选的后端是 [Elixir](https://elixir-lang.org/) 跑在 [Phoenix](https://www.phoenixframework.org/) 上。

Elixir 跑在 BEAM 虚拟机上——这个 VM 当初是 Erlang 为电话交换机设计的,每个进程几 KB 内存、调度天然抢占式、跨进程消息传递就是语言原语。"开几万个进程"在 BEAM 上就是日常操作。

room == process 在这套技术栈里特别自然,因为 Phoenix 的 LiveView socket 模型(基于 WebSocket 的实时双向连接抽象)刚好对应一个房间一个进程:玩家连进 socket → 服务端起一个 GenServer 进程对应这个房间 → 玩家发消息就是给进程发 cast/call → 房间状态在进程的 state 里。整套实时多人协作的脚手架不用自己搭。

为什么不用 TypeScript / Node?评论区有几条对应回答:

[colechristensen](https://news.ycombinator.com/user?id=colechristensen):

> "Phoenix is delightfully fast and not having to deal with two entirely different application stacks where your application is split down the middle (or the javascript ecosystem) is a breath of fresh air."(Phoenix 又快又顺,不用应付前后端两套完全不同的栈,也不用陷在 JS 生态里,是种解脱)

[frail_figure](https://news.ycombinator.com/user?id=frail_figure):

> "It makes it trivial to do soft-realtime because it's just actors (GenServers) passing messages... I invite you (and others) to try it out and do a small weekend project. It'll make you reconsider reaching for TS on the backend."(做 soft-realtime 几乎不用动脑,因为本质就是 actor [GenServer] 之间传消息……建议你周末拿来做个小项目,会让你重新考虑后端用 TS)

[mcintyre1994](https://news.ycombinator.com/user?id=mcintyre1994) 一句话点破:

> "I'd guess that 2 is a huge part of the answer to 1 here - Elixir makes real-time multiplayer easy."(我猜实时多人这个需求本身就是选 Elixir 的核心理由——Elixir 让实时多人变得简单)

Calvin 自己的总结:

> "I'm happy I didn't just go with Node or Bun as would have been the default choice for a guy whose best language is TypeScript."(我很庆幸没默认选 Node 或 Bun——对一个 TypeScript 是主语言的人来说那本来是默认选择)

### 后端备选:Cloudflare Durable Objects

评论区有同业代表持不同意见。[zuzululu](https://news.ycombinator.com/user?id=zuzululu) 推荐 [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/):

> "Cloudflare Durable Objects really is generous, not often can you get replicated database and realtime sync for such low barrier in cost and implementation."(Cloudflare Durable Objects 真大方,能用这么低的成本和实现门槛拿到复制数据库加实时同步的不多)

Cloudflare DO 在概念上跟 BEAM 类似——也是"每个对象一个独立的运行时单元",状态绑定在对象上、对象在边缘节点就近运行、消息传递作为基本通信。如果你不想自己运维 BEAM 集群,Cloudflare DO 提供的是托管版的"actor 模型"。

[frail_figure](https://news.ycombinator.com/user?id=frail_figure) 反驳:

> "BEAM gives it to you for free, and you don't rely on an Internet-scale monopoly to run it."(BEAM 免费给你这套能力,而且不用依赖一家互联网级垄断公司来跑)

判断标准:

- 想自己掌控运维、不想被云厂商锁住 → BEAM 路线(自托管或 Fly.io)
- 想要托管、能接受 Cloudflare 锁定、不想学 Elixir → Cloudflare DO 路线
- 已经在 JS 生态里且想用 actor 模型 → Cloudflare DO 也走得通

### 客户端:Swift + SpriteKit

Calvin 客户端选的是 [Swift](https://www.swift.org/) + [SpriteKit](https://developer.apple.com/spritekit/),target Mac 和 iOS。

Swift 不用解释。SpriteKit 是苹果原生的 2D 游戏引擎,跟 UIKit/SwiftUI 集成顺、上手快、不用引入第三方游戏引擎(Unity/Godot)的复杂度。给 Migo 这种 2D 小游戏完全够用。

Calvin 提了一个反直觉的细节:整个 App 只加了**一个** Swift 依赖——一个 Phoenix socket 客户端库。其他全是平台原生 API。这种 dependency discipline 在 AI 辅助编码的时代变得更可行,因为你不再需要为了"省时间"而引入大量第三方库——AI 直接给你写。

### 部署:Fly.io + Crunchy Bridge

后端跑在 [Fly.io](https://fly.io/),数据库用 [Crunchy Bridge](https://www.crunchydata.com/products/crunchy-bridge) 托管的 Postgres。

注意:评论区 [zuzululu](https://news.ycombinator.com/user?id=zuzululu) 顺手提了一句:

> "fly.io (known among developer threads to be very unreliable uptime) wouldn't be my first pick."(fly.io——开发者圈普遍反映稳定性不太行——不会是我的首选)

Fly.io 优势是边缘节点遍布全球、部署简单、跟 Elixir 集群天然契合([Fly.io 自己也用 Elixir](https://fly.io/phoenix-files/) 做控制面)。劣势是历史上有过几次大面积 outage。如果你的游戏对 uptime 极敏感,可以考虑 Hetzner / DigitalOcean / 自购 VPS 跑 BEAM 集群。

### 客户端原生 vs Web

这是 Calvin 给的另一票关键决策:**优先做原生,不做 Web**。

> "the web has really disappointed me in terms of performance when compared to native. It's always outdone by native. You can even try this yourself with Migo! Play Arrow on the web then go try it on the native iPhone version. The cute haptics, the full screen, the animations, it's really just not close."(论性能,web 真让我失望——总是被原生吊打。你自己用 Migo 试试就知道:先在网页上玩 Arrow,再去 iPhone 原生版玩,那种 haptics、全屏、动画的差距,真的没法比)

判断标准:

- 你的游戏靠**触感反馈/全屏沉浸/精细动画**取胜 → 原生
- 你的游戏靠**任何人秒上手/不下载就能玩**取胜 → web
- 想兼顾 → 先做一边、留对方接口(Calvin 就是这条路:原生为主、web 用 Phoenix LiveView 顺手出一个版本)

### 同时编译 Mac 版,提速 iOS 开发

Calvin 给独立开发者一条具体的提速建议:在 Xcode 项目里加一个 **Mac target**(在 iOS target 之外,再加一个"编译为 Mac 原生应用"的产物),日常开发主要跑 Mac 版而不是 iOS 模拟器。他原话:

> "I would encourage others to target Mac in addition to iOS for a reason you might not expect: build times. The simulators and Xcode are really pretty slow. It's all a lot faster if you're targeting Mac."(建议大家除了 iOS 也加上 Mac target——理由可能你想不到:构建速度。模拟器和 Xcode 真挺慢,编译成 Mac 版整体快很多)

iOS 模拟器启动慢、Xcode 构建慢、热重载弱——iOS-only 的开发循环非常痛苦。同时编译 Mac 版后,你日常迭代用 Mac 原生跑(开发机本身就是 Mac,零模拟器开销),需要验证 iOS 行为时再切模拟器或真机。能把日常 feedback loop 从分钟级压到秒级。

副作用:你顺手得到一个 Mac 版本,多一个发行渠道。

---

## 避坑清单

### 1. 服务器地理位置直接影响体验

Calvin 自己回应评论:

> "The server is in NJ. If you're in the EU the game might really lag. Sorry!"(服务器在新泽西。欧洲玩家可能会感觉很卡,抱歉)

实时多人对延迟敏感,单服务器 RTT 超过 100ms 已经能感受到滞后。早期 MVP 单地区可以接受,但用户跨地区后**必须做边缘部署**:

- Fly.io:原生支持多 region 部署,BEAM 集群自动跨节点同步
- Cloudflare DO:天然边缘运行,玩家就近连接

如果只在一个地区跑,至少在游戏内显式告诉玩家"服务器在 X 地区,离得远会卡",别让玩家以为是自己网络问题。

### 2. Mobile 测试的反馈循环比 Web 慢得多

[karado](https://news.ycombinator.com/user?id=karado) 提出一个具体痛点:

> "In my own projects I've found the feedback loop to be much slower in native mobile apps compared to web... I get the agent itself to do this before I do using Codex's chrome extension. This same process on mobile is a lot slower. How have you approached this aspect? Have you figured out a way to get the agent to control an emulator?"(我自己的项目里,原生 mobile app 的反馈循环比 web 慢得多……web 上我用 Codex 的 chrome 插件让 agent 自己去测最新功能/修复。同样的流程在 mobile 上慢很多。你怎么处理的?有没有让 agent 控制模拟器的方法?)

[zuzululu](https://news.ycombinator.com/user?id=zuzululu) 给的答案:

> "Maestro but honestly you should always be testing on real devices."([Maestro](https://maestro.dev/),但说实话你应该在真机上测)

[Maestro](https://maestro.dev/) 是一个 mobile UI 自动化测试框架,可以脚本化驱动模拟器或真机。AI agent 可以通过 Maestro 跟 mobile app 交互。但 zuzululu 加了一句"应该在真机上测"——模拟器上跑通的 haptics、性能、内存压力,真机不一定一样。

实操建议:

- 开发期:Mac 原生 + Maestro 脚本验证业务逻辑
- 发布前:真机长跑测试(haptics/性能/内存)

### 3. 包尺寸要从一开始就管

Calvin 提到 Migo 在 App Store 几个 MB(对照 Mario 64 是 8 MB):

> "I think AI reduces the need for so much bloat if one is mindful, and I think that's really great."(如果你留意,AI 反而能让代码不那么臃肿——这点我觉得很赞)

依赖一多,包尺寸就涨。AI 辅助编码的副作用是——你不再需要"为了省时间引入大库",可以让 AI 直接给你写。Calvin 的 Migo 总共加了一个 Swift 依赖(Phoenix socket 客户端)。

具体做法:

- 任何依赖加进来前问一句"AI 能不能直接给我写一份"
- 用平台原生 API(UIKit/SpriteKit/CoreAnimation)而不是跨平台 wrapper
- 上线前用 Xcode 的 [App Thinning](https://developer.apple.com/documentation/xcode/reducing-your-app-s-size)(包尺寸分析报告,告诉你哪些 asset 和代码段占了大头)检查是哪个 asset/代码段最重

---

## AI 时代的工作流

Calvin 在文章里很坦诚:

> "I really can't say I wrote any of the code... I do read the code, well, mostly. I certainly understand its design. I think that's still really important with AI."(我真的不能说代码是我写的……不过我会读代码,大部分会读。我对设计完全理解。这点我觉得在 AI 时代仍然很重要)

工作流的几个要点:

1. **理解设计 > 写每一行**——你要能讲清楚 room 为什么是 GenServer、为什么用 socket 不用轮询、为什么前端选 SpriteKit 而不是 SwiftUI 重做
2. **读代码不能省**——AI 写的代码至少要读懂,不读就是埋雷
3. **Coaching clankers 跟自己写不一样**——Calvin 用了 "clankers"(金属罐头)这种自嘲称呼 AI 助手。他原话:"Coaching clankers isn't as prone to flow as writing syntax yourself. Oh well."(教 clanker 干活进入心流的概率不如自己敲代码。算了)
4. **AI 是脚手架,不是建筑师**——架构决策(room == process、原生 vs web、target 哪些平台)必须自己拍

---

## AI 解决不了的难题

文章末尾 Calvin 把 AI 时代独立开发者最痛的一刀亮出来:

> "All the hardest parts of making successful software are still here. One of them is of course finding users/distribution. The clanker can't really match my software to people from my repo. Bummer. In fact, there's so much more software being written now that this problem is actually harder!"(做软件最难的那些事儿都没变。其中之一当然是找用户/分发。clanker 没法把我 repo 里的软件匹配给真正需要它的人。可惜。而且,正因为 AI 让现在写出来的软件多得多,这个问题反而更难了)

distribution 没有技术解。AI 让 App Store 应用数量爆炸,分发问题更难、不更容易。技术栈再合理,没人玩就是死循环。

实操建议(Calvin 没明说但暗示了):

- 做 social-friendly 的游戏(Migo 走的就是这条路——朋友拉朋友进房间)
- 早期社区营销>付费投放(HN/Reddit 等真实社区)
- 让免费试玩的网页版做 funnel(这是 Migo 给原生导流的方式)

---

## AI 是脚手架,路自己挑

Calvin 在文章末尾说:

> "Really none of this would have been possible without AI, so really I'm pretty grateful to be building software in this era."(这一切要是没有 AI 真的做不出来,所以能在这个时代做软件,我挺感激的)

你想做一款联网多人小游戏。Calvin 给的栈是 Elixir + Phoenix + Swift + SpriteKit + Fly.io——这套适合**你能接受学一门新后端语言、想要 fault tolerance、客户端原生优先**的情况。如果你要保留 JS 栈,Cloudflare Durable Objects 是最接近的备选;如果你想做纯 web 游戏,那是另一条完全不同的路(评论区没展开)。

选定 room == process 之后,剩下的栈都是"什么语言能让 process 廉价到不计成本"的衍生选择。

---

**关键链接**:

- 原文博客:[What I've Learned (So Far) Building Online Mini Games with Elixir and Swift](https://calvinflegal.com/2026/05/24/what-ive-learned-so-far-building-online-mini-games-with-elixir-and-swift.html)
- HN 讨论:[news.ycombinator.com/item?id=48256053](https://news.ycombinator.com/item?id=48256053)(29 分 / 11 条评论)
- Migo Games:[migo.games](https://migo.games) | [Mac/iOS App Store](https://apps.apple.com/app/migo-games/id6452680712)
- [Elixir 官网](https://elixir-lang.org/) + [Phoenix Framework](https://www.phoenixframework.org/) + [Phoenix LiveView](https://hexdocs.pm/phoenix_live_view/)
- [Fly.io](https://fly.io/) + [Crunchy Bridge Postgres](https://www.crunchydata.com/products/crunchy-bridge)
- [SpriteKit 文档](https://developer.apple.com/spritekit/)
- [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/)(备选后端)
- [Maestro](https://maestro.dev/)(mobile 测试自动化)