音效修复

master
humingxing 1 day ago
parent 07123d2527
commit d9ba6edf90
  1. 0
      assets/bundles/audio/sfx/weapon/剑攻击.mp3
  2. 0
      assets/bundles/audio/sfx/weapon/剑攻击.mp3.meta
  3. BIN
      assets/bundles/audio/sfx/weapon/法杖攻击.mp3
  4. 1
      assets/bundles/audio/sfx/weapon/法杖攻击.mp3.meta
  5. 16
      assets/bundles/first/config/Text.json
  6. 182
      assets/script/battle/audio/BattleAudio.ts
  7. 6
      assets/script/battle/presentation/SkillVisualChannel.ts
  8. 5
      assets/script/battle/skill/SkillController.ts
  9. 6
      assets/script/interface/AutoTable.ts
  10. 5
      assets/script/ui/BattleFail/UIBattleFail.ts
  11. 3
      assets/script/ui/BattleGame/BattlePeachCoinFlyPresenter.ts
  12. 4
      assets/script/ui/BattleSuccess/UIBattleSuccess.ts
  13. 2
      assets/script/ui/PopDailyShopOpen/UIPopDailyShopOpen.ts
  14. 2
      assets/script/ui/PopSelect/UIPopSelect.ts
  15. 83
      docs/需求-音效.md

@ -0,0 +1 @@
{"ver":"1.0.0","importer":"audio-clip","imported":true,"uuid":"094dbe62-cda1-4deb-97f6-22c6ec70f132","files":[".json",".mp3"],"subMetas":{},"userData":{"downloadMode":0}}

@ -123,12 +123,24 @@
"id": 2001, "id": 2001,
"type": 10, "type": 10,
"level": 0, "level": 0,
"weight": 3 "weight": 5
}, },
{ {
"id": 2002, "id": 2002,
"type": 11, "type": 11,
"level": 0, "level": 0,
"weight": 2 "weight": 5
},
{
"id": 3001,
"type": 12,
"level": 0,
"weight": 15
},
{
"id": 3002,
"type": 13,
"level": 0,
"weight": 5
} }
] ]

