EG/plugins/user/weather_season_system/particles/particle_effects.py
2025-12-12 16:16:15 +08:00

745 lines
26 KiB
Python

"""
粒子效果系统
负责管理天气和季节的粒子效果,如雨滴、雪花、雾气等
"""
import time
import random
from typing import Dict, Any, List, Optional, Tuple
import math
class ParticleEffects:
"""
粒子效果系统
负责管理天气和季节的粒子效果,包括粒子生成、更新和渲染
"""
def __init__(self, plugin):
"""
初始化粒子效果系统
Args:
plugin: 天气和季节系统插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 粒子类型配置
self.particle_types = {
'rain_drop': {
'name': '雨滴',
'shape': 'line', # 'point', 'line', 'sprite'
'size_range': (0.1, 0.5),
'speed_range': (10.0, 25.0),
'lifetime_range': (0.5, 2.0),
'color': (0.7, 0.7, 1.0, 0.8),
'gravity': 9.8,
'wind_influence': 0.5,
'spawn_rate': 100, # 每秒生成数量
'max_particles': 2000
},
'snow_flake': {
'name': '雪花',
'shape': 'sprite',
'size_range': (0.2, 1.0),
'speed_range': (0.5, 3.0),
'lifetime_range': (3.0, 8.0),
'color': (1.0, 1.0, 1.0, 0.9),
'gravity': 0.1,
'wind_influence': 1.0,
'spawn_rate': 80,
'max_particles': 1500
},
'hail_stone': {
'name': '冰雹',
'shape': 'sprite',
'size_range': (0.3, 1.5),
'speed_range': (15.0, 30.0),
'lifetime_range': (1.0, 4.0),
'color': (0.9, 0.9, 1.0, 0.95),
'gravity': 12.0,
'wind_influence': 0.3,
'spawn_rate': 50,
'max_particles': 1000
},
'fog_particle': {
'name': '雾气',
'shape': 'sprite',
'size_range': (5.0, 15.0),
'speed_range': (0.1, 0.5),
'lifetime_range': (8.0, 20.0),
'color': (0.8, 0.8, 0.8, 0.2),
'gravity': 0.0,
'wind_influence': 0.8,
'spawn_rate': 20,
'max_particles': 500
},
'leaf_particle': {
'name': '落叶',
'shape': 'sprite',
'size_range': (0.5, 2.0),
'speed_range': (1.0, 5.0),
'lifetime_range': (5.0, 15.0),
'color': (0.6, 0.5, 0.3, 0.7),
'gravity': 1.0,
'wind_influence': 1.5,
'spawn_rate': 10,
'max_particles': 200
},
'dust_particle': {
'name': '尘土',
'shape': 'sprite',
'size_range': (0.1, 0.8),
'speed_range': (0.5, 3.0),
'lifetime_range': (2.0, 8.0),
'color': (0.8, 0.7, 0.6, 0.5),
'gravity': 0.2,
'wind_influence': 1.2,
'spawn_rate': 30,
'max_particles': 800
}
}
# 粒子池
self.particle_pool = []
self.active_particles = []
# 粒子发射器
self.particle_emitters = {}
# 粒子系统配置
self.system_config = {
'max_total_particles': 10000,
'lod_levels': {
'low': 0.3,
'medium': 0.6,
'high': 1.0
},
'quality_level': 'medium',
'distance_fade': True,
'collision_detection': False,
'particle_lod_distance': 50.0
}
# 粒子物理配置
self.physics_config = {
'gravity': (0.0, -9.8, 0.0),
'air_resistance': 0.1,
'wind_vector': (1.0, 0.0, 0.0),
'wind_strength': 1.0
}
# 粒子更新配置
self.update_interval = 0.016 # 60 FPS
self.last_update_time = 0.0
self.accumulator = 0.0
# 统计信息
self.stats = {
'particles_spawned': 0,
'particles_destroyed': 0,
'active_particles': 0,
'emitters_active': 0,
'total_update_time': 0.0
}
print("✓ 粒子效果系统已创建")
def initialize(self) -> bool:
"""
初始化粒子效果系统
Returns:
是否初始化成功
"""
try:
# 初始化粒子池
self._initialize_particle_pool()
self.initialized = True
print("✓ 粒子效果系统初始化完成")
return True
except Exception as e:
print(f"✗ 粒子效果系统初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用粒子效果系统
Returns:
是否启用成功
"""
try:
if not self.initialized:
print("✗ 粒子效果系统未初始化")
return False
self.enabled = True
print("✓ 粒子效果系统已启用")
return True
except Exception as e:
print(f"✗ 粒子效果系统启用失败: {e}")
import traceback
traceback.print_exc()
return False
def disable(self):
"""禁用粒子效果系统"""
try:
self.enabled = False
self._clear_all_particles()
print("✓ 粒子效果系统已禁用")
except Exception as e:
print(f"✗ 粒子效果系统禁用失败: {e}")
import traceback
traceback.print_exc()
def finalize(self):
"""清理粒子效果系统资源"""
try:
self.disable()
self.initialized = False
print("✓ 粒子效果系统资源已清理")
except Exception as e:
print(f"✗ 粒子效果系统资源清理失败: {e}")
import traceback
traceback.print_exc()
def update(self, dt: float):
"""
更新粒子效果系统状态
Args:
dt: 时间增量
"""
try:
if not self.enabled:
return
# 累积时间
self.accumulator += dt
# 固定时间步长更新
while self.accumulator >= self.update_interval:
self._update_particles(self.update_interval)
self._update_emitters(self.update_interval)
self.accumulator -= self.update_interval
# 更新统计信息
self.stats['total_update_time'] += dt
except Exception as e:
print(f"✗ 粒子效果系统更新失败: {e}")
import traceback
traceback.print_exc()
def _initialize_particle_pool(self):
"""初始化粒子池"""
try:
pool_size = self.system_config['max_total_particles']
for i in range(pool_size):
particle = {
'id': i,
'type': None,
'position': [0.0, 0.0, 0.0],
'velocity': [0.0, 0.0, 0.0],
'size': 1.0,
'color': [1.0, 1.0, 1.0, 1.0],
'lifetime': 1.0,
'age': 0.0,
'active': False,
'gravity': 0.0,
'wind_influence': 0.0
}
self.particle_pool.append(particle)
print(f"✓ 粒子池初始化完成,大小: {pool_size}")
except Exception as e:
print(f"✗ 粒子池初始化失败: {e}")
def _update_particles(self, dt: float):
"""更新粒子"""
try:
# 获取当前风力
wind_vector = self.physics_config['wind_vector']
wind_strength = self.physics_config['wind_strength']
# 获取重力
gravity = self.physics_config['gravity']
particles_to_remove = []
for particle in self.active_particles:
if not particle['active']:
continue
# 更新年龄
particle['age'] += dt
# 检查是否超出生命周期
if particle['age'] >= particle['lifetime']:
particles_to_remove.append(particle)
continue
# 应用重力
particle['velocity'][0] += gravity[0] * particle['gravity'] * dt
particle['velocity'][1] += gravity[1] * particle['gravity'] * dt
particle['velocity'][2] += gravity[2] * particle['gravity'] * dt
# 应用风力影响
wind_effect = wind_strength * particle['wind_influence']
particle['velocity'][0] += wind_vector[0] * wind_effect * dt
particle['velocity'][1] += wind_vector[1] * wind_effect * dt
particle['velocity'][2] += wind_vector[2] * wind_effect * dt
# 更新位置
particle['position'][0] += particle['velocity'][0] * dt
particle['position'][1] += particle['velocity'][1] * dt
particle['position'][2] += particle['velocity'][2] * dt
# 应用空气阻力
air_resistance = self.physics_config['air_resistance']
particle['velocity'][0] *= (1.0 - air_resistance * dt)
particle['velocity'][1] *= (1.0 - air_resistance * dt)
particle['velocity'][2] *= (1.0 - air_resistance * dt)
# 移除死亡粒子
for particle in particles_to_remove:
self._deactivate_particle(particle)
except Exception as e:
print(f"✗ 粒子更新失败: {e}")
def _update_emitters(self, dt: float):
"""更新粒子发射器"""
try:
# 根据当前天气激活相应的粒子发射器
self._update_weather_emitters()
# 更新所有激活的发射器
for emitter_name, emitter in self.particle_emitters.items():
if emitter['active']:
self._spawn_particles_from_emitter(emitter, dt)
except Exception as e:
print(f"✗ 发射器更新失败: {e}")
def _update_weather_emitters(self):
"""根据当前天气更新粒子发射器"""
try:
if not self.plugin.weather_manager:
return
current_weather = self.plugin.weather_manager.get_current_weather()
weather_info = self.plugin.weather_manager.get_weather_info(current_weather)
if not weather_info:
return
# 获取天气对应的粒子效果
particle_effect = weather_info.get('particle_effect')
# 检查是否需要激活或更新粒子发射器
if particle_effect and particle_effect in self.particle_types:
# 检查发射器是否已经存在
if particle_effect not in self.particle_emitters:
# 创建粒子发射器
self._create_particle_emitter(particle_effect)
else:
# 确保发射器处于激活状态
self.particle_emitters[particle_effect]['active'] = True
else:
# 停用所有天气相关的发射器
for emitter_name in list(self.particle_emitters.keys()):
if emitter_name in self.particle_types:
self.particle_emitters[emitter_name]['active'] = False
except Exception as e:
print(f"✗ 天气发射器更新失败: {e}")
def _create_particle_emitter(self, particle_type: str):
"""
创建粒子发射器
Args:
particle_type: 粒子类型
"""
try:
if particle_type not in self.particle_types:
return
emitter_config = self.particle_types[particle_type]
emitter = {
'name': particle_type,
'type': particle_type,
'config': emitter_config,
'position': [0.0, 50.0, 0.0], # 默认在空中
'area': [100.0, 0.0, 100.0], # 发射区域大小
'active': True,
'last_spawn_time': 0.0,
'spawn_accumulator': 0.0
}
self.particle_emitters[particle_type] = emitter
print(f"✓ 粒子发射器已创建: {emitter_config['name']}")
except Exception as e:
print(f"✗ 粒子发射器创建失败: {e}")
def _spawn_particles_from_emitter(self, emitter: Dict[str, Any], dt: float):
"""
从发射器生成粒子
Args:
emitter: 发射器数据
dt: 时间增量
"""
try:
config = emitter['config']
# 计算应该生成的粒子数量
emitter['spawn_accumulator'] += dt
spawn_interval = 1.0 / config['spawn_rate']
particles_to_spawn = int(emitter['spawn_accumulator'] / spawn_interval)
emitter['spawn_accumulator'] -= particles_to_spawn * spawn_interval
# 限制生成数量不超过最大粒子数
active_count = len([p for p in self.active_particles if p['type'] == emitter['type']])
max_particles = config['max_particles']
particles_to_spawn = min(particles_to_spawn, max_particles - active_count)
# 生成粒子
for i in range(particles_to_spawn):
self._spawn_particle(emitter)
except Exception as e:
print(f"✗ 粒子生成失败: {e}")
def _spawn_particle(self, emitter: Dict[str, Any]):
"""
生成单个粒子
Args:
emitter: 发射器数据
"""
try:
# 检查是否还有可用的粒子
available_particle = None
for particle in self.particle_pool:
if not particle['active']:
available_particle = particle
break
if not available_particle:
# 粒子池已满,移除最老的粒子
if self.active_particles:
oldest_particle = self.active_particles[0]
self._deactivate_particle(oldest_particle)
available_particle = oldest_particle
else:
return # 无法生成新粒子
config = emitter['config']
# 初始化粒子属性
available_particle['type'] = emitter['type']
available_particle['active'] = True
# 随机位置(在发射器区域内)
half_area = [emitter['area'][i] / 2 for i in range(3)]
available_particle['position'][0] = emitter['position'][0] + random.uniform(-half_area[0], half_area[0])
available_particle['position'][1] = emitter['position'][1] # Y轴保持不变
available_particle['position'][2] = emitter['position'][2] + random.uniform(-half_area[2], half_area[2])
# 随机速度
speed = random.uniform(config['speed_range'][0], config['speed_range'][1])
angle = random.uniform(0, 2 * math.pi)
available_particle['velocity'][0] = math.cos(angle) * speed
available_particle['velocity'][1] = -random.uniform(0.5, 1.0) * speed # 主要向下
available_particle['velocity'][2] = math.sin(angle) * speed
# 随机大小
available_particle['size'] = random.uniform(config['size_range'][0], config['size_range'][1])
# 颜色
available_particle['color'] = list(config['color'])
# 生命周期
available_particle['lifetime'] = random.uniform(config['lifetime_range'][0], config['lifetime_range'][1])
available_particle['age'] = 0.0
# 物理属性
available_particle['gravity'] = config['gravity']
available_particle['wind_influence'] = config['wind_influence']
# 添加到激活列表
if available_particle not in self.active_particles:
self.active_particles.append(available_particle)
# 更新统计信息
self.stats['particles_spawned'] += 1
except Exception as e:
print(f"✗ 单个粒子生成失败: {e}")
def _deactivate_particle(self, particle: Dict[str, Any]):
"""
停用粒子
Args:
particle: 粒子数据
"""
try:
particle['active'] = False
# 从激活列表中移除
if particle in self.active_particles:
self.active_particles.remove(particle)
# 更新统计信息
self.stats['particles_destroyed'] += 1
except Exception as e:
print(f"✗ 粒子停用失败: {e}")
def _clear_all_particles(self):
"""清除所有粒子"""
try:
for particle in self.active_particles:
particle['active'] = False
self.active_particles.clear()
print("✓ 所有粒子已清除")
except Exception as e:
print(f"✗ 粒子清除失败: {e}")
def get_active_particles(self) -> List[Dict[str, Any]]:
"""
获取激活的粒子列表
Returns:
激活的粒子列表
"""
return [p for p in self.active_particles if p['active']]
def get_particle_types_list(self) -> List[str]:
"""
获取所有粒子类型列表
Returns:
粒子类型名称列表
"""
return list(self.particle_types.keys())
def get_particle_type_info(self, particle_type: str) -> Optional[Dict[str, Any]]:
"""
获取粒子类型信息
Args:
particle_type: 粒子类型名称
Returns:
粒子类型信息字典或None
"""
try:
if particle_type in self.particle_types:
return self.particle_types[particle_type].copy()
return None
except Exception as e:
print(f"✗ 粒子类型信息获取失败: {e}")
return None
def create_custom_emitter(self, emitter_name: str, config: Dict[str, Any]):
"""
创建自定义粒子发射器
Args:
emitter_name: 发射器名称
config: 发射器配置
"""
try:
emitter = {
'name': emitter_name,
'type': 'custom',
'config': config,
'position': config.get('position', [0.0, 0.0, 0.0]),
'area': config.get('area', [10.0, 0.0, 10.0]),
'active': config.get('active', True),
'last_spawn_time': 0.0,
'spawn_accumulator': 0.0
}
self.particle_emitters[emitter_name] = emitter
print(f"✓ 自定义粒子发射器已创建: {emitter_name}")
except Exception as e:
print(f"✗ 自定义粒子发射器创建失败: {e}")
def activate_emitter(self, emitter_name: str):
"""
激活粒子发射器
Args:
emitter_name: 发射器名称
"""
try:
if emitter_name in self.particle_emitters:
self.particle_emitters[emitter_name]['active'] = True
print(f"✓ 粒子发射器已激活: {emitter_name}")
else:
print(f"✗ 粒子发射器不存在: {emitter_name}")
except Exception as e:
print(f"✗ 粒子发射器激活失败: {e}")
def deactivate_emitter(self, emitter_name: str):
"""
停用粒子发射器
Args:
emitter_name: 发射器名称
"""
try:
if emitter_name in self.particle_emitters:
self.particle_emitters[emitter_name]['active'] = False
print(f"✓ 粒子发射器已停用: {emitter_name}")
else:
print(f"✗ 粒子发射器不存在: {emitter_name}")
except Exception as e:
print(f"✗ 粒子发射器停用失败: {e}")
def set_emitter_position(self, emitter_name: str, position: Tuple[float, float, float]):
"""
设置发射器位置
Args:
emitter_name: 发射器名称
position: 位置坐标(x, y, z)
"""
try:
if emitter_name in self.particle_emitters:
self.particle_emitters[emitter_name]['position'] = list(position)
print(f"✓ 发射器位置已设置: {emitter_name} = {position}")
else:
print(f"✗ 粒子发射器不存在: {emitter_name}")
except Exception as e:
print(f"✗ 发射器位置设置失败: {e}")
def set_emitter_area(self, emitter_name: str, area: Tuple[float, float, float]):
"""
设置发射器区域大小
Args:
emitter_name: 发射器名称
area: 区域大小(x, y, z)
"""
try:
if emitter_name in self.particle_emitters:
self.particle_emitters[emitter_name]['area'] = list(area)
print(f"✓ 发射器区域已设置: {emitter_name} = {area}")
else:
print(f"✗ 粒子发射器不存在: {emitter_name}")
except Exception as e:
print(f"✗ 发射器区域设置失败: {e}")
def set_physics_config(self, config: Dict[str, Any]):
"""
设置物理配置
Args:
config: 物理配置字典
"""
self.physics_config.update(config)
print(f"✓ 物理配置已更新: {self.physics_config}")
def get_physics_config(self) -> Dict[str, Any]:
"""
获取物理配置
Returns:
物理配置字典
"""
return self.physics_config.copy()
def set_system_config(self, config: Dict[str, Any]):
"""
设置系统配置
Args:
config: 系统配置字典
"""
self.system_config.update(config)
print(f"✓ 系统配置已更新: {self.system_config}")
def get_system_config(self) -> Dict[str, Any]:
"""
获取系统配置
Returns:
系统配置字典
"""
return self.system_config.copy()
def set_update_interval(self, interval: float):
"""
设置更新间隔
Args:
interval: 更新间隔(秒)
"""
self.update_interval = max(0.001, min(0.1, interval))
print(f"✓ 粒子更新间隔设置为: {interval}")
def get_stats(self) -> Dict[str, Any]:
"""
获取统计信息
Returns:
统计信息字典
"""
self.stats['active_particles'] = len(self.active_particles)
self.stats['emitters_active'] = len([e for e in self.particle_emitters.values() if e['active']])
return self.stats.copy()
def reset_stats(self):
"""重置统计信息"""
self.stats = {
'particles_spawned': 0,
'particles_destroyed': 0,
'active_particles': 0,
'emitters_active': 0,
'total_update_time': 0.0
}
print("✓ 粒子效果系统统计信息已重置")
def is_emitter_active(self, emitter_name: str) -> bool:
"""
检查发射器是否激活
Args:
emitter_name: 发射器名称
Returns:
发射器是否激活
"""
if emitter_name in self.particle_emitters:
return self.particle_emitters[emitter_name]['active']
return False