☰
noname 开源三国杀音频系统指南:Audio 格式、技能语音与阵亡台词配置全解析
2026/10/4 13:03:03 网站建设 项目流程
  • 游戏开发
  • 前端

【免费下载链接】noname

项目地址:https://gitcode.com/gh_mirrors/nona/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中,键的格式为#[音频地址]:[后缀](技能台词没有后缀)。台词键有两条硬性规则:

  1. 音频地址不应包含文件后缀(如.mp3);
  2. 若音频存于默认路径(如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 阵亡音频的配置

用法

阵亡音频有两种配置入口:

  1. 为 Character 实例的dieAudios属性赋值:lib.character.guanyu.dieAudios = [true, "ext:无名扩展/audio/die:true"]
  2. 在角色的数组形式中填写任意个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两个实现类:

  1. 入口门面:get.Audio.skill({ skill, player, info, args })与get.Audio.die({ player, info, args })(get/audio.ts)。info参数既可以是完整技能对象,也可以是单个audio值——非对象或数组时会被自动包装成{ audio: info }。
  2. 角色信息格式化:formatPlayer负责把字符串角色名或 Player 实例统一成{ name, sex, tempname, skin }结构(get/audio.ts),供audioname/audioname2的角色匹配使用;皮肤还会通过lib.characterSubstitute解析出皮肤的tempname,使皮肤技能也能命中自定义语音(get/audio.ts)。
  3. 递归解析:parseAudio(get/audio.ts)按「数组 → 布尔/内联 → 数字与路径 → 引用」的优先级逐层展开,最终产出TextMap[],每个元素同时携带file(源文件名)与text(台词文本)。
  4. 对外接口:Audio类暴露fileList、textList、audioList三个只读列表(get/audio.ts),并内置#audioCache缓存以避免重复解析(get/audio.ts)。
  5. 游戏内调用: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),即动态音频始终实时解析,而普通静态配置走缓存以提升性能。

五、最佳实践速查

结合本文全部规则,给出几条可直接落地的配置建议:

  1. 技能语音优先数字简写:默认目录下audio: 2即自动匹配技能名1.mp3、技能名2.mp3,台词键只需登记#技能名1、#技能名2,维护成本最低。
  2. 共用技能按角色区分:多个角色共用技能时用audioname数组做文件名后缀;某角色需要完全不同的语音源(如联动角色、特殊化身)时用audioname2键值对覆盖,两者可叠加使用。
  3. 扩展包音频使用ext:路径:扩展音频统一放在extension/[扩展名]/audio/下,通过"ext:[扩展名]/audio:true"这类写法引用;台词键中的ext:路径要保留、但省略.mp3后缀。
  4. 阵亡音频支持多段:dieAudios可以是[true, "ext:...:true"]数组或["die:true", "die:ext:..."]角色写法,多段台词会用:die后缀的键登记。
  5. 注意分隔符差异:路径与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

项目地址:https://gitcode.com/gh_mirrors/nona/noname
点击查看免费下载
上一篇:cp-algorithms 容斥原理(Inclusion-Exclusion Principle)详解:公式证明与组合计数实战
下一篇:5个核心功能+3种应用场景:NSC_BUILDER让您的Switch游戏管理更高效

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询