@ -1,5 +1,6 @@
import { TeamComp, type EntityId } from 'db://assets/mx/module/battle'; import { TeamComp, type EntityId } from 'db://assets/mx/module/battle';
import { GAudioPath } from '../../game/ConfigRes'; import { GAudioPath } from '../../game/ConfigRes';
import { TableNames } from '../../game/ConfigTableData';
import { getTowerLoadoutService } from '../combat/TowerLoadoutService'; import { getTowerLoadoutService } from '../combat/TowerLoadoutService';
import { resetTangSengAudioState } from './TangSengAudioState'; import { resetTangSengAudioState } from './TangSengAudioState';
@ -22,6 +23,7 @@ type ActiveSfx = { priority: SfxPri; startedAt: number };
/** /**
* 战斗音效:按现有 audio 资源名映射,去叠音 / 并发上限 / 优先级 / BGM 压低。 * 战斗音效:按现有 audio 资源名映射,去叠音 / 并发上限 / 优先级 / BGM 压低。
* 普攻/技能按五行 Hero.herotype、宝石 Weapon.type 解析(现表 id 101+)。
*/ */
export class BattleAudio { export class BattleAudio {
private static _lastPlayAt = new Map<string, number>(); private static _lastPlayAt = new Map<string, number>();
@ -60,10 +62,10 @@ export class BattleAudio {
/** 进战斗预载常用音效,避免首帧 async 加载导致与动画不同步 */ /** 进战斗预载常用音效,避免首帧 async 加载导致与动画不同步 */
private static _warmUpBattleSfx(): void { private static _warmUpBattleSfx(): void {
for (const name of ['刀攻击', '枪攻击', '棍攻击', '箭攻击', '孙悟空技能', '悟净技能', '李靖技能', '阎王技能', '杨戬技能音效', '金箍棒技能', '玲珑宝塔技能', '碎片掉落']) { for (const name of ['剑攻击', '枪攻击', '棍攻击', '箭攻击','法杖攻击', '孙悟空技能', '悟净技能', '李靖技能', '阎王技能', '杨戬技能音效', '金箍棒技能', '玲珑宝塔技能', '碎片掉落']) {
void gg.audio.preloadEffect({ name, path: GAudioPath.SfxWeapon }); void gg.audio.preloadEffect({ name, path: GAudioPath.SfxWeapon });
} }
for (const name of ['怪物受击', '合成', '格子放置', '砚台出字', '开场号角', 'BOSS出场', '危险警报']) { for (const name of ['怪物受击', '合成', '格子放置', '武器拖动', '砚台出字', '可刷新通知', 'BOSS出场', '危险警报', '结算金币效果']) {
void gg.audio.preloadEffect({ name, path: GAudioPath.SfxUi }); void gg.audio.preloadEffect({ name, path: GAudioPath.SfxUi });
} }
} }
@ -92,23 +94,21 @@ export class BattleAudio {
static playBajiaoFanCast(): void { static playBajiaoFanCast(): void {
} }
/** 同字升级 */ /** 同级数字合成升级 */
static playUpgrade(): void { static playUpgrade(): void {
this._play('合成', GAudioPath.SfxUi, Pri.System, 0); this._play('合成', GAudioPath.SfxUi, Pri.System, 0);
} }
/** 神将组合 */ /** @deprecated 多格神将组合已废弃;若残留路径触发则同「合成」 */
static playCompose(): void { static playCompose(): void {
this._play('合成', GAudioPath.SfxUi, Pri.System, 0); this._play('合成', GAudioPath.SfxUi, Pri.System, 0);
} }
/** /**
* 神将合成语音(仅有资源的英雄) * @deprecated 多格神将组合已废弃,无调用时机
* @param weapon1Id Hero.id * @param _weapon1Id 旧 Hero.id
*/ */
static playComposeVoice(weapon1Id: number): void { static playComposeVoice(_weapon1Id: number): void {
const name = HERO_COMPOSE_VOICE[weapon1Id];
if (name) this._play(name, GAudioPath.SfxVoice, Pri.Voice, 1000);
} }
/** @deprecated 新产品无此音效 */ /** @deprecated 新产品无此音效 */
@ -200,34 +200,45 @@ export class BattleAudio {
this._play('游戏失败', GAudioPath.SfxUi, Pri.Result, 0); this._play('游戏失败', GAudioPath.SfxUi, Pri.Result, 0);
} }
/** 击杀武器碎片飞出 */ /** 击杀宝石掉落飞出 */
static playFragmentDrop(): void { static playFragmentDrop(): void {
this._play('碎片掉落', GAudioPath.SfxWeapon, Pri.UI, 80); this._play('碎片掉落', GAudioPath.SfxWeapon, Pri.UI, 80);
} }
/** 局内波次/击杀金币飞行 */
static playCoinFly(): void {
this._play('结算金币效果', GAudioPath.SfxUi, Pri.UI, 80);
}
/** /**
* 普攻 * 普攻(按 Hero.herotype → 现有刀/枪/棍/箭)。
* 弹道类(水弹连发等)应在真正出手时调,勿在 PresentationCue 提前播。
* @param caster 攻击实体 * @param caster 攻击实体
* @param debounceMs 去抖;连发出手用较短间隔
*/ */
static playWeaponAttack(caster: EntityId): void { static playWeaponAttack(caster: EntityId, debounceMs = 150): void {
if (!this._isLocalEntity(caster)) return; if (!this._isLocalEntity(caster)) return;
const id = getTowerLoadoutService().getLoadout(caster)?.weapon1Id; const heroId = getTowerLoadoutService().getLoadout(caster)?.weapon1Id;
if (id == null) return; if (heroId == null) return;
const name = ATTACK_SFX[id]; const name = resolveAttackSfxByHeroId(heroId);
if (name) this._play(name, GAudioPath.SfxWeapon, Pri.Attack, 150); if (!name) return;
this._play(name, GAudioPath.SfxWeapon, Pri.Attack, debounceMs);
} }
/** /**
* 英雄主动或专属武器技能 * 英雄主动或宝石武器技能
* @param caster 施法实体 * @param caster 施法实体
* @param isEquipSkill true=专属武器技能(金箍棒/玲珑宝塔) * @param isEquipSkill true=宝石/武器技能
*/ */
static playWeaponSkill(caster: EntityId, isEquipSkill = false): void { static playWeaponSkill(caster: EntityId, isEquipSkill = false): void {
if (!this._isLocalEntity(caster)) return; if (!this._isLocalEntity(caster)) return;
const id = getTowerLoadoutService().getLoadout(caster)?.weapon1Id; const loadout = getTowerLoadoutService().getLoadout(caster);
if (id == null) return; if (!loadout) return;
const name = isEquipSkill ? EQUIP_SKILL_SFX[id] : HERO_SKILL_SFX[id]; const name = isEquipSkill
if (name) this._play(name, GAudioPath.SfxWeapon, Pri.Combat, 0); ? resolveGemSkillSfx(loadout.weaponId)
: resolveHeroSkillSfxByHeroId(loadout.weapon1Id);
if (!name) return;
this._play(name, GAudioPath.SfxWeapon, Pri.Combat, 0);
} }
/** /**
@ -344,47 +355,100 @@ export class BattleAudio {
} }
} }
/** Hero.id → 普攻音效(按 Hero.weapon:1刀 2枪 3棍 4箭) */ /**
const ATTACK_SFX: Record<number, string> = { * Hero.herotype → 普攻音效。
1: '刀攻击', * 1金剑 2木棍 3水法杖 4火箭 5土枪
2: '枪攻击', */
3: '棍攻击', const ATTACK_BY_HEROTYPE: Record<number, string> = {
1: '剑攻击',
2: '棍攻击',
3: '法杖攻击',
4: '箭攻击', 4: '箭攻击',
11: '棍攻击', 5: '枪攻击',
12: '刀攻击',
13: '棍攻击',
14: '枪攻击',
15: '枪攻击',
16: '刀攻击',
17: '枪攻击',
18: '箭攻击',
19: '刀攻击',
20: '刀攻击',
21: '箭攻击',
22: '刀攻击',
}; };
/** Hero.id → 英雄主动技能音效 */ /**
const HERO_SKILL_SFX: Record<number, string> = { * Hero.herotype → 英雄主动技能音效(复用现有神将技能资源)。
11: '孙悟空技能', * 1金剑阵→孙悟空 2木飞棍→悟净 3水暴风雪→阎王 4火箭雨→杨戬 5土冲锋→李靖
13: '悟净技能', */
17: '杨戬技能音效', const HERO_SKILL_BY_HEROTYPE: Record<number, string> = {
20: '阎王技能', 1: '孙悟空技能',
22: '李靖技能', 2: '悟净技能',
3: '阎王技能',
4: '杨戬技能音效',
5: '李靖技能',
}; };
/** Hero.id → 专属武器技能音效 */ /**
const EQUIP_SKILL_SFX: Record<number, string> = { * 五行元素 → 宝石武器技能音效(Weapon.type 2金…6土 → element 1…5)。
11: '金箍棒技能', * 金→金箍棒 木→悟净 水→阎王 火→杨戬 土→玲珑宝塔
22: '玲珑宝塔技能', */
const GEM_SKILL_BY_ELEMENT: Record<number, string> = {
1: '金箍棒技能',
2: '悟净技能',
3: '阎王技能',
4: '杨戬技能音效',
5: '玲珑宝塔技能',
}; };
/** Hero.id → 合成语音 */ /**
const HERO_COMPOSE_VOICE: Record<number, string> = { * @param heroId Hero.id(五行 101+)
11: '孙悟空合成', * @returns 普攻资源名;无法解析时 warn 并返回空
14: '哪吒合成', */
17: '杨戬合成', function resolveAttackSfxByHeroId(heroId: number): string {
18: '王母合成', const hero = gg.data.table.getTableData<ITableHero>(TableNames.Hero, heroId);
19: '玉帝合成', if (!hero) {
22: '李靖合成', console.warn('[BattleAudio] 普攻音效缺少 Hero', heroId);
}; return '';
}
const herotype = Math.floor(Number(hero.herotype));
const name = ATTACK_BY_HEROTYPE[herotype];
if (!name) {
console.warn('[BattleAudio] 普攻音效未映射 herotype', herotype, 'heroId', heroId);
return '';
}
return name;
}
/**
* @param heroId Hero.id
* @returns 英雄主动技能资源名
*/
function resolveHeroSkillSfxByHeroId(heroId: number): string {
const hero = gg.data.table.getTableData<ITableHero>(TableNames.Hero, heroId);
if (!hero) {
console.warn('[BattleAudio] 英雄技能音效缺少 Hero', heroId);
return '';
}
const herotype = Math.floor(Number(hero.herotype));
const name = HERO_SKILL_BY_HEROTYPE[herotype];
if (!name) {
console.warn('[BattleAudio] 英雄技能音效未映射 herotype', herotype, 'heroId', heroId);
return '';
}
return name;
}
/**
* @param weaponId Weapon.id(宝石);0 表示未镶嵌
* @returns 宝石技能资源名
*/
function resolveGemSkillSfx(weaponId: number): string {
if (!(weaponId > 0)) {
console.warn('[BattleAudio] 宝石技能音效缺少 weaponId');
return '';
}
const weapon = gg.data.table.getTableData<ITableWeapon>(TableNames.Weapon, weaponId);
if (!weapon) {
console.warn('[BattleAudio] 宝石技能音效缺少 Weapon', weaponId);
return '';
}
// Weapon.type:1通用 2金 3木 4水 5火 6土 → element 1–5
const element = Math.floor(Number(weapon.type)) - 1;
const name = GEM_SKILL_BY_ELEMENT[element];
if (!name) {
console.warn('[BattleAudio] 宝石技能音效未映射 Weapon.type', weapon.type, 'weaponId', weaponId);
return '';
}
return name;
}

