看到 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 没有这些重流程。我记得第一次启动时,整个项目文件夹加依赖也就几百 MB,npm 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 上,后来项目内容多了,到处找对象成了最大开销。命名规约这件事,越早开始做越省心。