游戏开发者:5 分钟接入 .pulse
这份指南只面向 Web / HTML5 游戏开发者。目标只有一个:让制作好的 .pulse 音乐包开始播放,并跟随游戏切换音乐状态。
你不需要理解素材、片段、Layer、DSP 或转场编排。第一次接入只需要三个动作:① 加载音乐包 → ② 播放 → ③ 切换游戏音乐状态。
如果你负责制作音乐包,请改看 Pulse Designer 手册。不确定角色时,从文档入口开始。
TL;DR:30 秒速览(熟手版)
从 Pulse Desktop 帮助页复制 pulseContract,然后:
pnpm add @clxgame/pulse-game
const music = await loadPulseGame("/packs/forest-combat.pulse"); // 加载,不自动播放
await music.play(); // 必须由用户点击触发
music.setGameState(pulseContract.states.combat); // 游戏事件里切状态
只调用合同公开的 ID;场景销毁时 music.dispose()。细节和陷阱见下文。
1. 拿到接入合同
规范交付的新 .pulse 文件应在包内携带一份开发者接入合同:它只公开游戏真正需要调用的 state ID 和 parameter ID。在 Pulse Desktop 的帮助页中打开这个包,复制自动生成的 pulseContract 和接入示例即可,不要自己翻 manifest 猜 ID。
例如,生成的合同可能包含:
// pulse-contract.ts
export const pulseContract = {
initialStateId: "exploration",
states: {
exploration: "exploration",
combat: "combat",
},
parameters: {
tension: { id: "tension", min: 0, max: 1, defaultValue: 0.2 },
},
} as const;
| 游戏含义 | Pulse 调用值 |
|---|---|
| 探索音乐 | state · exploration |
| 战斗音乐 | state · combat |
| 紧张程度 | parameter · tension(范围 0..1) |
合同里没有出现的状态和参数也是包内实现,不应由游戏调用。resource、segment 和 layer ID 永远属于音乐包内部实现。
⚠️ 旧包未声明接入合同怎么办? 如果帮助页显示"旧包未声明接入合同"的警告,SDK 为兼容旧包会暂时把所有状态和参数推断为公开;请让内容方确认后再接入。这个推断不会被悄悄写回
.pulse。
⛔ 合同损坏或版本不受支持时的行为 如果包已经声明合同但合同损坏或版本不受支持,SDK 不会退回"全部公开",而会停止生成调用代码并明确报错。
2. 安装并放置文件
pnpm add @clxgame/pulse-game
把音乐包放进项目的静态资源目录,例如:
public/
└─ packs/
└─ forest-combat.pulse ← 浏览器中的地址:/packs/forest-combat.pulse
3. 写最小接入代码
// game-music.ts
import { loadPulseGame } from "@clxgame/pulse-game";
import { pulseContract } from "./pulse-contract";
// 提前下载、校验并准备音乐;这里不会自动播放。
const music = await loadPulseGame("/packs/forest-combat.pulse");
// 正式新包通常为空;旧包会在这里明确说明"全部 ID 只是兼容推断"。
for (const warning of music.contract.warnings) console.warn(warning);
// 浏览器要求第一次播放来自真实的用户操作。
startButton.addEventListener("click", async () => {
await music.play();
});
// 换成你自己游戏的事件系统或业务回调。
gameEvents.on("encounter:start", () => {
music.setGameState(pulseContract.states.combat);
});
gameEvents.on("encounter:end", () => {
music.setGameState(pulseContract.states.exploration);
});
gameEvents.on("danger:changed", (value: number) => {
music.setGameParameter(pulseContract.parameters.tension.id, value);
});
// 场景或游戏实例销毁时释放音频资源。
scene.onDestroy(() => music.dispose());
💡
startButton、gameEvents和scene代表你项目里已有的按钮、事件系统和场景生命周期,不是 Pulse 新增的框架。
做到这里,开发者接入就完成了。音乐怎么循环、何时在小节边界切换、战斗状态里有哪些分层音轨,都由 .pulse 内容决定。
4. 你只需要记住这些规则
loadPulseGame()— 负责下载、校验并准备音乐,但不会绕过浏览器自动播放限制。play()— 应从 Start、Continue 或点击画面等用户操作中调用。setGameState()— 只能传入接入合同公开的 state ID;不存在或仅供包内使用的 ID 会立即报错。setGameParameter()— 只能传入合同公开的 parameter ID;未知、内部参数或非数字会立即报错;有限数值超出范围时,SDK 会限制到音乐包定义的最小值或最大值。- 一个场景一个 runtime — 一个场景通常只创建一个 runtime,不要在每次战斗事件中重新加载。
dispose()—dispose()之后不要继续使用这个 runtime。
5. 五分钟验收
-
.pulse请求返回 200; - 已从帮助页复制生成的
pulseContract,并且没有忽略旧包警告; - 点击开始按钮后能听到音乐;
- 进入战斗时切换到
combat; - 离开战斗时回到
exploration; - 场景退出时调用
dispose(); - 错误的 URL 或合同 ID 会给出清晰的开发期错误。
如果还没有声音,优先检查文件 URL、浏览器控制台、用户点击是否真正触发了 play(),以及 state ID 是否和交付合同完全一致。
🚧 上面的约十行代码只验收 happy path。上线前还要在游戏的启动边界捕获
loadPulseGame()/play()失败,并回退到普通 BGM 或静音;这部分放在完整说明的加载与部署部分,不塞进第一次接入代码。
需要更多控制时再往下读
预加载策略、事件监听、CDN 缓存、错误降级、自定义资源系统和部署检查都放在完整游戏接入说明书中。可运行实现见 HTML5 示例。
📌 当前版本没有真正的 Stinger;
trigger()只是状态切换兼容别名,不要把它当作叠加播放的一次性音效。Unity、Godot 和 Unreal runtime 也还不是当前生产承诺。