EG/plugins/user/dynamic_music_system/plugin.py
2025-12-12 16:16:15 +08:00

832 lines
28 KiB
Python
Raw 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.

"""
动态音乐系统插件
根据游戏状态和环境条件动态生成和控制音乐
"""
import math
import time
import uuid
from typing import Dict, List, Optional, Any, Callable
from plugins.plugin_manager import BasePlugin
from panda3d.core import AudioSound, NodePath
class Plugin(BasePlugin):
"""
动态音乐系统插件
根据游戏状态和环境条件动态生成和控制音乐
"""
def __init__(self, plugin_manager, name):
super().__init__(plugin_manager, name)
self.config = {
"version": "1.0.0",
"author": "EG Team",
"description": "动态音乐系统,支持根据游戏状态和环境条件实时生成和控制音乐"
}
# 核心组件
self.music_manager = None
self.transition_manager = None
self.composition_engine = None
self.analysis_engine = None
self.effect_processor = None
# 音乐状态
self.current_theme = None
self.current_intensity = 0.0
self.target_intensity = 0.0
self.intensity_change_rate = 1.0
self.current_layer_volumes = {}
# 游戏状态跟踪
self.game_state = {}
self.environment_conditions = {}
self.player_state = {}
# 音乐库
self.themes = {}
self.layers = {}
self.transitions = {}
self.sound_effects = {}
# GUI元素
self.gui_elements = []
# 事件处理器
self.event_handlers = {}
# 插件状态
self.is_initialized = False
self.is_enabled = False
self.is_playing = False
# 性能监控
self.performance_stats = {
'initialization_time': 0.0,
'enable_time': 0.0,
'last_update_time': 0.0,
'total_updates': 0,
'total_processing_time': 0.0,
'themes_loaded': 0,
'transitions_processed': 0
}
# 配置参数
self.master_volume = 1.0
self.crossfade_duration = 2.0
self.intensity_smoothing = 0.1
self.auto_intensity = True
def initialize(self) -> bool:
"""
初始化插件
"""
try:
if self.is_initialized:
return True
init_start_time = time.time()
print(f"初始化动态音乐系统插件: {self.name}")
# 初始化核心组件
from .core.music_manager import MusicManager
from .transitions.transition_manager import TransitionManager
from .composition.composition_engine import CompositionEngine
from .analysis.analysis_engine import AnalysisEngine
from .effects.effect_processor import EffectProcessor
self.music_manager = MusicManager(self)
self.transition_manager = TransitionManager(self)
self.composition_engine = CompositionEngine(self)
self.analysis_engine = AnalysisEngine(self)
self.effect_processor = EffectProcessor(self)
# 加载音乐资源
self._load_music_resources()
self.is_initialized = True
self.performance_stats['initialization_time'] = time.time() - init_start_time
print("✓ 动态音乐系统插件初始化完成")
return True
except Exception as e:
print(f"✗ 动态音乐系统插件初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用插件
"""
if not super().enable():
return False
if self.is_enabled:
return True
try:
enable_start_time = time.time()
print(f"启用动态音乐系统插件: {self.name}")
# 注册事件处理器
self._register_event_handlers()
# 创建GUI界面
self._create_gui()
self.is_enabled = True
self.performance_stats['enable_time'] = time.time() - enable_start_time
print("✓ 动态音乐系统插件启用成功")
return True
except Exception as e:
print(f"✗ 动态音乐系统插件启用失败: {e}")
import traceback
traceback.print_exc()
return False
def disable(self) -> bool:
"""
禁用插件
"""
if not super().disable():
return False
if not self.is_enabled:
return True
try:
print(f"禁用动态音乐系统插件: {self.name}")
# 清理GUI界面
self._cleanup_gui()
# 移除事件处理器
self._unregister_event_handlers()
# 停止所有音乐
self.stop_all_music()
self.is_enabled = False
print("✓ 动态音乐系统插件禁用成功")
return True
except Exception as e:
print(f"✗ 动态音乐系统插件禁用失败: {e}")
import traceback
traceback.print_exc()
return False
def finalize(self):
"""
清理插件资源
"""
print(f"清理动态音乐系统插件资源: {self.name}")
# 停止所有音乐
self.stop_all_music()
# 清理所有组件
components = [
self.music_manager,
self.transition_manager,
self.composition_engine,
self.analysis_engine,
self.effect_processor
]
for component in components:
if component:
component.cleanup()
self.music_manager = None
self.transition_manager = None
self.composition_engine = None
self.analysis_engine = None
self.effect_processor = None
self.themes.clear()
self.layers.clear()
self.transitions.clear()
self.sound_effects.clear()
self.is_initialized = False
self.is_enabled = False
def _register_event_handlers(self):
"""
注册事件处理器
"""
world = self.plugin_manager.world
# 音乐控制快捷键
self.event_handlers['f1'] = self.toggle_debug_mode
self.event_handlers['f2'] = self.toggle_music_system
self.event_handlers['f3'] = self.play_test_theme
self.event_handlers['f4'] = self.increase_intensity
self.event_handlers['f5'] = self.decrease_intensity
self.event_handlers['f6'] = self.trigger_battle_music
self.event_handlers['f7'] = self.trigger_exploration_music
self.event_handlers['f8'] = self.trigger_ambient_music
self.event_handlers['f9'] = self.show_stats
self.event_handlers['f10'] = self.toggle_auto_intensity
self.event_handlers['f11'] = self.show_detailed_stats
self.event_handlers['f12'] = self.reset_music_system
# 注册事件
for event, handler in self.event_handlers.items():
world.accept(event, handler)
world.accept(event.upper(), handler) # 同时注册大写事件
def _unregister_event_handlers(self):
"""
移除事件处理器
"""
world = self.plugin_manager.world
for event in self.event_handlers.keys():
world.ignore(event)
world.ignore(event.upper())
self.event_handlers.clear()
def _create_gui(self):
"""
创建GUI界面
"""
try:
world = self.plugin_manager.world
# 检查是否有GUI管理器
if not hasattr(world, 'gui_manager') or not world.gui_manager:
print("⚠ GUI管理器不可用跳过GUI创建")
return
gui_manager = world.gui_manager
# 创建音乐系统控制面板
panel_button = gui_manager.createGUIButton(
pos=(0.7, 0, 0.35),
text="动态音乐",
size=0.06
)
self.gui_elements.append(panel_button)
# 创建功能按钮
functions = [
("调试模式 [F1]", self.toggle_debug_mode),
("切换系统 [F2]", self.toggle_music_system),
("测试主题 [F3]", self.play_test_theme),
("增加强度 [F4]", self.increase_intensity),
("降低强度 [F5]", self.decrease_intensity),
("战斗音乐 [F6]", self.trigger_battle_music),
("探索音乐 [F7]", self.trigger_exploration_music),
("环境音乐 [F8]", self.trigger_ambient_music),
("音乐统计 [F9]", self.show_stats),
("自动强度 [F10]", self.toggle_auto_intensity),
("详细统计 [F11]", self.show_detailed_stats),
("重置系统 [F12]", self.reset_music_system)
]
for i, (name, callback) in enumerate(functions):
button = gui_manager.createGUIButton(
pos=(0.7, 0, 0.28 - i * 0.055),
text=name,
size=0.045
)
self.gui_elements.append(button)
world.accept(f"music_system_function_{i}", callback)
except Exception as e:
print(f"⚠ GUI创建失败: {e}")
def _cleanup_gui(self):
"""
清理GUI界面
"""
try:
world = self.plugin_manager.world
if hasattr(world, 'gui_manager') and world.gui_manager:
gui_manager = world.gui_manager
# 删除所有GUI元素
for element in self.gui_elements:
gui_manager.deleteGUIElement(element)
self.gui_elements.clear()
except Exception as e:
print(f"⚠ GUI清理失败: {e}")
# 事件处理方法
def toggle_debug_mode(self):
"""切换调试模式"""
print("✓ 动态音乐系统调试模式切换")
def toggle_music_system(self):
"""切换音乐系统"""
if self.is_playing:
self.stop_all_music()
print("✓ 动态音乐系统已停止")
else:
self.play_theme("ambient")
print("✓ 动态音乐系统已启动")
def play_test_theme(self):
"""播放测试主题"""
print("✓ 播放测试音乐主题")
try:
# 创建一个测试主题
self.create_theme("test_theme", {
'layers': ['test_layer_1', 'test_layer_2'],
'bpm': 120,
'key': 'C',
'mood': 'neutral'
})
self.play_theme("test_theme")
except Exception as e:
print(f"✗ 播放测试主题失败: {e}")
def increase_intensity(self):
"""增加音乐强度"""
self.target_intensity = min(1.0, self.target_intensity + 0.1)
print(f"✓ 音乐强度增加到: {self.target_intensity:.2f}")
def decrease_intensity(self):
"""降低音乐强度"""
self.target_intensity = max(0.0, self.target_intensity - 0.1)
print(f"✓ 音乐强度降低到: {self.target_intensity:.2f}")
def trigger_battle_music(self):
"""触发战斗音乐"""
print("✓ 触发战斗音乐")
self.target_intensity = 1.0
# 这里可以添加更多战斗音乐相关的逻辑
def trigger_exploration_music(self):
"""触发探索音乐"""
print("✓ 触发探索音乐")
self.target_intensity = 0.6
# 这里可以添加更多探索音乐相关的逻辑
def trigger_ambient_music(self):
"""触发环境音乐"""
print("✓ 触发环境音乐")
self.target_intensity = 0.3
# 这里可以添加更多环境音乐相关的逻辑
def show_stats(self):
"""显示音乐系统统计信息"""
stats = self.get_stats()
print("=== 动态音乐系统统计信息 ===")
for key, value in stats.items():
print(f" {key}: {value}")
def toggle_auto_intensity(self):
"""切换自动强度控制"""
self.auto_intensity = not self.auto_intensity
print(f"✓ 自动强度控制已{'启用' if self.auto_intensity else '禁用'}")
def show_detailed_stats(self):
"""显示详细统计信息"""
print("=== 动态音乐系统详细统计信息 ===")
# 显示各组件统计
components = [
('音乐管理器', self.music_manager),
('过渡管理器', self.transition_manager),
('作曲引擎', self.composition_engine),
('分析引擎', self.analysis_engine),
('效果处理器', self.effect_processor)
]
for name, component in components:
if component:
component_stats = component.get_stats()
print(f"{name}统计:")
for key, value in component_stats.items():
print(f" {key}: {value}")
# 显示性能统计
perf_stats = self.performance_stats
print("性能统计:")
for key, value in perf_stats.items():
print(f" {key}: {value}")
def reset_music_system(self):
"""重置音乐系统"""
print("✓ 重置动态音乐系统")
self.current_theme = None
self.current_intensity = 0.0
self.target_intensity = 0.0
self.current_layer_volumes.clear()
self.stop_all_music()
print("✓ 动态音乐系统已重置")
def _load_music_resources(self):
"""加载音乐资源"""
# 这里应该加载实际的音乐文件
# 为演示目的,我们创建一些虚拟的音乐主题和层
# 创建示例主题
self.themes = {
'ambient': {
'layers': ['ambient_base', 'ambient_high'],
'bpm': 60,
'key': 'Am',
'mood': 'calm'
},
'exploration': {
'layers': ['exploration_base', 'exploration_melody'],
'bpm': 90,
'key': 'C',
'mood': 'adventurous'
},
'battle': {
'layers': ['battle_drums', 'battle_melody', 'battle_bass'],
'bpm': 140,
'key': 'Em',
'mood': 'intense'
},
'victory': {
'layers': ['victory_fanfare', 'victory_melody'],
'bpm': 120,
'key': 'C',
'mood': 'triumphant'
},
'defeat': {
'layers': ['defeat_ambient', 'defeat_melody'],
'bpm': 50,
'key': 'Am',
'mood': 'melancholic'
}
}
# 创建示例层
self.layers = {
'ambient_base': {'volume': 0.6, 'loop': True, 'priority': 1},
'ambient_high': {'volume': 0.3, 'loop': True, 'priority': 2},
'exploration_base': {'volume': 0.7, 'loop': True, 'priority': 1},
'exploration_melody': {'volume': 0.5, 'loop': True, 'priority': 2},
'battle_drums': {'volume': 0.8, 'loop': True, 'priority': 1},
'battle_melody': {'volume': 0.7, 'loop': True, 'priority': 2},
'battle_bass': {'volume': 0.6, 'loop': True, 'priority': 3},
'victory_fanfare': {'volume': 1.0, 'loop': False, 'priority': 1},
'victory_melody': {'volume': 0.8, 'loop': False, 'priority': 2},
'defeat_ambient': {'volume': 0.4, 'loop': True, 'priority': 1},
'defeat_melody': {'volume': 0.6, 'loop': True, 'priority': 2}
}
# 创建示例过渡
self.transitions = {
'ambient_to_exploration': {
'from_theme': 'ambient',
'to_theme': 'exploration',
'duration': 3.0,
'type': 'crossfade'
},
'exploration_to_battle': {
'from_theme': 'exploration',
'to_theme': 'battle',
'duration': 2.0,
'type': 'crossfade'
},
'battle_to_victory': {
'from_theme': 'battle',
'to_theme': 'victory',
'duration': 1.5,
'type': 'cut'
},
'battle_to_defeat': {
'from_theme': 'battle',
'to_theme': 'defeat',
'duration': 1.5,
'type': 'cut'
}
}
self.performance_stats['themes_loaded'] = len(self.themes)
print("✓ 音乐资源加载完成")
def get_stats(self) -> Dict[str, Any]:
"""
获取插件统计信息
Returns:
统计信息字典
"""
stats = self.performance_stats.copy()
# 收集各组件统计
components = [
self.music_manager,
self.transition_manager,
self.composition_engine,
self.analysis_engine,
self.effect_processor
]
component_names = [
'music', 'transition', 'composition', 'analysis', 'effect'
]
for i, component in enumerate(components):
if component:
component_stats = component.get_stats()
for key, value in component_stats.items():
stats[f"{component_names[i]}_{key}"] = value
# 添加音乐系统统计
stats['current_theme'] = self.current_theme
stats['current_intensity'] = self.current_intensity
stats['target_intensity'] = self.target_intensity
stats['is_playing'] = self.is_playing
stats['auto_intensity'] = self.auto_intensity
return stats
# 公共接口方法
def create_theme(self, theme_name: str, theme_data: Dict[str, Any]) -> bool:
"""
创建音乐主题
Args:
theme_name: 主题名称
theme_data: 主题数据
Returns:
是否创建成功
"""
try:
self.themes[theme_name] = theme_data.copy()
print(f"✓ 音乐主题创建成功: {theme_name}")
return True
except Exception as e:
print(f"✗ 创建音乐主题失败: {e}")
return False
def play_theme(self, theme_name: str, force: bool = False) -> bool:
"""
播放音乐主题
Args:
theme_name: 主题名称
force: 是否强制播放(跳过过渡)
Returns:
是否播放成功
"""
if theme_name not in self.themes:
print(f"✗ 音乐主题不存在: {theme_name}")
return False
try:
if self.music_manager:
success = self.music_manager.play_theme(theme_name, force)
if success:
self.current_theme = theme_name
self.is_playing = True
print(f"✓ 音乐主题开始播放: {theme_name}")
return success
return False
except Exception as e:
print(f"✗ 播放音乐主题失败: {e}")
return False
def stop_theme(self, theme_name: str) -> bool:
"""
停止音乐主题
Args:
theme_name: 主题名称
Returns:
是否停止成功
"""
if theme_name not in self.themes:
print(f"✗ 音乐主题不存在: {theme_name}")
return False
try:
if self.music_manager:
success = self.music_manager.stop_theme(theme_name)
if success and self.current_theme == theme_name:
self.current_theme = None
if not self.music_manager.get_active_themes():
self.is_playing = False
print(f"✓ 音乐主题已停止: {theme_name}")
return success
return False
except Exception as e:
print(f"✗ 停止音乐主题失败: {e}")
return False
def stop_all_music(self):
"""停止所有音乐"""
try:
if self.music_manager:
self.music_manager.stop_all()
self.current_theme = None
self.is_playing = False
print("✓ 所有音乐已停止")
except Exception as e:
print(f"✗ 停止所有音乐失败: {e}")
def set_intensity(self, intensity: float):
"""
设置音乐强度
Args:
intensity: 强度值 (0.0-1.0)
"""
self.target_intensity = max(0.0, min(1.0, intensity))
print(f"✓ 音乐强度设置为: {self.target_intensity:.2f}")
def get_intensity(self) -> float:
"""
获取当前音乐强度
Returns:
当前强度值 (0.0-1.0)
"""
return self.current_intensity
def set_layer_volume(self, layer_name: str, volume: float):
"""
设置音乐层音量
Args:
layer_name: 层名称
volume: 音量值 (0.0-1.0)
"""
if self.music_manager:
self.music_manager.set_layer_volume(layer_name, volume)
self.current_layer_volumes[layer_name] = volume
print(f"✓ 音乐层 {layer_name} 音量设置为: {volume:.2f}")
def get_layer_volume(self, layer_name: str) -> float:
"""
获取音乐层音量
Args:
layer_name: 层名称
Returns:
音量值 (0.0-1.0)
"""
return self.current_layer_volumes.get(layer_name, 1.0)
def create_transition(self, transition_name: str, transition_data: Dict[str, Any]) -> bool:
"""
创建音乐过渡
Args:
transition_name: 过渡名称
transition_data: 过渡数据
Returns:
是否创建成功
"""
try:
self.transitions[transition_name] = transition_data.copy()
if self.transition_manager:
self.transition_manager.register_transition(transition_name, transition_data)
print(f"✓ 音乐过渡创建成功: {transition_name}")
return True
except Exception as e:
print(f"✗ 创建音乐过渡失败: {e}")
return False
def trigger_transition(self, transition_name: str) -> bool:
"""
触发音乐过渡
Args:
transition_name: 过渡名称
Returns:
是否触发成功
"""
if transition_name not in self.transitions:
print(f"✗ 音乐过渡不存在: {transition_name}")
return False
try:
if self.transition_manager:
success = self.transition_manager.trigger_transition(transition_name)
if success:
self.performance_stats['transitions_processed'] += 1
print(f"✓ 音乐过渡已触发: {transition_name}")
return success
return False
except Exception as e:
print(f"✗ 触发音乐过渡失败: {e}")
return False
def set_game_state(self, state: Dict[str, Any]):
"""
设置游戏状态
Args:
state: 游戏状态字典
"""
self.game_state.update(state)
print(f"✓ 游戏状态已更新: {state}")
def set_environment_conditions(self, conditions: Dict[str, Any]):
"""
设置环境条件
Args:
conditions: 环境条件字典
"""
self.environment_conditions.update(conditions)
print(f"✓ 环境条件已更新: {conditions}")
def set_player_state(self, state: Dict[str, Any]):
"""
设置玩家状态
Args:
state: 玩家状态字典
"""
self.player_state.update(state)
print(f"✓ 玩家状态已更新: {state}")
def set_master_volume(self, volume: float):
"""
设置主音量
Args:
volume: 主音量 (0.0-1.0)
"""
self.master_volume = max(0.0, min(1.0, volume))
if self.music_manager:
self.music_manager.set_master_volume(self.master_volume)
print(f"✓ 主音量已设置: {self.master_volume:.2f}")
def get_master_volume(self) -> float:
"""
获取主音量
Returns:
主音量 (0.0-1.0)
"""
return self.master_volume
def update(self, dt: float):
"""
更新插件状态
Args:
dt: 时间增量
"""
update_start_time = time.time()
# 更新当前强度(平滑过渡到目标强度)
if self.current_intensity != self.target_intensity:
intensity_diff = self.target_intensity - self.current_intensity
self.current_intensity += intensity_diff * self.intensity_smoothing
# 确保不会超过目标值
if (intensity_diff > 0 and self.current_intensity > self.target_intensity) or \
(intensity_diff < 0 and self.current_intensity < self.target_intensity):
self.current_intensity = self.target_intensity
# 更新各组件
components = [
self.music_manager,
self.transition_manager,
self.composition_engine,
self.analysis_engine,
self.effect_processor
]
for component in components:
if component:
component.update(dt)
# 更新性能统计
self.performance_stats['total_updates'] += 1
self.performance_stats['last_update_time'] = time.time()
self.performance_stats['total_processing_time'] += (time.time() - update_start_time)
# 自动强度控制
if self.auto_intensity:
self._update_auto_intensity()
def _update_auto_intensity(self):
"""更新自动强度控制"""
# 基于游戏状态自动调整音乐强度
# 这里是一个简化的实现,实际应用中会更复杂
# 检查是否有战斗状态
if self.game_state.get('in_combat', False):
self.target_intensity = 1.0
# 检查是否有探索状态
elif self.game_state.get('exploring', False):
self.target_intensity = 0.6
# 默认环境音乐
else:
self.target_intensity = 0.3