
看到 superpowers 这个词大多数人第一时间想到的是“超能力”。但在游戏开发者这个小圈子里它还是一个真实存在的开源项目名字而且是可以直接装到电脑上玩起来的东西。Superpowers 是一套基于浏览器的实时协作游戏开发环境服务端负责存项目、编译脚本、管资源浏览器就是编辑器本身。你把环境跑起来之后打开一个网页就能建场景、写 TypeScript、跑 Demo还能让几个人同时编辑同一个场景互相看着对方的光标干活。我最初是被“一起做 Game Jam”的场景吸引想安装它结果在版本适配和脚本理解上踩了不少坑。这篇文章就把我从下载到跑通第一个小游戏的完整过程写下来给想装 Superpowers 的朋友当一份避坑笔记。1. 它到底是一个什么样的工具装它能解决什么问题1.1 一句话定位Superpowers 的本质是一个分布式实时协作的 HTML5 游戏 IDE。它的架构很轻服务端用 Node.js 跑起来负责项目存储、资源管理和脚本编译客户端不需要额外安装任何软件开着现代浏览器就能干活。编辑器里看到的场景视图、资源面板、脚本编辑区本质都是网页应用的一部分通过 WebSocket 和服务端通信。所以它的运行模式和大多数游戏引擎不太一样。Unity 或者 Godot 都是先下一个很大的客户端创建工程、打开编辑器、等编译。Superpowers 是一个项目文件夹启动之后整个编辑器在浏览器里加载所有项目数据落在服务端。这种方式带来了一个直接的好处只要大家能连到同一个服务端人就能进入编辑器不太需要折腾 Git 分支合并一类的事。1.2 和 Unity、Godot 相比Superpowers 的差异点如果用 Unity 做原型第一步就是安装几个 GB 的 IDE然后等待编辑器初始化、导入资源、编译脚本。Superpowers 没有这些重流程。我记得第一次启动时整个项目文件夹加依赖也就几百 MBnpm install 完成后一条命令就能把编辑器拉起来浏览器一开就是工作区。这个启动成本让我很愿意在临时起意时用它做原型验证。协作是它最吸引人的特色。传统引擎里两个人同时改一个场景要么靠插件支持要么靠版本控制系统来回 merge。Superpowers 的做法是多用户在共享服务端上编辑模型、场景、脚本都能被多人同时操作编辑过程实时同步。我在实际体验里看到同事在场景里拖拽一个对象的坐标那变化几乎是立刻显示在我屏幕上的。这个能力在 Game Jam 或者远程团队做脑暴原型时非常值钱。当然它也有不适合的场景。如果要做的游戏依赖重度 3D 美术、复杂粒子管线、或者想要直接打包成原生手游那 Superpowers 不是最优解。它输出的游戏本质上是 HTML5 网页应用跑在浏览器、Canvas 和 WebGL 兜底的环境里。对移动端、主机平台的正式发布支持远不如大型引擎完善。我更愿意把它定义为“低门槛的协作原型工坊”而不是“引擎替代品”。1.3 什么样的团队适合用它以我个人经验看三类人适合把 Superpowers 装进工具箱第一类是 Game Jam 玩家需要快速把想法变成可玩页面几个人同时编辑同一个场景效率很高 第二类是游戏设计课的教学场景学生不用装复杂客户端浏览器就是 IDE 环境统一第三类是远程团队做玩法原型需要一个能多人实时修改的共享工作区。反过来如果你已经在用 Unity 做完整商业项目团队规范也都建立在 Unity 生态上那就没有必要为了它强行切换。它更适合作为轻量协作工具插在你的工作流旁边而不是替代主力引擎。2. 安装部署从下载到浏览器打开完整记录2.1 环境准备Node.js 版本选择Superpowers 的官方仓库一般会标注旧版 Node.js 要求但我建议你装 Node.js 16 或更高版本。这里有个很现实的坑老的依赖在 Node 17 以上的 OpenSSL 3 环境里执行 npm install 时可能会报ERR_OSSL_EVP_UNSUPPORTED。我第一次就是拿 Node 18 环境装它装到一半直接红字退出整个人都懵了。后来排查到是老版本 Webpack 和哈希算法 MD4 不兼容新 OpenSSL 导致的。解决办法其实不复杂启动前设置一个环境变量让 Node 使用旧版 OpenSSL provider 即可。Windows 上我用这么一行set NODE_OPTIONS--openssl-legacy-providermacOS 或 Linux 上写成export NODE_OPTIONS--openssl-legacy-provider之后再执行启动命令就不会再报那个 OpenSSL 错误。如果你不想动系统全局的 Node 版本也可以用 nvm 装一个 Node 16在项目目录里单独使用这是最省事的一条路。2.2 一步步安装以当前我用的 0.26.x 版本为例先在任意目录下把 Superpowers 的源码工程下载下来。这个项目在开源社区里就叫 Superpowers官方仓库、Release 包都能找到选择任一 release 的 zip 包即可。下载后解压到本地目录命令行进入那个目录依次执行npm install npm start第一次执行npm install会下载不少依赖项目耗时取决于网络情况可能要等几分钟。如果下载速度不理想可以把 npm 的 registry 换成国内镜像源具体怎么换属于通用操作这里不多说。装完之后npm start 会启动服务端。正常启动时终端里会显示类似下面这些信息Starting the web server on port 42370 Starting the websocket server on port 42371看到这两行基本就成功一大半了。打开浏览器访问http://localhost:42370就能看到 Superpowers 的入口页面。整个过程和装一个普通 Node 项目没有本质区别。2.3 启动后的项目和目录结构很多第一次用的朋友会好奇项目资源到底存在哪我在编辑器里创建的 Sprite、场景、脚本并没有像传统引擎那样堆在文件系统里一堆.png、.ts文件。Superpowers 把资源管理在服务端的存储层里编辑器界面只是可视化入口。这也就意味着你最好不要手动去目录里乱改文件所有增删改都应该在编辑器里完成。启动之后浏览器首页一般会让你创建服务器或者打开已有项目。老版本里首次访问可能要先在首页配置一个项目名称、选择服务器目录稍新一点的版本直接在首页就有新建项目的入口。项目创建好以后会自动进入编辑器界面。整个界面分成几个核心区域中央是场景视图左边是资源面板下面或侧边是控制台脚本编辑区在打开脚本资源后会自动出现。第一次看到这套界面时我的感受是它确实很轻所有面板都能拖拽调整布局没有传统 IDE 那种沉重的菜单堆叠。2.4 端口和访问小知识默认端口是 42370 和 42371前者跑 HTTP 入口后者跑 WebSocket 通道。为什么要单独提 WebSocket 端口因为很多人启动好了自己在浏览器上能打开但局域网同事访问不了。排查到最后往往是同事访问的机器连不上 42371 这个 WebSocket 端口编辑器界面光加载首页成功实际数据一直同步不过来。所以如果想让团队里的人通过局域网直接访问记得确认 42370 和 42371 这两个端口同时可访问。如果在云主机上部署也要在安全策略里同时放行这两个端口。这个细节不算复杂但容易漏掉漏掉以后问题非常隐蔽。3. 界面与核心概念拆解场景、实体、组件和脚本3.1 资源面板到底在管什么Superpowers 左侧的资源面板管理的是所有“资产”包括场景、脚本、Sprite、声音、模型、Tilemap 等。每一种资源都有类型双击某个资源会打开对应的编辑器。比如双击 Sprite 资源会看到图片的帧和中心点设置双击脚本资源会打开代码编辑器双击场景资源才会在中央区域打开场景视图。这个设计本身不特殊几乎所有游戏引擎都是这么组织的。但 Superpowers 的特色在于这些资源在多人协作时是实时同步的不是“你锁一个文件、别人不能动”的模式。你正在编辑的脚本同事也能打开看到你刚拖进场景的一张图同事的场景里如果已经加载了那个资源下一秒就能显示出来。我实际用下来很多临时美术资源都是同事一边导出一边拖进共享项目的效率确实高。3.2 场景、实体与组件的关系场景是游戏的沙盒也是运行时玩家能看到的世界容器。在 Superpowers 的术语里场景里的对象一般叫 Actor本质上就是一个拥有位置的节点。你可以在 Actors 面板里右键新建一个 Actor然后通过添加组件的方式给这个对象增加能力。我这里用一个生活化类比Actor 就像一个空白的相框组件则是挂上去的相框配件。你想让它显示图片挂一个 Sprite Renderer 组件想让它被摄影机拍到场景里得有一个 Camera 组件想让它有逻辑就挂一个 Behavior 脚本组件。当你把一个带有 Sprite Renderer 组件和 Behavior 组件的 Actor 放进场景它就是一个“能自动播放动画或响应输入的小角色”。其实一个场景就是一个世界它在运行时可以被加载、切换。你在编辑器里看到的场景视图就是预览点击 Play 进入运行状态后一切资源、组件、脚本才会真正开始跑起来。场景和实体的关系理解清楚了后面的实操就会顺畅很多。3.3 脚本采用 TypeScript这是最大公约数Superpowers 的脚本语言是 TypeScript这很关键。TypeScript 是 JavaScript 的超集加上了类型系统写起来更有安全感。我不太会 JS 的朋友也能直接上手因为编辑器里大多数常用对象都带类型提示。写脚本时最常用的模式是继承Sup.Behavior这相当于给 Actor 添加一个自定义组件。举个例子做一个最简单的小方块自动旋转脚本大概长这样class Rotator extends Sup.Behavior { speed 90; update() { this.actor.rotate(0, 0, this.speed * Sup.Time.warpedDeltaTime); } } Sup.registerBehavior(Rotator);我不想把这段脚本的逻辑讲得太玄核心就两点一是Sup.Behavior这个父类让脚本能被挂在 Actor 上二是update()方法在运行时每一帧被调用。speed字段会直接暴露在编辑器右侧的 Inspector 面板里你可以不写代码就调整转速。这种“脚本字段可视化”的设计让我觉得它比很多纯代码框架更适合快速迭代。3.4 实时协作的同步原理允许多人同时编辑还能大致保持一致性原理上主要靠 CRDT。简单理解CRDT 是一类无冲突的协同数据结构每个编辑操作都会带一个唯一的操作标识多个客户端做的修改在服务端汇总时可以按照一定的规则合并不需要传统意义上的加锁。所以你可以看到同事正在移动一个 Actor他也能看到你同时改脚本互相都不会被逼着“保存并退出”。我见过有人担心既然没有锁会不会两个人同时改一个脚本导致乱七八糟实际上文本层面的字符级合并我还是比较乐观的编辑过程能看到对方光标双方会自然岔开不同的修改区域。真正麻烦的是语义冲突比如一个人删掉了某个类另一个人还在往里面写方法这种冲突 CRDT 是管不了的需要团队协作时彼此说一声。我的经验是分工先行数据同步工具只是辅助。4. 实操记录做一个键盘控制的 2D 方块4.1 创建项目、场景和基础对象环境跑起来后我在首页新建了一个项目项目模板选了 2D。Superpowers 提供 2D 和 3D 两种模板选择 2D 会自动配上默认摄影机方向和坐标体系省掉很多初始设置。进入编辑器后我新建了一个场景资源命名为 Main双击打开。第一次打开场景中央面板会出现一个类似舞台的空白视图右侧 Inspector 面板则是空白。我接着在层级区域新建了一个 Actor把它命名为 Player。这一步相当于创建了一个表示玩家的空对象接下来所有可见组件和行为都会加在它身上。为了让它能在场景里显示出来我给它添加了 Sprite Renderer 组件并把它的 Sprite 资源指定为内置的白色方块。Superpowers 内置了一些基础 Sprite 资产不需要自己导入图片就能快速验证逻辑这也是很适合原型开发的设计。4.2 添加输入映射和摄影机如果脚本里要响应键盘输入可以写死在代码里判断按键名也可以用 Input Mappings。我比较推荐用 Input Mappings 的方式因为现代游戏通常需要支持方向键和 WASD或者将来接手柄。在项目设置面板里我添加了一个叫 Horizontal 的水平轴把 Left、A 映射到负方向Right、D 映射到正方向再添加 Vertical 轴把 Up、W 和 Down、S 映射好。这一步看起来很不起眼但能省后面大量麻烦。你想想如果直接在每个脚本里写“LEFT”“A”等魔法字符串目标多了之后改键位会很痛苦。而用轴的话代码里只关心轴的值是多少映射关系集中管理。场景里的摄影机也要确认好位置。如果场景中没有 Camera画面可能一片空白。我新建了一个 Camera Actor并把它放在原点附近朝向 2D 平面即可。在 2D 模板下拍摄方向已经预设过不需要特别调角度。4.3 编写第一个行为脚本接下来是核心环节让 Player 能响应键盘移动。我在资源面板新建了一个 Behavior 脚本取名为 PlayerMove双击打开后写了下面这串代码class PlayerMove extends Sup.Behavior { speed 3; update() { const move new Sup.Math.Vector2(0, 0); if (Sup.Input.isKeyDown(LEFT)) move.x - 1; if (Sup.Input.isKeyDown(RIGHT)) move.x 1; if (Sup.Input.isKeyDown(UP)) move.y 1; if (Sup.Input.isKeyDown(DOWN)) move.y - 1; if (move.x ! 0 || move.y ! 0) { move.x * this.speed * Sup.Time.warpedDeltaTime; move.y * this.speed * Sup.Time.warpedDeltaTime; this.actor.move(move); } } } Sup.registerBehavior(PlayerMove);现在拆一下这段脚本为什么这么写。我用一个二维向量move来记录方向按左键就减 x按右键就加 x按上下键调整 y。如果直接拿这个向量去改变坐标不同帧率下移动速度会完全不一样所以必须把向量乘以Sup.Time.warpedDeltaTime。这个值表示“经过一帧的时间比例”能让移动速度与帧率解耦。最后调用this.actor.move(move)把位移应用到 Actor 上。保存脚本后如果编辑器没有报编译错误就可以回到选中 Player Actor 的状态在 Inspector 面板里添加 Behavior 组件选择 PlayerMove。添加成功后speed 字段就显示在面板里了默认是 3。这一步相当于把“玩家移动”这个能力挂到了那个空对象身上。4.4 运行测试和调试点击编辑器右上角的 Play 按钮浏览器就会进入游戏运行状态。这时候场景中央会出现我们挂好脚本的方块键盘按方向键它就动起来了。每次代码保存Superpowers 会热加载脚本不需要频繁重启整个项目这是我很喜欢的地方。原本在传统引擎里改一行数值要重新编译这里基本是即时生效。调试时如果脚本有错误编辑器下方的控制台会直接报出红色错误信息。我记得第一次写完手滑把Sup.Time.warpedDeltaTime拼错了Play 状态下方块完全不动。控制台显示一行提示我改完保存还没来得及重新点 Play方块自己就能动了。这个热更新能力让原型的修改感非常轻快。运行过程中需要注意一点如果按键盘没有反应先点一下游戏画面区域让它获得浏览器焦点。这种问题听起来特别基础但真实踩过很多回——编辑器窗口本身可能会拦截按键输入焦点不在游戏画面上的时候键盘事件到不了你的游戏逻辑。5. 常见问题与排查实录5.1 启动阶段最容易踩的坑启动阶段我遇到最多的有两个问题。一个是 npm install 报ERR_OSSL_EVP_UNSUPPORTED原因前面已经讲过高版本 Node 的 OpenSSL 模块不再默认支持老哈希算法。解决办法就是设置NODE_OPTIONS--openssl-legacy-provider。如果你一直用 Node 18 或更高版本建议直接装个 Node 16 的版本专门跑它一劳永逸。另一个问题终端显示端口 42370 已被占用。这种情况常见于以前启动过别的服务或者之前跑过一次 Superpowers 没有关干净。我一般用命令行查端口占用先确认是哪个进程占着端口再释放。比如在 Windows 上netstat -ano | findstr 42370看到 PID 后再决定是停掉那个进程还是给 Superpowers 换个端口。启动时看到 42370 和 42371 同时监听才算真正跑通。5.2 脚本和场景层面的诡异问题场景里方块没显示先检查两个东西有没有挂 Sprite Renderer 组件以及场景中有没有 Camera。很多新手在 Unity 里习惯先做对象忘了摄影机结果物体在但没视角看见它。还有一种是 Actor 的坐标跑到太远处去了在场景视图里双击 A 键或者搜索场景内的 Actor把它拉回原点附近。脚本拖不上去也要分情况排查。如果脚本资源图标变成红色通常代表代码编译不过。常见的错误包括类没有正确注册、字段类型写错、或者调用了不存在的 API。Superpowers 控制台会具体指出第几行有问题。我建议先看控制台的报错信息再回代码编辑器排查。如果脚本一切都好但拖上去之后没有任何反应很可能是 Behavior 组件没选中正确脚本或者脚本里自己写了一个空的update()却没有实际逻辑检查一遍字段名就能定位。5.3 多人协作时的同步问题第一次邀请同事协作时我遇到过“对方能打开编辑器但看不到我的场景改动”的情况。仔细排查基本都出在 WebSocket 端口 42371 没通。索引页能打开是因为 HTTP 端口 42370 能访问但资源、场景数据的实时同步依赖 42371这个端口不通界面就卡在初始化状态或者同步中断。我的建议是局域网协作时先把 42371 端口是否可访问验证清楚。可以换台电脑直接访问http://主机IP:42371如果连不通检查机器的本地防火墙、云主机安全组规则。另外如果改动了服务端所在的网络环境别忘了一起检查这两个端口一个通了另一个没通的情况真的会让人头大。还有一点要提前说清楚编辑器层面的多人协作不等于游戏运行时本身也支持多人玩家。Superpowers 提供的“协作”是针对制作过程游戏代码里的多玩家逻辑仍然需要你自己通过网络协议实现。不要把编辑器协同误当成游戏服务器框架那样会走很大的弯路。5.4 我给新手的操作建议装完跑完第一个 Demo 之后下一步做小游戏时我有几个比较具体的建议。第一先创建 Input Mappings把移动、跳跃统一成轴脚本里少用具体键名这会让你后期改键位、接手柄都轻松很多。第二脚本字段多用英文、命名具体反正 Inspector 面板里会显示字段名好的命名会让美术同学也能看得懂。第三优先把资源和逻辑解耦只需要换掉 Sprite 资源不用改代码就能换角色外观原型迭代会非常顺。最后再分享一个小技巧用 Superpowers 做原型时把场景里临时创建的测试 Actor 和正式 Actor 分开命名比如测试用Test_Plane正式用Plane_Main。多人协作时统一命名的场景能少很多误会。刚开始用的时候我还习惯性把东西全堆到默认 Actor 上后来项目内容多了到处找对象成了最大开销。命名规约这件事越早开始做越省心。