关于 roblox-sfx
为 Roblox 游戏制作声音资源的技能,会搜索 Creator Store 音频,并在 Studio 中测量候选音频后筛选。它生成按角色组织的声音库代码,避免仅凭商店标注时长做判断。
主要功能
- 01按游戏声音角色搜索音频
- 02在 Studio 中测量候选音频
- 03按加载状态和时长筛选
- 04生成声音清单与 Luau 模块
- 05提供试听和安装脚本
适用场景
- 01为游戏中的脚步或音效寻找音频
- 02为环境音乐设定时长范围
- 03生成可按角色调用的声音库
- 04检查候选音频是否可加载
MCP 相关
该技能使用连接的 Studio MCP 执行 Luau 探测和报告脚本,以读取候选声音的加载状态与实际时长。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ syphodev-yt/ roblox-ai-skills --skill roblox-sfx安装后在 Claude Code 中输入/roblox-sfx确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LOCAL.template.mdexamples/spec.example.jsonscripts/emit_bank.pyemit_probe.pyfind_sfx.pypick.py详细介绍
Roblox 声音,按 ID 验证
第一次在这台设备上使用? 请阅读此文件夹中的
LOCAL.md。如果文件不存在,请运行roblox-skills-setup技能(或将LOCAL.template.md复制为LOCAL.md并填写)。LOCAL.md保存你的路径和工具位置;它属于你,绝不会共享。
整个技能基于一个事实,而非假设:
免费的 Creator Store 音频资源可在未发布、未保存的场景中播放,无需 API 密钥、无需拥有资源,也无需上传。
因此,声音不需要 Open Cloud 音频配额(未经验证时每月上传 10 次, ID 验证后为 100 次,且仅支持创建,因此每次重新生成都会消耗一个名额)。它需要的是 搜索和检查。这让音频阶段完全脱离关键路径。
spec.json (roles: what the game needs a sound FOR)
-> find_sfx.py unauthenticated toolbox search -> candidates.json
-> emit_probe.py probe.lua + report.lua
-> Studio create the Sounds, then MEASURE them <- the gate
-> pick.py three gates, then score -> manifest.json
-> emit_bank.py SoundBank.luau + Sfx.luau + install.lua + tour.lua
搜索会产生候选项。Studio 会给出事实。只有事实才能决定选择。
测量了什么,以及何时测量
2026-08-30 在未保存的 Place1 中,通过已连接的第三方 robloxstudio-mcp 对 Studio 进行了实时验证
(placeId 0):
| 问题 | 答案 |
|---|---|
toolbox-service/v1/marketplace/3 需要身份验证吗? | 不需要。 搜索 footstep 返回 200,共 1000 条结果 |
items/details 需要身份验证吗? | 不需要。 提供完整元数据,包括 audioType、艺术家、投票数和 isFree |
| 能否下载音频文件进行分析? | 不能。 assetdelivery v1 和 v2 均返回 401 |
| 未拥有的免费音频 ID 能否在未发布的场景中加载? | 可以。 40 个候选项中有 40 个 IsLoaded = true |
Sound:Play() 能否在编辑模式下工作? | 可以,在 Studio 中能听到,无需进行游戏测试 |
工具箱的 duration 字段可信吗? | 不可信。 一个实际长度为 0.500s 的声音被列为 0;8 个已选音频中有 2 个的列表时长与测量值不符 |
第四行正是选择不能投机取巧的原因。这里没有波形可供分析, 没有频谱图可供查看,也无法靠听觉判断。能用的只有列表信息和测量结果,而 此技能会如实说明仅使用这两者。
决定一切的规则
列表信息是一种说法。Sound.TimeLength 是测量结果。绝不以说法作为门槛。
find_sfx.py 会故意将列表中的时长存为
listed_duration,避免后续流程误将它当作数据;对于两者相差
超过 25% 的任何选项,pick.py 都会记录 listing_was_wrong。首次实际运行时,有 8 个选项中的 2 个触发了该标记。
两次 Studio 调用,绝不能只调用一次
execute_luau 只要脚本发生任何让出执行,就会丢弃所有输出;而且设置
SoundId 的那一帧无法解析 Sound 的时长。因此,探测分为两步:
call 1 probe.lua creates every candidate Sound, returns immediately
(a beat)
call 2 report.lua returns role,id,loaded,length as CSV
如果第 2 次调用后仍有任何项目为 loaded=false,请再运行一次
report.lua,然后再作判断。只有第二次仍失败才算真正失败;这意味着 ID 无法解析、资源已删除,或此场景无权使用该音频。
三道门槛
只有 Studio 按顺序确认以下三项后,候选项才符合条件:
IsLoaded == true— ID 已解析,且场景可以使用该音频TimeLength > 0— 时长为零的声音是损坏的,不是短音频min_len <= TimeLength <= max_len— 符合该角色的时长范围
搜索分数只用于打破平局,而且只在平局时使用。排名最高的列表项若未通过门槛, 一定会输给排名最低但通过门槛的选项。首次运行时,这条规则淘汰了一个 6.55s 的“木门打开”音频,因为对应角色的时长范围上限是 5s。
角色未填充属于失败,不是警告。 emit_bank.py 拒绝生成有空缺的音频库;如需覆盖,必须明确指定 --allow-holes,这意味着有意发布无声事件。
编写规范
每个游戏需要声音的对象对应一条,而不是每个你想象出来的声音对应一条。
{
"name": "farm",
"roles": [
{ "name": "sheep_bleat", "query": "sheep bleat", "kind": "SoundEffect",
"min_len": 0.3, "max_len": 4.0, "volume": 0.5 },
{ "name": "ambience", "query": "countryside ambience", "kind": "Music",
"min_len": 20, "max_len": 300, "volume": 0.2, "looped": true }
]
}
kind为SoundEffect或Music。这是资产上的真实字段,类型不匹配会受到严厉扣分,因为让一首 Music 曲目承担SoundEffect的角色,是把一段九分钟混音当成脚步声的经典做法。- 频段是设计决策。持续时间超过 1.5s 的脚步声就不是脚步声,把这一点写下来,机器就能在不听声音的情况下将其排除。
volume和looped会传递到生成的资源库中。
示例规范见 examples/spec.example.json;复制它并编辑各个角色即可。
运行
cd ~/.claude/skills/roblox-sfx
python scripts/find_sfx.py spec examples/spec.example.json --out work/candidates.json --per-role 5
python scripts/emit_probe.py work/candidates.json --out work
# execute_luau work/probe.lua -> "created N sounds across M roles"
# execute_luau work/report.lua -> save the return value verbatim to work/report.txt
python scripts/pick.py work/candidates.json work/report.txt --out work/manifest.json
python scripts/emit_bank.py work/manifest.json --out work
# execute_luau work/install.lua -> SoundService.SFX, audible in this place
# execute_luau work/tour.lua -> plays the whole bank in order, so a human can judge it
一次性查询,无需规范:
python scripts/find_sfx.py search --query "wolf howl" --kind SoundEffect --limit 20
交付内容
| 文件 | 放入位置 | 用途 |
|---|---|---|
SoundBank.luau | src/shared/Config/ | 仅包含数据:角色 -> id、测量时长、音量、循环状态、署名 |
Sfx.luau | src/shared/ | Sfx.play(role, parent, pitchVary)、Sfx.preload()、Sfx.stop() |
install.lua | execute_luau | 直接构建 SoundService.SFX,适用于没有 Rojo 同步的场景 |
tour.lua | execute_luau | 按顺序播放每个声音,间隔为其实际时长 |
生成的代码会遵循两条规则:
- 资源库之外的代码绝不会引用资产 id。 游戏逻辑代码只写
Sfx.play("wolf_howl", head)。调整音量或替换资产只需修改数据,无需修改代码。 - 声音绝不能影响游戏逻辑。
Sfx.luau中的每个入口点都通过 pcall 隔离,失败时返回 nil。缺少声音不能阻止射击或绵羊移动。
tour.lua 使用 task.delay 而不是通过 task.wait 循环,原因与探测脚本拆成两部分相同:会让出执行权的脚本会丢失输出。
陷阱
列表中的时长不准确。 上文已提及。如果有人“优化”掉 Studio 往返流程,这是最可能导致该流水线交付错误声音的问题。
MCP 上的 search_assets 行不通。 它会返回 HTTP 403 Scope not authorized,改参数名也无济于事。因此,该技能会直接调用工具箱端点,完全不需要身份验证。
你听不到任何声音,模型也听不到。 没有 Cookie 时,assetdelivery 会返回 401,因此无法获取波形、频谱图或响度数据。该技能对声音做出的所有判断都来自列表或 TimeLength。需要凭听感判断时,运行 tour.lua,让人来评估。对于无人值守的运行,过滤条件加上评分就是诚实的上限;这是值得明确说明的结论,而不是该掩盖过去的事。
免费素材包不等于免费音频。 免费的 Creator Store 素材包 必须由人将其添加到账户后才能加载(我们在 VFX 收集器上吃过亏)。按 id 使用的单个音频资产没有这一步,这正是该技能按 id 操作、从不要求任何人添加素材的原因。
探测期间将音量设为 0 是有意为之。 在打开的 Studio 中同时播放 40 个候选声音会让人不适。probe.lua 会设置 Volume = 0;audition.lua、install.lua 和 tour.lua 则会设置实际音量。
Roblox 自带的素材库会获得有意设置的加分。 Creator id 为 1 的资产(形如 91207… 的 id)质量稳定、始终获准使用,且永远不会被删除。如果运行结果必须在数月后仍可复现,优先选择这些资产。
为什么选择素材库,而不是生成音频
从素材库中选择无需配额、id 验证、审核等待或上传;Agent 搜索并从 Creator Store 中选择素材,也无需人工干预。
生成音频会消耗仅限创建用途的上传配额,生成的内容却无人能在整个流程中听到。这种交换不值得,而这正是该技能要表达的观点。