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>
This commit is contained in:
Claude
2026-01-23 16:20:10 +08:00
parent 021f6a54f5
commit 16223c89a5
25 changed files with 2731 additions and 318 deletions

334
utils/soundSystem.js Normal file
View File

@@ -0,0 +1,334 @@
/**
* 音效系统
* 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()