Gandi 3.0 · 按游戏引擎的样子重排的 Scratch 编辑器

让 Scratch真正成为生产力工具

面板任意停靠,几个角色的工作区同时打开,积木在角色之间直接拖;一个作品好几个世界,两万块积木照样丝滑。

不用安装,浏览器打开就能用 · 读写标准 .sb3 · 兼容 Scratch 3 全部扩展
Gandi3.0 文件 ▣ 主场景 ▾
布局:默认布局 ☼ ⚙ 就绪
角色
舞台backdrop1
角色1造型1跨场景
Pico造型2
ScratchCatcostume1
角色角色1
x0y0大小100方向90
积木栏(角色1)
动作
外观
声音
事件
控制
侦测
运算
变量
自制积木
事件 当绿旗被点击
工作区(角色1)造型(角色1)声音(角色1)资源
舞台
MooPico
工作区(Pico)
工作区(Pico)
0单个角色 两万块积木,平移缩放 60 fps
047 MB 的作品,从选文件到能干活
0一万个克隆体 各跑各的脚本,稳 60 fps
0场景积木,一个作品好几个世界
它解决什么问题

Scratch 的界面是给「一个小作品」设计的

一张舞台、一列角色、一个工作区,够用而且好懂。但当作品长到几十个角色、几万块积木、要分好几关的时候,这套界面会开始拦着你。

你想做的事原来只能Gandi 3.0
对照两个角色的脚本来回点角色同时开两个工作区,积木直接拖过去
做多关卡全塞进一张舞台,靠变量硬分每关一个场景,各有各的角色和脚本
找一块素材一个个角色点进去翻资源库面板,全作品的造型声音一张表
编辑几万块积木的角色卡到动不了平移缩放稳定 60 fps
把界面摆成自己顺手的样子没得摆面板任意停靠、浮动、贴边,存成布局
根本区别是「多编辑目标」

可以同时打开好几个角色的工作区、造型区、声音区,并且能把积木从一个角色的工作区直接拖到另一个角色的工作区里。积木栏全局只有一个,独立成面板。

编辑器外壳

面板即窗口

整个界面是一套完整的停靠系统,不是固定三栏。任意面板都能拖成标签、拖进任何一格、拖出来变成浮动窗口。点下面的布局切一切。

Ctrl + / 切布局
Gandi3.0文件布局:默认布局
角色
角色1造型1
积木栏
全局唯一 · 三种摆法
工作区(角色1)
工作区(Pico)
舞台
资源
全作品素材一张表
终端
$ 扩展日志在这里
浮动窗贴边:铺满 / 左半 / 右半,把标题栏拖到边缘 14px 以内 关掉再打开,真的放回原处 最大化盖住整个编辑器 布局可存、可命名、可排序
真实录屏:把面板拖出来变成浮动窗,再贴回边上
真实录屏:两个角色的工作区并排,积木直接拖过去
多编辑目标

几个角色的工作区同时打开,积木直接拖过去

同一页可以开好几个积木工作区,它们靠事件互相重放保持一致。从 A 角色的工作区把积木拖进 B 角色的工作区,拖动时另一边显示跟随指针的影子。

  • 1积木栏全局只有一个,是能拖到屏幕任何地方的独立面板,不再假设它贴着工作区左边。
  • 2造型区、声音区也能开多个,对照着改两个角色的造型。
  • 3悬停铺开:鼠标放上去,长积木完整铺出来,不会被面板边缘截断。
Gandi3.0两个工作区
角色1 · 工作区
角色2 · 工作区
多场景

一个作品,好几个世界

原来菜单、第一关、Boss 战、结算全挤在一张舞台里,每个角色都得自己判断「我这一关该不该出现」。场景把这件事拆开:每个场景是一个完整的小作品,自己的舞台、角色、脚本、造型、声音、变量。

Gandi3.0▣ 主场景 ▾00:00
舞台
菜单
GANDI QUEST
开始游戏继续设置
关卡 1 · HP 100
BOSS · HP 1200
收到「开战」
场景
菜单入口 · 3 个角色
在场待收 1
第一关12 个角色
卸载待收 1
Boss 战9 个角色
卸载待收 1
结算4 个角色
卸载待收 1
工作区(主场景)
当绿旗被点击 预加载场景 Boss 战 在后台运行场景 第一关 切换到场景 第一关,参数 关卡 = 1 向场景 Boss 战 广播 开战 显示场景 Boss 战 卸载闲置场景
点绿旗,看这段脚本怎么把四个世界串起来。

卸载

不在内存里。纹理和解码后的音频都放掉了,存盘一个字节都不丢。

休眠

装着但没在跑。送给它的广播不会丢,排队等它下次开始。

在场 / 后台

在跑。可以在画面上,也可以在后台先把下一关算着,前台放过场动画。

