Files
text-adventure-game/utils/soundSystem.js
Claude 16223c89a5 feat: 实现游戏核心系统和UI组件
核心系统:
- combatSystem: 战斗逻辑、伤害计算、战斗状态管理
- skillSystem: 技能系统、技能解锁、经验值、里程碑
- taskSystem: 任务系统、任务类型、任务执行和完成
- eventSystem: 事件系统、随机事件处理
- environmentSystem: 环境系统、时间流逝、区域效果
- levelingSystem: 升级系统、属性成长
- soundSystem: 音效系统

配置文件:
- enemies: 敌人配置、掉落表
- events: 事件配置、事件效果
- items: 物品配置、装备属性
- locations: 地点配置、探索事件
- skills: 技能配置、技能树

UI组件:
- CraftingDrawer: 制造界面
- InventoryDrawer: 背包界面
- 其他UI优化和动画

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:20:10 +08:00

335 lines
9.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 音效系统
* Phase 3 UI美化 - 音效支持
*
* 注意uni-app的音频功能有限不同平台支持情况不同
* H5端使用Web Audio API
* 小程序端:使用 wx.createInnerAudioContext()
* App端使用 plus.audio
*/
/**
* 音效类型枚举
*/
export const SOUND_TYPES = {
// UI音效
CLICK: 'click',
OPEN: 'open',
CLOSE: 'close',
TOGGLE: 'toggle',
// 游戏音效
COMBAT_START: 'combat_start',
COMBAT_HIT: 'combat_hit',
COMBAT_HIT_PLAYER: 'combat_hit_player',
COMBAT_VICTORY: 'combat_victory',
COMBAT_DEFEAT: 'combat_defeat',
// 奖励音效
REWARD: 'reward',
LEVEL_UP: 'level_up',
SKILL_UP: 'skill_up',
// 错误音效
ERROR: 'error',
WARNING: 'warning',
// 系统音效
NOTIFICATION: 'notification',
MESSAGE: 'message'
}
/**
* 音效配置
*/
const SOUND_CONFIG = {
// 是否启用音效
enabled: true,
// 音量 (0-1)
volume: 0.5,
// 音效文件路径配置 (需要实际文件)
soundPaths: {
[SOUND_TYPES.CLICK]: '/static/sounds/click.mp3',
[SOUND_TYPES.OPEN]: '/static/sounds/open.mp3',
[SOUND_TYPES.CLOSE]: '/static/sounds/close.mp3',
[SOUND_TYPES.TOGGLE]: '/static/sounds/toggle.mp3',
[SOUND_TYPES.COMBAT_START]: '/static/sounds/combat_start.mp3',
[SOUND_TYPES.COMBAT_HIT]: '/static/sounds/combat_hit.mp3',
[SOUND_TYPES.COMBAT_HIT_PLAYER]: '/static/sounds/combat_hit_player.mp3',
[SOUND_TYPES.COMBAT_VICTORY]: '/static/sounds/combat_victory.mp3',
[SOUND_TYPES.COMBAT_DEFEAT]: '/static/sounds/combat_defeat.mp3',
[SOUND_TYPES.REWARD]: '/static/sounds/reward.mp3',
[SOUND_TYPES.LEVEL_UP]: '/static/sounds/level_up.mp3',
[SOUND_TYPES.SKILL_UP]: '/static/sounds/skill_up.mp3',
[SOUND_TYPES.ERROR]: '/static/sounds/error.mp3',
[SOUND_TYPES.WARNING]: '/static/sounds/warning.mp3',
[SOUND_TYPES.NOTIFICATION]: '/static/sounds/notification.mp3',
[SOUND_TYPES.MESSAGE]: '/static/sounds/message.mp3'
}
}
/**
* 音频上下文缓存
*/
let audioContext = null
let audioCache = {}
/**
* 初始化音频系统
*/
export function initSoundSystem() {
// #ifdef H5
try {
audioContext = new (window.AudioContext || window.webkitAudioContext)()
} catch (e) {
console.warn('Web Audio API not supported:', e)
}
// #endif
// #ifdef MP-WEIXIN
// 小程序端使用微信音频API
// #endif
// 从本地存储加载音效设置
loadSoundSettings()
}
/**
* 播放音效
* @param {String} soundType - 音效类型
* @param {Object} options - 选项 { volume, speed }
*/
export function playSound(soundType, options = {}) {
if (!SOUND_CONFIG.enabled) {
return { success: false, message: '音效已禁用' }
}
const volume = options.volume !== undefined ? options.volume : SOUND_CONFIG.volume
// #ifdef H5
return playSoundH5(soundType, volume)
// #endif
// #ifdef MP-WEIXIN
return playSoundWeixin(soundType, volume)
// #endif
// #ifdef APP-PLUS
return playSoundApp(soundType, volume)
// #endif
return { success: false, message: '当前平台不支持音效' }
}
/**
* H5端播放音效 (使用Web Audio API合成简单音效)
*/
function playSoundH5(soundType, volume) {
if (!audioContext) {
initSoundSystem()
if (!audioContext) {
return { success: false, message: '音频上下文初始化失败' }
}
}
// 恢复音频上下文(某些浏览器需要用户交互后才能恢复)
if (audioContext.state === 'suspended') {
audioContext.resume()
}
try {
// 创建振荡器生成简单音效
const oscillator = audioContext.createOscillator()
const gainNode = audioContext.createGain()
oscillator.connect(gainNode)
gainNode.connect(audioContext.destination)
// 根据音效类型设置不同的频率和包络
const soundParams = getSoundParameters(soundType)
oscillator.type = soundParams.type || 'sine'
oscillator.frequency.setValueAtTime(soundParams.frequency, audioContext.currentTime)
// 设置音量包络
const now = audioContext.currentTime
gainNode.gain.setValueAtTime(0, now)
gainNode.gain.linearRampToValueAtTime(volume * 0.3, now + 0.01)
gainNode.gain.exponentialRampToValueAtTime(0.001, now + soundParams.duration)
oscillator.start(now)
oscillator.stop(now + soundParams.duration)
return { success: true }
} catch (e) {
console.warn('播放音效失败:', e)
return { success: false, message: e.message }
}
}
/**
* 获取音效参数 (用于合成音效)
*/
function getSoundParameters(soundType) {
const params = {
// 点击音效 - 短促的高音
[SOUND_TYPES.CLICK]: { type: 'sine', frequency: 800, duration: 0.05 },
// 打开音效 - 上升音调
[SOUND_TYPES.OPEN]: { type: 'sine', frequency: 400, duration: 0.15 },
// 关闭音效 - 下降音调
[SOUND_TYPES.CLOSE]: { type: 'sine', frequency: 300, duration: 0.1 },
// 切换音效
[SOUND_TYPES.TOGGLE]: { type: 'square', frequency: 500, duration: 0.08 },
// 战斗开始 - 低沉
[SOUND_TYPES.COMBAT_START]: { type: 'sawtooth', frequency: 150, duration: 0.3 },
// 命中敌人 - 尖锐
[SOUND_TYPES.COMBAT_HIT]: { type: 'square', frequency: 1200, duration: 0.08 },
// 被命中 - 低频
[SOUND_TYPES.COMBAT_HIT_PLAYER]: { type: 'sawtooth', frequency: 200, duration: 0.15 },
// 胜利 - 上升音阶
[SOUND_TYPES.COMBAT_VICTORY]: { type: 'sine', frequency: 523, duration: 0.4 },
// 失败 - 下降音
[SOUND_TYPES.COMBAT_DEFEAT]: { type: 'sawtooth', frequency: 100, duration: 0.4 },
// 奖励 - 悦耳的高音
[SOUND_TYPES.REWARD]: { type: 'sine', frequency: 880, duration: 0.2 },
// 升级 - 双音
[SOUND_TYPES.LEVEL_UP]: { type: 'sine', frequency: 659, duration: 0.3 },
// 技能升级
[SOUND_TYPES.SKILL_UP]: { type: 'sine', frequency: 784, duration: 0.25 },
// 错误 - 低频嗡鸣
[SOUND_TYPES.ERROR]: { type: 'sawtooth', frequency: 100, duration: 0.2 },
// 警告
[SOUND_TYPES.WARNING]: { type: 'square', frequency: 440, duration: 0.15 },
// 通知
[SOUND_TYPES.NOTIFICATION]: { type: 'sine', frequency: 660, duration: 0.2 },
// 消息
[SOUND_TYPES.MESSAGE]: { type: 'sine', frequency: 587, duration: 0.15 }
}
return params[soundType] || { type: 'sine', frequency: 440, duration: 0.1 }
}
/**
* 微信小程序端播放音效
*/
function playSoundWeixin(soundType, volume) {
// #ifdef MP-WEIXIN
const soundPath = SOUND_CONFIG.soundPaths[soundType]
if (!soundPath) {
return { success: false, message: '音效文件未配置' }
}
try {
const audio = uni.createInnerAudioContext()
audio.src = soundPath
audio.volume = volume
audio.play()
return { success: true }
} catch (e) {
return { success: false, message: e.message }
}
// #endif
return { success: false, message: '非微信小程序环境' }
}
/**
* App端播放音效
*/
function playSoundApp(soundType, volume) {
// #ifdef APP-PLUS
const soundPath = SOUND_CONFIG.soundPaths[soundType]
if (!soundPath) {
return { success: false, message: '音效文件未配置' }
}
try {
const player = plus.audio.createPlayer(soundPath)
player.setVolume(volume * 100)
player.play()
return { success: true }
} catch (e) {
return { success: false, message: e.message }
}
// #endif
return { success: false, message: '非App环境' }
}
/**
* 设置音效开关
* @param {Boolean} enabled - 是否启用音效
*/
export function setSoundEnabled(enabled) {
SOUND_CONFIG.enabled = enabled
saveSoundSettings()
}
/**
* 设置音量
* @param {Number} volume - 音量 (0-1)
*/
export function setSoundVolume(volume) {
SOUND_CONFIG.volume = Math.max(0, Math.min(1, volume))
saveSoundSettings()
}
/**
* 获取音效设置
* @returns {Object} { enabled, volume }
*/
export function getSoundSettings() {
return {
enabled: SOUND_CONFIG.enabled,
volume: SOUND_CONFIG.volume
}
}
/**
* 保存音效设置到本地存储
*/
function saveSoundSettings() {
try {
uni.setStorageSync('soundSettings', JSON.stringify({
enabled: SOUND_CONFIG.enabled,
volume: SOUND_CONFIG.volume
}))
} catch (e) {
console.warn('保存音效设置失败:', e)
}
}
/**
* 从本地存储加载音效设置
*/
function loadSoundSettings() {
try {
const settings = uni.getStorageSync('soundSettings')
if (settings) {
const parsed = JSON.parse(settings)
SOUND_CONFIG.enabled = parsed.enabled !== undefined ? parsed.enabled : true
SOUND_CONFIG.volume = parsed.volume !== undefined ? parsed.volume : 0.5
}
} catch (e) {
console.warn('加载音效设置失败:', e)
}
}
/**
* 快捷音效播放函数
*/
export const playClick = () => playSound(SOUND_TYPES.CLICK)
export const playOpen = () => playSound(SOUND_TYPES.OPEN)
export const playClose = () => playSound(SOUND_TYPES.CLOSE)
export const playCombatStart = () => playSound(SOUND_TYPES.COMBAT_START)
export const playCombatHit = () => playSound(SOUND_TYPES.COMBAT_HIT)
export const playCombatVictory = () => playSound(SOUND_TYPES.COMBAT_VICTORY)
export const playCombatDefeat = () => playSound(SOUND_TYPES.COMBAT_DEFEAT)
export const playReward = () => playSound(SOUND_TYPES.REWARD)
export const playLevelUp = () => playSound(SOUND_TYPES.LEVEL_UP)
export const playSkillUp = () => playSound(SOUND_TYPES.SKILL_UP)
export const playError = () => playSound(SOUND_TYPES.ERROR)
export const playWarning = () => playSound(SOUND_TYPES.WARNING)
// 初始化音效系统
initSoundSystem()