@ -12,6 +12,7 @@ import { HeroWeaponMount, WEAPON_REST_ANGLE, BOW_ATK_REST_ANGLE, BOW_RAIN_AIM_DE
import { BattleSpinePlayback } from './BattleSpinePlayback'; import { BattleSpinePlayback } from './BattleSpinePlayback';
import { glyphSpineHasSkin } from '../view/GlyphSpineSkin'; import { glyphSpineHasSkin } from '../view/GlyphSpineSkin';
import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch'; import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch';
import { BattleAudio } from '../audio/BattleAudio';
import { BOW_ATK_ARROW_SPEED, SCATTER_RANGE_MUL, getBowAtkHost } from '../combat/BowAtkRuntime'; import { BOW_ATK_ARROW_SPEED, SCATTER_RANGE_MUL, getBowAtkHost } from '../combat/BowAtkRuntime';
import { GBundle, GPath } from '../../game/ConfigRes'; import { GBundle, GPath } from '../../game/ConfigRes';
@ -1226,6 +1227,7 @@ export class SkillVisualChannel {
host.fillScatterDegs(aimDeg, count, stepDeg, this._scatterDegs); host.fillScatterDegs(aimDeg, count, stepDeg, this._scatterDegs);
this._weapon.setArrowVisible(false); this._weapon.setArrowVisible(false);
const duration = Math.max(0.12, travel / BOW_ATK_ARROW_SPEED); const duration = Math.max(0.12, travel / BOW_ATK_ARROW_SPEED);
let launched = 0;
for (let i = 0; i < this._scatterDegs.length; i++) { for (let i = 0; i < this._scatterDegs.length; i++) {
const deg = this._scatterDegs[i]; const deg = this._scatterDegs[i];
const rad = deg * Math.PI / 180; const rad = deg * Math.PI / 180;
@ -1254,7 +1256,9 @@ export class SkillVisualChannel {
pierce, pierce,
onConsume: pierce ? undefined : this._recycleBowArrow.bind(this, clone), onConsume: pierce ? undefined : this._recycleBowArrow.bind(this, clone),
}); });
launched++;
} }
if (launched > 0) BattleAudio.playWeaponAttack(caster, 40);
} }
/** /**
@ -1467,6 +1471,7 @@ export class SkillVisualChannel {
buff2, buff2,
onLand: land.onLand, onLand: land.onLand,
}); });
BattleAudio.playWeaponAttack(caster, 40);
return true; return true;
} }
@ -1648,6 +1653,7 @@ export class SkillVisualChannel {
const duration = Math.max(0.12, dist / WATER_BALL_SPEED); const duration = Math.max(0.12, dist / WATER_BALL_SPEED);
const node = this._spawnWaterBall(this._arrowFrom); const node = this._spawnWaterBall(this._arrowFrom);
if (!node) return false; if (!node) return false;
BattleAudio.playWeaponAttack(caster, 40);
SkillTransformDriver.animateParabola({ SkillTransformDriver.animateParabola({
node, node,
duration, duration,

@ -378,7 +378,10 @@ export class SkillController extends Component {
} }
try { try {
const heroId = getTowerLoadoutService().getLoadout(event.caster)?.weapon1Id; const heroId = getTowerLoadoutService().getLoadout(event.caster)?.weapon1Id;
if (heroId !== GONG_WEAPON1_ID && !(skill instanceof Gong)) { // 12/13/17:音效在弹道真正出手时播(水弹连发等),避免开动画就响
const atkHead = Math.floor(Number(event.params?.atkTypeHead ?? event.params?.atkType) || 0);
const deferAttackSfx = atkHead === 12 || atkHead === 13 || atkHead === 17;
if (heroId !== GONG_WEAPON1_ID && !(skill instanceof Gong) && !deferAttackSfx) {
BattleAudio.playWeaponAttack(event.caster); BattleAudio.playWeaponAttack(event.caster);
} }
skill.onAttack(ctx); skill.onAttack(ctx);

@ -277,7 +277,11 @@ declare global {
2、木 2、木
3、水 3、水
4、火 4、火
5、土 */ 5、土
10、双倍符(数字等级+1)
11、4倍符(数字等级+1)
12、随机牌(随机转换数字属性)
13、衰弱符(对方数字等级-1) */
readonly type: number; readonly type: number;
/** 等级 */ /** 等级 */
readonly level: number; readonly level: number;

