- 游戏开发
- 前端
【免费下载链接】noname
本文是面向 noname(无名杀)开源项目开发者的音频系统实战指南,以 docs/audio-guide.md 为骨架,结合仓库源码逐层拆解skill/、die/目录下的音频格式约定、audio/audioname/dieAudios等配置项的写法与优先级,以及lib.translate中技能台词的配对规则。读完本文,你将能独立为技能配置多段语音、为角色指定专属台词、为扩展包接入自定义音频路径,并理解音频从配置到实际播放的完整解析链路。
一、Audio 音频的格式总览
在 noname 中,音频配置的核心是「值越小、含义越丰富」的写法约定:同一个配置位置,false、true、数字、字符串、数组乃至带:分隔符的路径字符串,分别代表完全不同的解析规则。理解这些规则是掌握整个音频系统的第一步。
为便于说明,下文沿用原文档的约定,以两个经典技能为例:
wusheng(武圣)采用wusheng: {audio: [示例写法]}的完整对象写法;paoxiao(咆哮)采用paoxiao: {audio: 2}的数字简写;- 默认路径统一为
skill/。
技能音频的默认路径与默认值,在源码 get/audio.ts 的SkillAudio类中有直接定义:defaultPath = "skill/"、defaultInfo = false(即不显式配置时技能默认不播放语音);而角色阵亡音频的默认路径为die/、默认值为true,对应 get/audio.ts 中的DieAudio类。
基础用法
| 配置值 | 含义 | 示例 | 解析结果 |
|---|---|---|---|
false | 不播放语音 | audio: false | [] |
true | 播放默认路径下与技能名或角色名一致的音频 | audio: true | ["skill/wusheng.mp3"] |
| 数字 | 播放默认路径下以数字做后缀的音频文件 | audio: 2 | ["skill/wusheng1.mp3", "skill/wusheng2.mp3"] |
| 字符串(引用) | 播放对应引用技能或角色的音频 | audio: "paoxiao" | ["skill/paoxiao1.mp3", "skill/paoxiao2.mp3"] |
源码中对false的处理位于 get/audio.ts:parseAudio先将配置String()化,命中"false"直接返回空数组。数字与true的解析则统一交给一条正则/(?:(.*):|^)(true|\d+)(?::(.*)|$)/(get/audio.ts),它会从配置中拆出「路径 / 数量标记 / 扩展名」三段:无路径时回落到defaultPath,无扩展名时默认.mp3,然后循环构造wusheng1.mp3、wusheng2.mp3这样的文件列表。
指定路径播放
当音频不放在默认目录时,通过扩展路径 +:分隔符的组合来定位:
| 配置值 | 含义 | 示例 | 解析结果 |
|---|---|---|---|
| 路径 | 直接按指定的音频路径播放 | "ext:无名扩展/audio/wusheng_custom.mp3" | ["ext:无名扩展/audio/wusheng_custom.mp3"] |
路径 +true | 播放指定路径下与技能名或角色名一致的音频 | "ext:无名扩展/audio:true:mp3" | ["ext:无名扩展/audio/wusheng.mp3"] |
| 路径 + 数字 | 播放指定路径下以数字后缀命名的音频 | "ext:无名扩展/audio:2" | ["ext:无名扩展/audio/wusheng1.mp3", "ext:无名扩展/audio/wusheng2.mp3"] |
格式要点:在「路径 +true」和「路径 + 数字」两种格式中,路径与true或数字之间使用的是冒号:而不是斜杠/;true或数字与文件类型之间同样用:而不是.。当后缀为:mp3时可以省略不写——这正是 get/audio.ts 中ext == void 0时默认补.mp3的逻辑。此外,若配置以data:或blob:开头,解析器会直接把它当作内联音频地址原样返回(get/audio.ts),这也是皮肤、扩展中动态音频的常见接入方式。
高级组合播放
数组是表达能力最强的配置形态:
[引用/地址, 数量](仅限技能):播放引用的技能或指定地址的音频,并限制只播放前 n 个。["paoxiao", 1]→["skill/paoxiao1.mp3"]- 该分支在 get/audio.ts 有专门实现:当数组恰好为「字符串 + 数字」二元组、且该字符串对应一个已存在的技能时,先递归解析引用技能的全部音频,再
slice(0, number)截断;若引用的目标不存在,则把数字作为引用目标的音频后缀数量重新解析。
[任意组合]:数组内可混合以上任意格式,解析时会去重合并后一并播放。["paoxiao", "ext:无名扩展/audio:true"]→["skill/paoxiao1.mp3", "skill/paoxiao2.mp3", "ext:无名扩展/audio/wusheng.mp3"]- 数组的处理逻辑见 get/audio.ts:递归解析每个元素,按
name字段存入字典去重,最后返回Object.values(map)。
台词配对(数组与通用规则)
技能台词统一存放在lib.translate中,键的格式为#[音频地址]:[后缀](技能台词没有后缀)。台词键有两条硬性规则:
- 音频地址不应包含文件后缀(如
.mp3); - 若音频存于默认路径(如
skill/、die/等),路径部分也应省略。
以wusheng: {audio: 2}为例,台词键即为#wusheng1、#wusheng2。台词键的拼接规则在源码 get/audio.ts 的textMap方法中实现:name字段 = 去除默认路径前缀后的路径 + 文件名,text字段则直接查询lib.translate["#" + 该 name]。
二、Skill 技能音频的配置
用法与默认值
技能音频的配置方式是为技能的audio属性赋值,例如wusheng: {audio: 2}。默认情况下:
- 技能音频默认路径为
skill/; - 技能音频的默认值为
[true, 2],即默认同时尝试播放基础音效与 1、2 号语音,展开为[skill/[技能名].mp3, skill/[技能名]1.mp3, skill/[技能名]2.mp3]。
仓库中大量技能采用了数字简写,例如 bingshi/skill.js 中多个技能配置了audio: 2,也有audio: 4、audio: 6、audio: 12等多段语音的写法,说明数字可任意指定语音条数,只需目录下存在对应数量的技能名N.mp3文件。
自定义音频名称:audioname与audioname2
当一个技能被多个角色共用、而不同角色希望听到不同语音时,noname 提供了两级自定义机制(类型定义见 get/audio.ts):
audioname:一个包含角色名后缀的数组。当持有技能的角色名命中数组中的名字时,文件名会拼接_角色名后缀。wusheng: {audio: 2, audioname: ["zhangfei"]}→ 张飞使用wusheng时解析为["skill/wusheng_zhangfei1.mp3", "skill/wusheng_zhangfei2.mp3"]。- 这一后缀逻辑在 get/audio.ts 的
textMapWithIndex中实现:先通过getName从角色的tempname/ 本名 / 备用名中筛选出命中的audioname条目,再拼进文件名。
audioname2:一个「角色名 → 新 audio 配置」的键值对映射,可为特定角色彻底替换audio配置,且支持路径写法。wusheng: {audio: 2, audioname2: { zhangfei: "ext:无名扩展/audio:true" }}→ 张飞使用时解析为["ext:无名扩展/audio/wusheng.mp3"]。- 解析优先级见 get/audio.ts:构造函数中按角色名匹配
audioname2的键,命中后存入filteredAudioName2;随后在 getAudioInfo 中,audioname2的优先级高于技能自身的audio。
实战中,audioname常被用来给家族/势力共用技能区分语音。例如 clan/skill.js 中audioname: ["clan_zhugeguo", "clan_zhugezhan"]、clan/skill.js 中一次性列出王氏家族 8 个角色的audioname数组;bingshi/skill.js 中的audioname: ["pot_zhouyu_shadow"]则展示了为同一技能的不同化身/马甲角色定制语音的用法。
Skill 的台词配对
技能台词键以#[音频地址]为核心,键中不带:die之类的后缀。几个典型示例:
wusheng: {audio: 2}→ 台词键#wusheng1、#wusheng2;wusheng: {audio: "ext:无名扩展/audio:2"}→ 台词键#ext:无名扩展/audio/wusheng1、#ext:无名扩展/audio/wusheng2;wusheng: {audio: "ext:无名扩展/audio/wusheng_custom.mp3"}→ 台词键#ext:无名扩展/audio/wusheng_custom(注意省略.mp3);wusheng: {audio: 2, audioname: ["zhangfei"]}→ 台词键#wusheng1、#wusheng2、#wusheng_zhangfei1、#wusheng_zhangfei2(基础语音与专属语音的台词需分别登记);wusheng: {audio: 2, audioname2: { zhangfei: "ext:无名扩展/audio:true" }}→ 台词键#wusheng1、#wusheng2、#ext:无名扩展/audio/wusheng。
三、Die 阵亡音频的配置
用法
阵亡音频有两种配置入口:
- 为 Character 实例的
dieAudios属性赋值:lib.character.guanyu.dieAudios = [true, "ext:无名扩展/audio/die:true"] - 在角色的数组形式中填写任意个
die:xxx条目:guanyu: ["male", "shu", 4, ["wusheng"], ["die:true", "die:ext:无名扩展/audio/die:true"]]
第二种写法的底层解析在 library/element/character.js 的setPropertiesFromTrash中:凡是item.startsWith("die:")的字符串,都会把die:之后的内容(即true、路径等)追加进dieAudios数组;同时源码还保留了旧格式die_audio:的兼容解析,但会打印die_audio参数已废弃,请使用多个die参数的警告。此外 game/index.js 在加载扩展角色时会兜底:若角色的dieAudios为空,则自动补一条ext:${extname}/audio/die:true,保证扩展角色即使不配置阵亡音频也有默认语音可播。
默认值
- 阵亡音频的默认路径为
die/; - 阵亡音频的默认值为
true,即["die/[角色名].mp3"]。
仓库资源目录 apps/core/audio/die 中存放了数量庞大的阵亡语音文件,绝大多数角色均有角色名.mp3,部分角色还有角色名1.mp3、角色名2.mp3等多段语音,例如caocao.mp3、boss_lvbu2.mp3、clan_xxx.mp3等,与数字后缀规则一一对应。
Die 的台词配对
阵亡台词的键格式为#[音频地址]:die,统一带:die后缀,以与技能台词区分:
lib.character.guanyu.dieAudios = 2→ 台词键#guanyu1:die、#guanyu2:die;lib.character.guanyu.dieAudios = "die:ext:无名扩展/audio/die:true"→ 台词键#ext:无名扩展/audio/die/guanyu:die;lib.character.guanyu.dieAudios = "ext:无名扩展/audio/guanyu_custom.mp3"→ 台词键#ext:无名扩展/audio/guanyu_custom:die。
DieAudio.textMap的实现见 get/audio.ts:台词键固定拼接":die"后缀,文件路径与技能同理会去掉die/默认前缀。
四、从配置到播放:源码中的解析链路
理解配置写法之后,值得花一小节看清 noname 是如何把这些配置变成实际播放的音频文件的。整条链路的核心是 get/audio.ts 中的Audio门面类与SkillAudio/DieAudio两个实现类:
- 入口门面:
get.Audio.skill({ skill, player, info, args })与get.Audio.die({ player, info, args })(get/audio.ts)。info参数既可以是完整技能对象,也可以是单个audio值——非对象或数组时会被自动包装成{ audio: info }。 - 角色信息格式化:
formatPlayer负责把字符串角色名或 Player 实例统一成{ name, sex, tempname, skin }结构(get/audio.ts),供audioname/audioname2的角色匹配使用;皮肤还会通过lib.characterSubstitute解析出皮肤的tempname,使皮肤技能也能命中自定义语音(get/audio.ts)。 - 递归解析:
parseAudio(get/audio.ts)按「数组 → 布尔/内联 → 数字与路径 → 引用」的优先级逐层展开,最终产出TextMap[],每个元素同时携带file(源文件名)与text(台词文本)。 - 对外接口:
Audio类暴露fileList、textList、audioList三个只读列表(get/audio.ts),并内置#audioCache缓存以避免重复解析(get/audio.ts)。 - 游戏内调用:game/index.js 把门面类包装成
parseSkillAudio、parseSkillText、parseSkillTextMap、parseDieTextMap等游戏 API;trySkillAudio(game/index.js)则负责实际触发播放,会先通过game.broadcast广播给所有客户端,并受lib.config.background_speak开关控制。UI 侧,角色详情页通过get.Audio.die(...)拉取阵亡台词列表渲染到台词面板(见 ui/click/index.js 等处的调用)。
需要特别留意的两点源码行为:
- 引用递归保护:技能 A 引用技能 B 的音频时,若形成 A→B→A 的循环引用,
checkHistory会抛出RangeError提示「infinite recursion」(get/audio.ts),编写交叉引用时务必避免死循环。 - 优先级与缓存:传入
info、配置了logAudio/audioname2命中项时useCache会被置为false跳过缓存(get/audio.ts),即动态音频始终实时解析,而普通静态配置走缓存以提升性能。
五、最佳实践速查
结合本文全部规则,给出几条可直接落地的配置建议:
- 技能语音优先数字简写:默认目录下
audio: 2即自动匹配技能名1.mp3、技能名2.mp3,台词键只需登记#技能名1、#技能名2,维护成本最低。 - 共用技能按角色区分:多个角色共用技能时用
audioname数组做文件名后缀;某角色需要完全不同的语音源(如联动角色、特殊化身)时用audioname2键值对覆盖,两者可叠加使用。 - 扩展包音频使用
ext:路径:扩展音频统一放在extension/[扩展名]/audio/下,通过"ext:[扩展名]/audio:true"这类写法引用;台词键中的ext:路径要保留、但省略.mp3后缀。 - 阵亡音频支持多段:
dieAudios可以是[true, "ext:...:true"]数组或["die:true", "die:ext:..."]角色写法,多段台词会用:die后缀的键登记。 - 注意分隔符差异:路径与
true/数字之间是:,文件类型后缀前是.,true/数字与扩展名之间又是:——写错分隔符会导致音频解析不到文件。
相关资源
- 官方音频指南原始文档:docs/audio-guide.md
- 音频解析核心实现:apps/core/noname/get/audio.ts
- 角色
dieAudios解析与废弃参数兼容:apps/core/noname/library/element/character.js - 游戏内音频 API 与播放触发:apps/core/noname/game/index.js
- 技能
audioname实战示例:apps/core/character/clan/skill.js、apps/core/character/bingshi/skill.js - 阵亡语音资源目录:apps/core/audio/die
- 游戏开发
- 前端
【免费下载链接】noname
相关推荐
libnoname/noname项目音频系统使用指南
libnoname/noname项目音频系统使用指南 概述 libnoname/noname是一个基于JavaScript开发的卡牌游戏框架,其音频系统提供了灵
游戏开发前端告别卡顿与延迟:Snapcast三大音频格式全解析与配置指南
告别卡顿与延迟:Snapcast三大音频格式全解析与配置指南 你是否曾在打造多房间音频系统时遭遇同步延迟、音质损耗或带宽占用过高的问题?Snapcast作为一款
音视频Phaser 音频与声音系统完全指南:SoundManager、Web Audio、音频精灵与空间音频实战
Phaser 音频与声音系统完全指南:SoundManager、Web Audio、音频精灵与空间音频实战 本指南以 Phaser 仓库 skills/audi
游戏开发图形学前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考