Cardless:缘起

在上月底的时候,我萌生出做一款桌游产品的想法。

起初的前段时间,我对桌游活动的兴趣大涨。我和朋友们玩了一些桌游,有《Night of the Ninja:忍者之夜》、《Coup:政变》和《三国杀》,体验不错。随后,我探索了不少桌游相关的故事、产品和技术。《三国杀》创始人黄恺(KayaK)的成长经历(参考:维基百科 - 三国杀发展史)让我产生了深深的共鸣,甚至说是感动。并非他的创业经历多么跌宕起伏,而是我在他身上好像看见了自己的影子。开源回合制游戏框架 boardgame.io 给了我启发,让我意识到游戏框架的价值可能远胜过一个具体的游戏实现。最近,了解到朋友的朋友做实体桌游的经历(《法师模拟器》- 摩点),让我觉得动手做点东西出来好像并不遥远。

加上我要出差一段时间,临行前就构想了大家一起打桌游的场景,但我不想为了尚未成形的事情就在我的行李当中额外增加几个卡片盒。于是乎,这个桌游产品的需求雏形就产生了:我想做一个在手机上就能玩起来的产品。

在更早的时间,我已经用 vibe coding 做过一个简易的 《coup》。那时候我遇到一个挑战,游戏业务逻辑的实现极度复杂,例如游戏进行时不同卡牌效果的执行、Bluffing 机制的判定,这需要大量的时间去设计和开发。我为了快速得到一个可用的产品原型,我选择了一条偷懒的道路,我放弃了完整游戏逻辑的实现(几乎是全部业务逻辑,例如玩家回合、卡牌效果和 Bluffing 机制的判定),只实现了最底层的动作:获得牌,失去牌,金币的计数。游戏业务逻辑全部由每位玩家自行判断和执行,做出来的 app 本质上类似于一个虚拟牌桌/记牌器。

这个产品功能实际上是符合「桌游」的定位的。我们知道,桌游的一大乐趣在于现场与朋友们的互动交流。尽管将桌游改编成电子游戏并非难事,但是现实当中此类产品显然并没有获得太多的流行度(你听说过最大的线上桌游平台 Board Game Arena 吗?)。故而,我没有必要开发一个电子桌游。我的产品就是为了线下朋友聚会桌游的场景而开发的,所以完全可以靠现场的玩家执行回合顺序、判定和执行等复杂业务逻辑,就像玩真正的实体桌游时做的那样。

我在酝酿的过程中又产生了新的想法。如果我做一个《coup》出来,能玩多长时间?我又想玩《Night of the Ninja》怎么办呢,我需要再搓一个吗?这样的项目是不具备可扩展性的,每一个新的桌游都需要我从头开始编写大量的代码。众所周知,我是一个懒人,我看到后面无穷无尽的重复就感到头疼。那干脆我做出一个通用桌游框架出来?这样后面每一个新的游戏的开发都只是一次增量工作。既然它们都属于纯卡牌桌游,而且我只实现虚拟牌桌/记牌器的功能,各类卡牌桌游的底层抽象可能是完全一致的!是的,我很快发现了这一点。他们的游戏状态本质上就是多个集合与元素的关系,而我实现的最底层行为即元素在不同集合之间的移动。我基于这个点开始考虑通用桌游框架的设计。

继续酝酿,我在追求可扩展性的道路上有了更大的野心。这个通用框架,应当面向游戏设计师/非技术人士/有创意的普通人友好。我想起了中学时期的我自己,我在一张张卡片上面作画和书写人物技能,制作了一个融合了《欢乐西游》、《西游杀》和一众 MOBA 游戏共同特点的回合制卡牌桌游。那时的我没有能力利用计算机来提高我的桌游产品的完整度。我想到,有太多人像我一样,希望将自己的创意落到纸面上。这些小的创意如果经过发展和打磨,最终会成为一个又一个有趣的桌游产品,甚至像黄恺做到的那样。于是,我将桌游设计功能作为我这个通用桌游框架设计的重点,它一定是让设计者能够通过极简的描述就能完成自己的具体桌游的开发。慢慢地,我写出来关于桌游配置的 card(牌面定义)pile(牌堆定义) 等抽象,move(卡牌在牌堆间的移动) 这个基础动作,一直到后续的牌堆属性、公共/私有区域的划分和动作属性。我推翻了最初的快速原型,转向专注在设计者唯一接触的游戏配置机制的结构设计上面。这件事的代码量极小,agent 几乎无法帮助我,只能一遍又一遍的思考和尝试。我的目标是让几乎所有的卡片桌游:扑克牌、麻将、coup、三国杀等等,都能在它上面实现。

当游戏配置机制的结构基本定型之后,事情开始变得顺利起来。我逐渐确定下来 pilepublic/private 属性划分,然后通过 swap公共区交换 的思想解决了玩家间卡牌转移的难题,又在玩家视图当中做了关键取舍(删减了显示其他玩家牌堆的需求),摸索出了各个窗口的布局和样式,并最后决定采用 0 代码实现的方式来提供有限的预设动作。做游戏和其他 coding 产品略有区别,游戏过程中的交互方式,视觉效果/动画非常影响玩家体验,这些是做普通前端网站很少需要的。

最终,一个初步的 cardless 原型诞生了。下面放一张玩家体验视角的截图吧。

carless demo


最后修改于 2026-07-09