场景参数:值跟着切换过去,那边用具名椭圆接住
跨场景角色:全局只有一份实例,位置、造型、变量不会被重置
场景变量:「仅本场景」是真的私有,切走之后根本不存在
导入 / 导出:一个场景整个导出成 .sb3,能直接丢进 Scratch 打开
编辑不被打断:代码里切场景时,你正在编辑的那个不会被拽走
39 块积木:最常用的七块铺在最上面,其余分五组默认折起
作品级资源库

全作品的造型和声音,一张表

它不是新的存储层,而是给已经按 md5 存着的素材一个作品级身份。树 / 网格 / 详情三栏,一个条目,多个用法。

Gandi3.0资源 · 造型 1941 · 声音 73
资源
全部 2014
造型 1941
声音 73
共用 37
未使用 12
按角色
角色1
Pico
ScratchCat
小兵
全部 · 2014 项
名字造型1.svg
md53b9d82…f47c
大小122 × 199 · 14 KB
用法3 处共用
角色1 · 造型 1已更新
Pico · 造型 2已更新
小兵 · 造型 4已更新
这个造型被 3 处共用

角色1、Pico、小兵 都在用它。改动要影响哪些?

一个条目,多个用法。

共用与分叉的规则是明确的

复制角色、复制场景、把造型拖给别的角色,共用同一份;复制造型、「改为独立副本」,独立一份。

改之前问一次

在画板里改一个被多处共用的造型,会问:全部更新,还是只改这个角色。

未被引用的也保存

「未使用」筛选能看到并清理,造型条、声音条、角色列表上有角标,一眼看出共用了 N 处。

积木

积木栏是平台能力,不是扩展的许可

折叠、可扩展参数、自制积木的形状,这些都是编辑器给的,第三方扩展一句声明不写也一样能用。下面三个都能点。这里的每一块积木都是 gandi-blocks 渲染出来的。

可扩展参数 expandable

积木上那对 ➕➖。一块积木带几个参数由用户说了算,扩展在 getInfo() 里写一段 expandable 就有了,不用碰 Blockly。

切换到场景 第二关,参数 关卡 = 2
点这两个,或者积木上那对

命令块、报告块、布尔块、C 型积木、带参 hat 都支持。

可折叠分组 fold

分类 → 组 → 组里的小节,三级。任何一条标题都能点、能收;扩展写的 |fold-closed 只决定初始状态。

折叠是用户的权利,不是扩展的许可。

自制积木 procedures

三种形状:指令 / 报告 / 布尔。参数可以是下拉菜单,不只是输入框。改形状会自动重建所有调用块,并把用户插进去的积木搬过去。

定义 跳跃 高度 方向 跳跃 10 向上

表单是纯 React 的对话框,不是在对话框里再开一个 Blockly 工作区。

框选批量拖,能选串中间的:A-B-C-D 里框住 B、C 拖走,A 自动接上 DCtrl + 框选
拖着复制,走的是副本Alt + 拖
复制粘贴删除,右键菜单里也有Ctrl+C Ctrl+V Del
下拉可搜索,点开多一个输入框,打字筛选插件
输入框增强:换行、长文本展开成编辑面板,行号、软折行、等宽字体插件
跳到定义,自制积木调用块上右键,认得多工作区右键
积木栏网格模式:分类条横在上方,积木多列流式排,按文字搜三种摆法
注释存的是 Markdown,不是 HTML.sb3
造型编辑器 paint2

不是 SVG DOM,是 Canvas 2D

旧编辑器撞的墙和积木编辑器是同一堵:几千个 <text> 一动就要重排。所以这里场景图是不可变的、命中测试自己在文档坐标里做、视口就是一个矩阵。无限画布,平移缩放是矩阵乘法。

钢笔 / 路径编辑 布尔运算 并 / 交 / 差 变形 warp 路径特效 吸附:网格 / 对象 / 路径 组件 / 符号 文字转路径 沿路径排文字 描摹:位图转矢量 矢量位图混排 序列帧时间轴 洋葱皮 补间

造型就是帧

时间轴上每一格是一个造型。洋葱皮把前后帧淡色叠上来,按旋转中心对齐;补间在帧与帧之间自动插值。画出来的曲线和播放实际用的曲线逐点相同。

8 fps循环洋葱皮 · 补间1 / 6
1
2
3
4
5
6
paint2 造型编辑器:路径编辑、图层、序列帧时间轴
声音编辑器 sound2

不是「剪一段波形」,是一套完整的音频工作台

四种形态:波形 / 频谱 / 编谱 / 音色。多轨编排、钢琴卷帘、混音台、合成器与 SoundFont;编好的谱子能变成积木,积木也能读回谱子。