@ -9,7 +9,8 @@ import { Auto_BattleFail } from './Auto_BattleFail';
import { BattleSuccessCoinFlyPresenter } from '../BattleSuccess/BattleSuccessCoinFlyPresenter'; import { BattleSuccessCoinFlyPresenter } from '../BattleSuccess/BattleSuccessCoinFlyPresenter';
import { StatisticsType, ItemNumType } from '../../game/GameData'; import { StatisticsType, ItemNumType } from '../../game/GameData';
import { ItemData } from '../../game/ConfigProjectData'; import { ItemData } from '../../game/ConfigProjectData';
import { UIID, GAudioPath } from '../../game/ConfigRes'; import { UIID } from '../../game/ConfigRes';
import { BattleAudio } from '../../battle/audio/BattleAudio';
import { MatchWinStats } from '../../manager/MatchWinStats'; import { MatchWinStats } from '../../manager/MatchWinStats';
import { RankDataManager } from '../../manager/RankDataManager'; import { RankDataManager } from '../../manager/RankDataManager';
import { VivoAd } from '../../sdk/functions/VivoAd'; import { VivoAd } from '../../sdk/functions/VivoAd';
@ -73,7 +74,7 @@ export class UIBattleFail extends Auto_BattleFail {
this.btnGet.active = true; this.btnGet.active = true;
this.btnADGet.active = true; this.btnADGet.active = true;
gg.audio.playEffect({ name: '游戏失败', path: GAudioPath.SfxUi }); BattleAudio.playFail();
const starsBefore = RankDataManager.getPlayerRankStars(); const starsBefore = RankDataManager.getPlayerRankStars();
MatchWinStats.record(false, gg.game.CurentBattle?.opponentRankId ?? 0); MatchWinStats.record(false, gg.game.CurentBattle?.opponentRankId ?? 0);

@ -1,5 +1,6 @@
import { Label, Node, Tween, tween, UIOpacity, Vec3 } from 'cc'; import { Label, Node, Tween, tween, UIOpacity, Vec3 } from 'cc';
import { GEvent } from 'db://assets/mx/module/event/GEvent'; import { GEvent } from 'db://assets/mx/module/event/GEvent';
import { BattleAudio } from '../../battle/audio/BattleAudio';
import { GBundle } from '../../game/ConfigRes'; import { GBundle } from '../../game/ConfigRes';
import { CoinFly } from './CoinFly'; import { CoinFly } from './CoinFly';
@ -129,6 +130,7 @@ export class BattlePeachCoinFlyPresenter {
this._wavePopped = 0; this._wavePopped = 0;
this._waveSpawnFinished = false; this._waveSpawnFinished = false;
this._waveAwaitFly = true; this._waveAwaitFly = true;
BattleAudio.playCoinFly();
this._spawnOneWaveCoin(); this._spawnOneWaveCoin();
} }
@ -277,6 +279,7 @@ export class BattlePeachCoinFlyPresenter {
*/ */
playKillDrop(startWorld: Readonly<Vec3>, peachCount: number): void { playKillDrop(startWorld: Readonly<Vec3>, peachCount: number): void {
if (peachCount <= 0) return; if (peachCount <= 0) return;
BattleAudio.playCoinFly();
const n = this._spawnCoinFlies(startWorld.clone(), peachCount, null, true); const n = this._spawnCoinFlies(startWorld.clone(), peachCount, null, true);
if (n <= 0) { if (n <= 0) {
console.log('[BattlePeachCoinFly] 击杀掉桃未生成'); console.log('[BattlePeachCoinFly] 击杀掉桃未生成');

@ -17,7 +17,7 @@ import { BattleSuccessCoinFlyPresenter } from './BattleSuccessCoinFlyPresenter';
import { TaskType, ItemNumType } from '../../game/GameData'; import { TaskType, ItemNumType } from '../../game/GameData';
import { ItemData } from '../../game/ConfigProjectData'; import { ItemData } from '../../game/ConfigProjectData';
import MTools from 'db://assets/mx/tools/MTools'; import MTools from 'db://assets/mx/tools/MTools';
import { GAudioPath } from '../../game/ConfigRes'; import { BattleAudio } from '../../battle/audio/BattleAudio';
import { VivoAd } from '../../sdk/functions/VivoAd'; import { VivoAd } from '../../sdk/functions/VivoAd';
import { IPopAnimConfig } from 'db://assets/mx/module/ui/popAnim'; import { IPopAnimConfig } from 'db://assets/mx/module/ui/popAnim';
import { recordKeepHeroClearFromBattle } from '../../battle/bag/KeepHeroClearRecord'; import { recordKeepHeroClearFromBattle } from '../../battle/bag/KeepHeroClearRecord';
@ -165,7 +165,7 @@ export class UIBattleSuccess extends Auto_BattleSuccess {
action_param: levelId, action_param: levelId,
}); });
gg.audio.playEffect({ name: '游戏胜利', path: GAudioPath.SfxUi }); BattleAudio.playVictory();
MatchWinStats.record(true, gg.game.CurentBattle?.opponentRankId ?? 0); MatchWinStats.record(true, gg.game.CurentBattle?.opponentRankId ?? 0);
gg.sdk.reportDY('inLevel', `${gg.game.getReportDYRankLabel()}-胜利`); gg.sdk.reportDY('inLevel', `${gg.game.getReportDYRankLabel()}-胜利`);

@ -531,7 +531,7 @@ export class UIPopDailyShopOpen extends Auto_PopDailyShopOpen {
this._onOneCardClosed(index); this._onOneCardClosed(index);
return; return;
} }
gg.audio.playEffect({ name: '点击抽卡', path: GAudioPath.SfxUi }); gg.audio.playEffect({ name: '点击按钮', path: GAudioPath.SfxUi });
const targetX = openFace?.isValid ? openFace.position.x : card.position.x; const targetX = openFace?.isValid ? openFace.position.x : card.position.x;
const targetY = openFace?.isValid ? openFace.position.y : card.position.y; const targetY = openFace?.isValid ? openFace.position.y : card.position.y;
const skew = this._ensureSkew(card); const skew = this._ensureSkew(card);

@ -422,7 +422,7 @@ export class UIPopSelect extends Auto_PopSelect {
this._onOpenHalfDone(gen); this._onOpenHalfDone(gen);
return; return;
} }
gg.audio.playEffect({ name: '点击抽卡', path: GAudioPath.SfxUi }); gg.audio.playEffect({ name: '点击按钮', path: GAudioPath.SfxUi });
const hx = this._homeScale.x; const hx = this._homeScale.x;
const hy = this._homeScale.y; const hy = this._homeScale.y;
const hz = this._homeScale.z; const hz = this._homeScale.z;

