用 Scratch 编 FNF 模组 Pluto's Reprisal Part4,镜头移动和放大缩小这种效果都能做出来,这件事值得好好拆一遍。很多人一听 FNF 模组,第一反应是去改原版游戏的资源包,但用 Scratch 做模组完全是另一条路:不用装引擎,不用碰代码文件,所有逻辑都用积木拼,核心难点不是“能不能做”,而是怎么把镜头变化、节拍同步、角色出场这些时序理顺。
这个主题适合两类人看:一类是想做音游模组但还不熟悉 Unity、Godot 这类引擎的爱好者;另一类是已经在用 Scratch 做小游戏,想加入镜头动效但不知道从哪下手的玩家。我这次围绕 Pluto's Reprisal Part4 的实际开发过程,把镜头移动、放大缩小的实现思路、边拍边修 bug 的流程、以及真正会卡住你的那些坑点都整理出来。
1. 用 Scratch 做 FNF 模组,到底是在做什么
1.1 音游模组的核心不是画面,而是节奏和状态切换
FNF(Friday Night Funkin')本身是节奏游戏。玩家跟着音乐,在上下左右箭头到达判定线时按下对应方向键。所谓模组,通常是把原版角色、背景、歌曲、谱面替换成新内容。用 Scratch 做模组,不是去改原版游戏文件,而是用 Scratch 重新搭一个玩法接近的音游成品。
既然玩法是音游,那整个游戏的核心就两条:音符掉落和判定是否准确,画面变化和音乐节拍是否对得上。镜头移动放大缩小属于第二条里的表现层,它是锦上添花,但也是很容易翻车的地方。
很多人在 Scratch 里做音游,先去做角色动画、特效、背景,结果音符判定和音乐对不上,后面全白搭。正确顺序应该反过来,先把节奏框架跑稳,再往上加镜头变化。Pluto's Reprisal Part4 能把镜头移动放大缩小做出来,说明底层的时间线已经先立住了。
1.2 相比 Unity、Godot 等引擎,Scratch 的优势和短板
Scratch 做这种项目,优势非常明显:
- 不需要安装复杂引擎,打开网页编辑器或者离线编辑器就能开始。
- 不用写代码,逻辑全部靠积木拼接,适合没接触过编程的人。
- 事件处理很直观,比如“当收到广播”“当按下方向键”,天然适合做按键响应。
- 调试时可以随时点绿旗重启,改一个积木就能立刻看到效果。
短板同样明显:
- 性能上限低。克隆体多、素材大、特效复杂时很容易卡顿。
- 音频和画面不同步。Scratch 播放声音时没有一个稳定可靠的“当前播放到哪一毫秒”的接口,这让音游对拍特别麻烦。
- 舞台大小和图层机制有限。复杂场景、多层视差、精细特效表现力不如引擎。
- 发布和分享受限。在线项目对素材体积、运行环境有更多不确定性。
所以如果你只是想把一个 FNF 模组做得“能玩、像样、能给别人点开试试”,Scratch 完全够。如果你想做成长型作品,加入复杂过场、精确判定、大量特效,那就得认真做资源管理和性能取舍。
2. 镜头移动和放大缩小:我先拆成三件事
2.1 坐标更新:镜头位置本质是“所有舞台元素偏移”
Scratch 里没有摄像机对象,所以“镜头移动”要用变量模拟。我当时给整个工程定义了一套虚拟相机变量,比如 camera_x、camera_y、camera_zoom。
这套变量的含义是:摄像机现在盯着世界里的哪个点,以及当前画面放大到了多少倍。舞台上所有角色,实际显示位置都需要基于这套变量重新计算。
举个例子。一个角色的世界坐标是 world_x、world_y,那么它在屏幕上的显示坐标大致是:
屏幕x = world_x - camera_x 屏幕y = world_y - camera_y当 camera_x 从 0 变成 200,所有角色的屏幕 x 都会往左偏移 200,看起来就像镜头向右移动了。这是镜头移动最基本的原理。
但这里有一个很关键的问题:Scratch 的角色位置是直接通过“移到 x/y”积木设置的,你要么把所有元素都按这套公式手动计算,要么把角色塞进同一个“图层组”里统一管理。我建议使用前面的方式,虽然计算量变大,但后期调整单个角色非常方便。
2.2 缩放:用造型大小和图层距离模拟镜头变焦
放大缩小的实现比移动麻烦一些。Scratch 本身没有“镜头缩放”功能,你能控制的只有每个角色的“大小”。所以我的做法是:
当 camera_zoom 改变时,每个元素的最终大小 = base_size * camera_zoom。
这听起来简单,实际上有三个坑:
第一个坑是缩放中心。角色放大后,默认是围绕造型中心点缩放。如果镜头中心不是角色中心,视觉上就会“偏”。需要给每个元素定义一个统一的锚点,比如角色脚底中心、舞台中心点、背景左上角。缩放时先按锚点重算位置,再设置大小。
第二个坑是位图模糊。如果素材是位图图片,放大后会糊成一片。解决办法是素材尽量用矢量造型,或者把原图做大一点,缩小显示,给放大留出余量。网上找的素材想直接在 Scratch 里用,最好先确认格式和分辨率,不要直接导入一张 200×200 的小图就指望放大了还清晰。
第三个坑是缩放和移动的联动。镜头缩放时,元素不仅大小会变,位置也必须跟着变。如果只改大小不改位置,就会出现角色原地变大、背景没跟着动的怪效果。
2.3 平滑过渡:不要直接跳,而是用补间计数
镜头最忌“跳变”。camera_x 从 0 直接变成 200,画面会“唰”一下瞬移过去,非常生硬。要做出“镜头滑过去”的感觉,需要用补间逻辑。
我当时用的是很简单的插值法,每帧把当前值向目标值移动一部分:
camera_x 增加 ((目标x - camera_x) * 0.1)这种写法会让镜头一开始快、后面慢,有种缓出效果。如果想要更精准控制时间,可以用一个过渡进度变量:
过渡进度 = 过渡进度 + 0.02 如果 过渡进度 > 1,那么 过渡进度 = 1 camera_x = 起点x + (目标x - 起点x) * 过渡进度 camera_y = 起点y + (目标y - 起点y) * 过渡进度 camera_zoom = 起点缩放 + (目标缩放 - 起点缩放) * 过渡进度用这种写法,你能精确知道镜头在某一帧处于什么位置,排查问题时会方便很多。纯靠“增加 0.1 倍距离”虽然写起来快,但镜头到没到目标、什么时候到,不好判断。
我一般会先做移动,再做缩放,最后把两者合并到同一条过渡逻辑里。不要一开始就同时上移动和缩放,出 bug 你都分不清是位置问题还是缩放问题。
注意:补间系数不要设太大。有人为了让镜头“更有感觉”,把系数设成 0.3、0.4,结果镜头像被弹簧拽着一直乱晃。对新手来说,系数在 0.08 到 0.15 之间比较稳妥。
3. 实际制作顺序:先单镜头,再接谱面,再对拍
3.1 第一步:只做一条轨道和一个镜头脚本
我开始做 Part4 的时候,没有一上来就铺满整个歌曲。我先建了一个只有一个音符的测试谱面,然后在这个基础上调试镜头。
整个过程分成三块:
- 音符生成模块:负责在特定时间生成左右上下箭头,并按节拍向下移动。
- 判定模块:负责检测按键是否在判定区域内按下。
- 镜头模块:负责根据歌曲进度切换位置、大小和过渡效果。
第一步先把这三个模块的“空壳”搭出来,也就是让角色出现在舞台上、音符能下落、角色能播放一个 idle 动画。镜头模块先放一边,只有 camera_x、camera_y、camera_zoom 三个变量和默认值。
为什么要先这样?因为镜头只是表现层,它必须依附在稳定的逻辑层之上。音符下落都卡顿的话,镜头再炫也没有用。
3.2 第二步:让镜头变化绑定到歌词或节拍时间点
搞定基础谱面之后,再让镜头“动起来”。镜头的触发时机不能靠人为目测,得有明确的时间点。
Scratch 没有稳定读取音频播放位置的积木,所以我采用了一个“帧计数器”思路:绿旗启动后,每帧把变量 tick 加 1,然后根据设定的帧率估算歌曲时间。比如目标帧率是 30,那么 tick 到 60 大约就是 2 秒。
然后用条件广播控制镜头:
如果 tick = 90,那么发送广播“镜头切近景” 如果 tick = 210,那么发送广播“镜头拉远”这种方式虽然粗糙,但胜在可控。只要歌词、谱面和镜头全部挂在同一个 tick 时间轴上,就算稍微有偏差,也能通过微调 tick 触发值来解决。
如果有条件,更稳的方式是让音乐文件自身带节拍点,比如先把歌曲在音频软件里标出段落位置,再把这些时间点转换成 tick 参考值。不要靠“感觉差不多”去对时间,那样后期一定会返工。
3.3 第三步:加入角色出场和背景切换,最后再叠特效
谱面和镜头对拍稳定之后,才开始叠加其他内容。
角色出场的标准做法是:先让角色造型隐藏,到指定 tick 时切换造型并显示,同时播放一段入场动画。入场动画可以通过连续切换几个造型来实现,也可以用“重复执行”配合位置变化实现滑入效果。
背景切换同样挂在 tick 上。Part4 的场景变化比较多,我在切换背景时给背景加了一个透明度和大小渐变,让过渡不那么生硬。
最后才做特效,比如音符命中后的闪光、连击数字、背景粒子。特效是最吃性能的部分,后面再提怎么控制。
这里我踩过一个教训:千万不要在镜头过渡还没结束的时候同时触发背景切换和角色入场。多个动画叠加时,如果都用了不同的计时器,很容易出现角色已经到位了、背景还在缩放、镜头又重新开始的混乱局面。我的做法是:把镜头过渡、背景切换、角色入场全部统一到同一个进度变量,也就是同一个过渡公式里。
4. “边拍边修 bug”的正确打开方式
4.1 先把“复现”变成可重复操作
“边拍边修 bug”听起来像自嘲,其实是非常高效的开发状态。镜头动效这种视觉项目,你很难光看代码逻辑就判断“这样会出问题”。最可靠的方式是录制回放,肉眼观察。
我实际的操作流程是:
- 调整某个镜头逻辑。
- 绿旗启动,然后边操作边录屏。
- 回放录像,看具体在哪一帧出现了问题。
- 根据问题修改积木,再重新录制对比。
这里最重要的是“复现可控”。如果 bug 只在某种操作顺序下出现,那你就必须每次都按相同顺序操作。把输入固定下来,不要靠随机。比如测试时音符下落速度、镜头目标位置、触发 tick 都写死,不要每次都不一样。
Scratch 里虽然没有传统日志系统,但可以借助“列表”来记录关键事件。我习惯在镜头移动时把当前 tick、camera_x、camera_y、camera_zoom 追加到一个列表里,等跑完一遍后查看列表,就能判断到底是哪一步出了问题。
4.2 修 bug 的优先级:阻挡游戏的 bug 先修,视觉瑕疵后修
镜头 bug 也分等级。我一般按这个优先级排序:
| 优先级 | 问题类型 | 例子 |
|---|---|---|
| P0 | 阻挡游戏正常运行 | 音符判定失效、游戏卡死、无法返回标题 |
| P1 | 严重视觉错误 | 镜头飞到舞台外、缩放后角色错位、背景重叠 |
| P2 | 轻微视觉瑕疵 | 某一帧角色跳了一下、特效闪得不自然 |
| P3 | 可优化项 | 镜头缓动不够顺滑、背景层次感不足 |
如果同时出现多个问题,先从 P0 开始修。镜头乱飞虽然很显眼,但如果音符判定和声音错位了,那才是真正要命的。修完 P0,再看 P1,P2、P3 可以留到最后统一处理。
4.3 录制回放和日志:怎么判断是画面问题、逻辑问题还是素材问题
边拍边修的时候,最怕的是不知道问题出在哪一层。我常用的判断链路是:
- 先看画面是否按预期动了。如果画面没动,检查镜头变量是否在变。
- 再看变量是否在变。如果变量在变但画面不对,说明是坐标计算、图层或大小设置的问题。
- 如果变量根本没变,说明是触发条件没满足,比如广播名字写错、tick 时间点不对、条件分支被跳过。
- 如果触发条件也正确,但还是有问题,查积木顺序。Scratch 里积木是按顺序执行的,有时候你把“将大小设为”放在“移到 x/y”前面和后面的效果完全不同。
- 最后看素材。角色显示异常时,先检查造型名称是否匹配、素材中心点是否合理、图片是否是透明背景。
这一套顺序能排除大部分问题。不要一上来就去怀疑“内部 bug”,很多问题都是素材、触发条件、重置状态没处理好。
5. 我在 Part4 里实际踩到的问题和排查顺序
5.1 镜头突然飞到舞台角落
这个 bug 在开发过程中非常常见。表现形式是:镜头过渡到一半,突然“嗖”一下跑到舞台左下角或者右上角,然后永远回不来。
排查之后发现两种主要原因:
一种是因为局部变量和全局变量重名。Scratch 里的变量可以区分是“仅适用于当前角色”还是“适用于所有角色”。如果某个角色内部有一个和全局变量同名的局部变量,很容易互相覆盖。
另一种是因为镜头过渡循环没有在适当的时候停止。比如角色到达目标位置后,过渡循环还在继续运行,把 camera_x 越拉越远。这种情况要加一个“过渡结束”标志,到达目标值后立刻停止循环。
5.2 缩放回调之后角色位置错位
放大后拉远,角色应该回到初始位置,结果它偏了。这个问题我排查了很久,最后发现是角色的造型中心点不一致。
Scratch 中,一个角色可以拥有多个造型,每个造型的图片中心点可能不一样。镜头缩放对位置的计算默认使用当前造型的锚点。当角色在镜头放大过程中切换了造型,中心点变了,位置自然就偏了。
解决办法是:所有角色的所有造型中心点必须统一,最好都用脚底中心作为锚点。或者给每个角色单独编写“重新定位”积木,在镜头缩放结束后强制将角色放到正确坐标。
5.3 音符判定区域和背景发生分离
镜头放大缩小后,背景和音符判定线位置不统一。音符还在原来的判定线位置,但背景已经偏移了,导致玩家按方向键时感觉“明明按了才对,实际判定却不对”。
这个问题的根源通常是:音符的生成坐标和背景的坐标没有使用同一套世界坐标。如果用 Scratch 默认的舞台坐标来生成音符,而背景却基于虚拟镜头坐标计算,一旦镜头移动,两者就会分离。
修复方式:把音符、角色、背景、判定线全部统一纳入虚拟镜头坐标系。判定线不要放在固定舞台位置,而是和音符一样,根据 camera_x、camera_y 实时计算显示位置。
5.4 卡顿、掉帧和声音不同步
卡顿在 Scratch 音游里比任何功能 bug 都难处理。掉帧会直接影响音符下落速度,进而影响判定。
我遇到的卡顿主要集中在三处:
- 克隆体太多。音符生成使用了克隆体,每个克隆体每秒还要更新一次位置。如果当前画面上有 20 个克隆体以上,低配电脑就开始吃紧。
- 声音播放重叠。多个声音同时播放时,Scratch 的性能会明显下降。
- 背景图片过大。导入了一张高分辨率背景图,舞台每次渲染都要处理大图片,帧率直线下降。
解决思路:
- 把音符数值降到合理范围,不要一次性显示太多音符。
- 优化素材,图片能压缩就压缩,不要直接用超大原图。
- 减少同时播放的音效数量,可以用变量做“最近一次音效播放时间”,避免同一帧播放多个音效。
- 在测试阶段先关掉不影响逻辑的特效,比如背景粒子、命中闪光。
另外,强烈建议使用 Scratch 离线编辑器进行开发。在线版受浏览器性能影响更大,录制时也容易掉帧。离线版更稳定,也方便备份工程文件。
注意:如果镜头缩放时出现明显卡顿,先检查当前帧是否有多个大型造型同时缩放。你可以把缩放改成“只改重要角色,背景用淡入淡出”来降低压力。
6. 如果你想做类似模组,记住这几条边界
6.1 素材来源与版权
做 Scratch 模组时,很多素材来自网络,包括角色立绘、背景图、音乐、音效。如果是基于 FNF 的歌曲、角色或画风进行再创作,请务必只把它当作学习练习,不要以原作品名义发布或用于商业用途。
网上找的素材要直接在 Scratch 中使用,建议先做三件事:确认格式(最好 png/svg)、确认透明通道、确认分辨率。不要导入后才发现背景是一个白色矩形,遮挡了其他角色。
6.2 性能上限:低配电脑、在线版和离线版
Scratch 项目的性能上限很难量化,因为它依赖浏览器和设备。我的建议是:不要追求复杂的镜头粒子特效,而是把动态控制在“几层背景 + 几个角色 + 几条音符轨道”以内。
我先给一个保守配置参考:
| 项目 | 建议范围 |
|---|---|
| 同时存在的克隆体 | 30 个以内 |
| 同时播放的声音 | 3 个以内 |
| 舞台背景图片单张尺寸 | 不超过 1000×600 |
| 镜头缩放范围 | 0.8 到 1.2 之间 |
| 单个角色造型数量 | 10 个以内 |
如果你的机器配置比较低,这些数值还要再降。能跑通不代表适合长时间玩,更不代表适合直播录制。低配环境下,先把分辨率、克隆体数量和音效数量降下来。
6.3 判断“能玩”和“能发布”的验收标准
一个 Scratch 音游模组,做到什么程度算“能玩”?
我自己的验收标准是:
- 从绿旗启动开始,是否能不卡死地进入游戏。
- 是否能完整打完至少一关。
- 音符判定是否稳定,按键反馈是否正常。
- 镜头移动、缩放是否在预期时间和位置触发。
- 镜头拉回后,音符判定是否恢复正常。
- 失败后是否能重新开始,不会出现状态残留。
- 连续测试三遍,不会出现随机性错误。
如果这七条全部通过,才说明这个模组具备分享给别人玩的条件。如果只能跑出画面、只能看动画,那不叫完成了。
6.4 后续扩展思路:道具、过场、多角色、谱面生成
镜头移动和放大缩小实现后,可以做的扩展还有很多:
- 音符速度变化。某些高能段落把音符下落速度调快,制造压迫感。
- 判定反馈特效。在判定线附近加闪光、放大、变色。
- 多角色切换。不同角色使用不同的攻击反应动画。
- 过场系统。在歌曲段落之间插入对话和场景切换。
- 谱面自动化生成。把谱面数据写进列表,生成音符时直接读取,减少手动堆积木。
但要注意,任何扩展都要保持“一次只动一个变量”。想加道具系统,那就先只加道具,不要同时改镜头和音符速度。否则你根本分不清问题出在哪块。
回到 Pluto's Reprisal Part4,镜头移动和放大缩小能做出来,说明时序管理已经过关了。最开始我也觉得镜头难,后来发现真正难的不是让镜头动,而是让它在正确的时间动、动到正确的位置、结束后还能干干净净地复位。边拍边修 bug 不是效率低,而是视觉开发里最稳妥的验证方式:你把过程录下来,回放时大部分问题都会自己现形。如果只是学习,Scratch 完全够用;如果要长期维护,记得把镜头变量、触发表、素材归档都理清楚,这才是最容易被忽略的一步。