← 声音库 / ♫ Deltarune - Rude Buster1:1:000 / 57:1:000 · 小节:拍:细分
1 Electric PianoMS
2 2023 Drum KitMS
3 Electric Drum KitMS
4 StringsMS
5 BrassMS
6 Electric Piano 2MS
7 Bass GuitarMS
8 8-Bit SawtoothMS
9 909 Drum KitMS
10 2013 Drum KitMS
11 2023 Drum KitMS
12 Electric Piano 4MS
13 8-Bit SquareMS
14 Brass 2MS
13579111315
大钢琴
标准鼓组
标准鼓组
低音提琴
小号
大钢琴
原声贝斯
锯齿波主音
标准鼓组
标准鼓组
标准鼓组
大钢琴
方波主音
小号
120 bpm4/4混音 · Electric Piano音量声像混响 · 干湿渲染音频
波形降噪、修复、基频检测、变速不变调、循环点查找、静音裁剪、包络
频谱STFT 跑在 Worker 里,主线程不掉帧
编谱编排视图、钢琴卷帘、自动化包络、和弦面板、音阶、量化、混音台
音色合成器 / 采样器 / SoundFont、鼓机、MIDI 导入导出、WebMIDI 外接键盘
sound2 编排视图:二十条轨道、彩色片段与混音台
舞台

按它真正被显示的尺寸测量和绘制

4:3、16:9 或自定义尺寸;三种动态尺寸模式:固定 / 宽度跟面板长 / 高度跟面板长。做宽银幕或竖屏不用改积木。

  • 1页内全屏:在页面内部铺满窗口,不问浏览器要全屏,所以舞台不会被拉糊。
  • 2切换场景时交叉淡入淡出,按缓冲尺寸抓帧。
  • 3变量监视器、提问框层级固定,扩展加的覆盖层不会压住它们。
得分 1280
Moo480 × 360
代码文件与终端

代码挂在作品上,跟着 .sb3 一起走

JavaScript / Python / JSON / GLSL,Monaco 编辑器。终端接管 runtime.logSystem 六个口子,扩展的 console.log 在这里看得到,出错会自己弹出来。

  • Quick Open,按名字跳文件。
  • >_xterm 终端,扩展日志不再散落在浏览器控制台里。
Gandi3.0文件scripts / glitch.js
文件
▾ scripts
glitch.js
enemy-ai.py
▾ data
levels.json
▾ shaders
wave.glsl
README.md
glitch.js×
scripts › glitch.js
终端问题输出🗑
⎇ mainJavaScriptUTF-8LFLn 1, Col 1● 60 fps
扩展生态

贴一个集市页面地址就能装

背后走 CCW 的公开接口取真实 js 地址;另有 ScratchX 兼容层。装进 VM 之后,再出现在积木栏里。

集市页面地址贴一个链接
js 文件地址远程扩展
ScratchX兼容层
scratch-vm加载 → 积木栏里出现分类 → 折叠、可扩展参数照样能用

Scratch.gandi:给扩展的宿主接口

想在舞台上放 <iframe> 的扩展,过去只能满页面遍历去认哪个 canvas 是舞台,再猜它的父节点插进去;每换一次布局这套猜法就断一次。现在直接要一层:画布不动、监视器不动,你也不用知道编辑器的 DOM 长什么样。

选项默认含义
scalefalse内部坐标系固定成舞台原生尺寸,缩放交给编辑器
interactivefalse接收鼠标事件(会挡住那块舞台,所以默认穿透)
name写到 data-overlay 上,方便排查

编辑器没提供舞台服务时(无头播放器、单测、别家编辑器),这几个方法返回 null 并且只抱怨一次,不抛异常。

extension.js
// 问功能本身,能力探测不会随版本失效
if (typeof Scratch.gandi?.stage?.addOverlay === 'function') {
  const layer = Scratch.gandi.stage.addOverlay({
    scale: true,
    interactive: true,
    name: 'my-hud'
  });
  layer.appendChild(myIframe);
}

// 折叠 + 可扩展参数,在 getInfo() 里声明就有
blocks: [
  '---基础',
  { opcode: 'foo', /* … */ },
  '---高级用法|fold-closed',
  {
    opcode: 'switchToWithParams',
    text: '切换到场景 [SCENE],参数 [EXPAND]',
    expandable: {
      slot: [
        { name: 'KEY',   type: ArgumentType.STRING, defaultValue: '关卡' },
        { text: '=' },
        { name: 'VALUE', type: ArgumentType.STRING, defaultValue: '1' }
      ],
      between: ',', min: 1, max: 10
    }
  }
]
选择特定积木Ctrl 框选批量拖、Alt 拖复制、批量复制粘贴删除
下拉可搜索下拉菜单能搜;输入框里打字给变量和报告块候选
积木输入框增强换行、长文本展开成编辑面板
跳到定义自制积木调用块右键跳定义,认得多工作区