@ -1,71 +1,86 @@
# 需求:音效映射 # 需求:音效映射
> 日期:2026-09-08 > 日期:2026-10-16(对齐五行数字战斗)
> 资源目录:`assets/bundles/audio/` > 资源目录:`assets/bundles/audio/`
> 播放入口:`BattleAudio`、`gg.audio.playEffect` / `playMusic` > 播放入口:`BattleAudio`、`gg.audio.playEffect` / `playMusic`
老项目 `sfx/battle`、`sfx/wave` 已删除。无对应新资源的旧播放点不再播,不用其它音效顶替。 老项目 `sfx/battle`、`sfx/wave` 已删除。无对应新资源的旧播放点不再播。
**当前局内**见 [需求-五行合成.md](./需求-五行合成.md):同级数字合成 + 单格上阵;普攻/技能按 **Hero.herotype / Weapon.type** 复用现有 `sfx/weapon` 资源。
## 1. BGM ## 1. BGM
| 资源 | 时机 | | 资源 | 时机 |
|------|------| |------|------|
| `bgm/首页背景音` | 大厅 | | `bgm/首页背景音` | 大厅 |
| `bgm/局内背景音` | 进战斗 | | `bgm/局内背景音` | 进战斗(`BattleAudio.playBgmBattle`) |
## 2. UI(`sfx/ui/`) ## 2. UI(`sfx/ui/`)
| 资源 | 时机 | | 资源 | 时机 |
|------|------| |------|------|
| 点击按钮 | 按钮上的 `ClickButton` 组件(`gg.audio.playClick`) | | 点击按钮 | `ClickButton`(`gg.audio.playClick`);三选一/商店翻牌 |
| 体力消耗 | 首页开战扣体力飞行 | | 体力消耗 | 首页开战扣体力飞行 |
| 匹配完成 | 匹配页切到「站」 | | 匹配完成 | 匹配页切到「站」 |
| 可刷新通知 | 召唤按钮首次变为可刷新 | | 可刷新通知 | 召唤按钮首次变为可刷新 |
| 合成 | 同字升级 / 神将组合 | | 合成 | 同级数字升级(`BagMerged` / `MapUpgraded` → `playUpgrade`) |
| 怪物受击 | 本机攻击命中 | | 怪物受击 | 本机攻击命中 |
| 抽奖转盘 | 每日商店转盘每转一格 | | 抽奖转盘 | 每日商店转盘每转一格 |
| 格子放置 | 字落入格子 | | 格子放置 | 字落入格子 |
| 武器拖动 | 局内拖字开始 | | 武器拖动 | 局内拖字开始 |
| 游戏失败 | 失败结算打开 | | 游戏失败 | 失败结算(`BattleAudio.playFail`) |
| 游戏胜利 | 胜利结算打开 | | 游戏胜利 | 胜利结算(`BattleAudio.playVictory`) |
| 砚台出字 | 背包刷新每冒出一个字 | | 砚台出字 | 背包刷新每冒出一个字 |
| 结算金币效果 | 胜负领取金币飞行 | | 结算金币效果 | 胜负领取金币飞行;局内波次结算金币飞行 |
| 铲子铲地 | 铲子挖地 | | BOSS出场 | Boss 出场弹窗 |
| 危险警报 | 怪物危机提示每轮动画 |
## 3. 语音(`sfx/voice/`) ### 2.1 现有资源、当前无播放点(勿接已删玩法)
合成神将时播对应语音(仅下列有资源):孙悟空 / 哪吒 / 杨戬 / 王母 / 玉帝 / 李靖。 | 资源 | 说明 |
|------|------|
| 开场号角 | 开场表演怪已删 |
| 铲子铲地 | 开挖/铲子玩法已删 |
| `sfx/voice/*合成` | 多格神将组合已废弃 |
## 4. 武器(`sfx/weapon/`) ## 3. 武器(`sfx/weapon/`)— 五行复用
### 4.1 普攻(按 Hero.weapon) 播放:`BattleAudio.playWeaponAttack` / `playWeaponSkill`(仅本机 `Team.ownerId`)。
| weapon | 资源 | 代表 | - 近战等:`SkillController` PresentationCue 时播一次
|--------|------|------| - **弹道 atk_type 12/13/17**(含水弹连发):在 `SkillVisualChannel` **真正出手**时播;连发每发一次
| 1 | 刀攻击 | 刀、悟能、木吒、玉帝、阎王、李靖 |
| 2 | 枪攻击 | 枪、哪吒、金吒、杨戬 |
| 3 | 棍攻击 | 棍、悟空、悟净 |
| 4 | 箭攻击 | 弓、王母、嫦娥 |
### 4.2 英雄主动 ### 3.1 普攻(`Hero.herotype`)
| Hero.id | 资源 | | herotype | 资源 |
|---------|------| |----------|------|
| 11 悟空 | 孙悟空技能 | | 1 金 | 剑攻击 |
| 13 悟净 | 悟净技能 | | 2 木 | 棍攻击 |
| 17 杨戬 | 杨戬技能音效 | | 3 水 | 法杖攻击 |
| 20 阎王 | 阎王技能 | | 4 火 | 箭攻击 |
| 22 李靖 | 李靖技能 | | 5 土 | 枪攻击 |
### 4.3 专属武器技能 ### 3.2 英雄主动(`Hero.herotype` → 现有技能资源)
| Skill.id | 资源 | | herotype | 代表技能 | 资源 |
|----------|------| |----------|----------|------|
| 20501 如意金箍棒 | 金箍棒技能 | | 1 金 | 剑阵 | 孙悟空技能 |
| 20509 玲珑宝塔 | 玲珑宝塔技能 | | 2 木 | 飞棍 | 悟净技能 |
| 3 水 | 暴风雪 | 阎王技能 |
| 4 火 | 箭雨 | 杨戬技能音效 |
| 5 土 | 冲锋 | 李靖技能 |
### 3.3 宝石武器技能(`Weapon.type`:2金…6土 → element 1…5)
| element | 资源 |
|---------|------|
| 1 金 | 金箍棒技能 |
| 2 木 | 悟净技能 |
| 3 水 | 阎王技能 |
| 4 火 | 杨戬技能音效 |
| 5 土 | 玲珑宝塔技能 |
### 4.4 局内掉落 ### 3.4 局内掉落
| 资源 | 时机 | | 资源 | 时机 |
|------|------| |------|------|
| 碎片掉落 | 击杀后武器碎片飞出时 | | 碎片掉落 | 击杀后宝石飞出时 |

Loading…
Cancel
Save