两种插件,同一套契约 {id, name, setup}。内置插件跟编辑器一起构建发版,设置里一个开关;外部插件填一个网址装进来。Patcher 提供 before / after 钩子并支持 unpatch,多个扩展 patch 同一个方法不再层层套娃。

性能

编辑几万块积木不卡,运行一万个克隆体不掉帧

这是投入最大的一块。所有数字都是同一台机器、同一套脚本、前后两棵源码树背靠背测出来的,方法和复现步骤在仓库里的两份报告里。

同类编辑器Scratch 3 原版引擎 · 未做这些优化
VS
Gandi 3.0同一台机器 · 同一套脚本 · 同一个作品
0× 更快滚轮缩放帧时间
1679 ms17.3 ms
19899 块积木的角色
0× 更少DOM 节点数
106,6903,345
看不见的脚本从文档里移走
0× 更快打开 47 MB 的作品
50 秒以上4.7 ~ 5.6 秒
1941 个造型、73 段音频、25600 个积木
帧时间 · 实时同一个 19899 块积木的角色,来回平移缩放。每一根柱子是一帧,白线是 16.7 ms 的预算。
同类编辑器7 fps139 ms
Gandi 3.060 fps16 ms
同类编辑器Gandi 3.0

几万块积木的工作区

真实项目,单个角色 19899 个积木 · 帧时间中位数
平移
139.7 ms16.7 ms
快 8.4×
滚轮滚动
149.8 ms15.1 ms
快 9.9×
滚轮缩放
1679.1 ms17.3 ms
快 97×
放下到下一帧
4471.6 ms330.6 ms
快 13.5×
加载耗时
11.8 s6.8 s
快 1.7×

一万个克隆体,各有各的线程

全部绘制 · scratch-vm 编译器 + scratch-render 批量绘制
帧率(上限 120)
38.2 fps97.6 fps
快 2.6×
帧时间 中位数
24.3 ms9.9 ms
快 2.5×
其中渲染
13.6 ms3.1 ms
快 4.4×
超预算的帧
100%2.4%
少 41×
光线追踪器 每帧耗时
245.4 ms80.3 ms
快 3.1×

积木编辑器 gandi-blocks

  • 视口裁剪:看不见的脚本从文档里移走,不是 display:none,Blink 对隐藏的 SVG 子树照样跑文字排版
  • 文字层:字段的文字画在工作区共享的一层上,不再是每个字段一个 <text>
  • 平移缩放跑在合成层上,手势期间不重排;文字测量用 canvas,不触发布局刷新
  • 连接点数据库早退:没动的连接点不再做两次 O(n) splice

运行时 scratch-vm · scratch-render

  • 数值变量槽:只写过数字的变量挪进 V8 会拆箱的小对象
  • 自制积木内联、常量参数特化、常量折叠,语义是 Scratch 的不是 JS 的
  • startHats 按 key 直接找线程,线程表增量维护
  • 批量绘制:一万个同造型克隆体变成一次 draw call

打开 47 MB 的作品

  • 5 个角色、1941 个 SVG 造型、73 段音频共 47 分钟、25600 个积木
  • 从选文件到能干活:50 秒以上 → 4.7 ~ 5.6 秒
  • 关键是造型等到有人要看它再变成图片:一个角色同时只穿一件造型,另外 1935 件从头到尾没人看

诚实的一处退步

  • 三千个克隆体的生成从 89 ms 变成 111 ms:变量提升成槽要定义访问器
  • 一次性成本,之后每帧省 0.68 ms,二十帧就回本
  • 到一万个克隆体时生成反而快 2.2 倍,因为原来那个二次方的 hat 扫描在主导
快速开始

打开浏览器就能用

不用装任何东西。打开下面的地址,拖一个 .sb3 进去,或者从零开始。源码整理好之后会开源,敬请期待。

打开地址桌面版 Chrome / Edge 最新版体验最好
拖一个 .sb3 进去,或者新建Scratch 3 的作品原样打开,扩展照常加载
把界面摆成自己顺手的样子面板拖成标签、拖出浮动窗,存成布局
存回标准 .sb3多场景作品在旧编辑器里只看得到入口场景
读写标准 .sb3也能导入 .sb2。同名全局变量导入时自动并到一起。
场景导出可直接丢进 Scratch一个场景整个导出成 .sb3。
Scratch 3 全部扩展可用官方扩展、CCW 集市、ScratchX。
多场景作品在旧编辑器里只看得到入口场景;在旧编辑器保存会丢掉其它场景。
Gandi 3.0 · 公测 gandi.kuke.ink/editor

随时可能更新;作品记得存到本地。

源码开源 · 敬请期待
Moo Gandi 3.0

按游戏引擎的样子
重排的 Scratch

给那些已经长大了的作品。