""" 粒子系统模块 负责物理粒子的创建和管理 """ from panda3d.core import Vec3, Point3, LVector3f, ClockObject from panda3d.bullet import BulletRigidBodyNode, BulletSphereShape, BulletBoxShape import random import math import time from collections import deque class ParticleSystem: """ 粒子系统类 管理大量物理粒子的创建、更新和销毁 """ # 粒子类型常量 TYPE_DEFAULT = 'default' TYPE_SMOKE = 'smoke' TYPE_FIRE = 'fire' TYPE_WATER = 'water' TYPE_DEBRIS = 'debris' TYPE_EXPLOSION = 'explosion' TYPE_SPARK = 'spark' TYPE_BUBBLE = 'bubble' TYPE_LEAF = 'leaf' TYPE_SNOW = 'snow' TYPE_RAIN = 'rain' def __init__(self, physics_world, max_particles=1000, particle_type=TYPE_DEFAULT): """ 初始化粒子系统 Args: physics_world (PhysicsWorld): 物理世界对象 max_particles (int): 最大粒子数 particle_type (str): 粒子类型 """ self.physics_world = physics_world self.max_particles = max_particles self.particle_type = particle_type # 粒子容器 self.particles = [] self.active_particles = [] self.inactive_particles = deque() # 对象池 # 粒子发射器参数 self.emission_rate = 100 # 每秒发射粒子数 self.emission_accumulator = 0.0 self.emission_burst_count = 0 # 爆发粒子数 self.emission_burst_timer = 0.0 self.emission_burst_interval = 0.0 # 爆发间隔 # 粒子默认属性 self.particle_mass = 0.1 self.particle_radius = 0.1 self.particle_size = Vec3(0.1, 0.1, 0.1) # 非球形粒子尺寸 self.particle_lifetime = 5.0 self.particle_color = Vec3(1, 1, 1) self.particle_shape_type = 'sphere' # sphere, box, capsule # 发射器属性 self.emitter_position = Point3(0, 0, 0) self.emitter_velocity = Vec3(0, 0, 0) self.emitter_spread = 0.1 self.emitter_radius = 0.0 self.emitter_shape = 'point' # point, circle, box, sphere self.emitter_dimensions = Vec3(1, 1, 1) # 发射器尺寸 # 物理属性 self.gravity_scale = 1.0 self.air_resistance = 0.01 self.bounce_factor = 0.5 self.friction = 0.1 self.restitution = 0.3 self.linear_damping = 0.01 self.angular_damping = 0.01 # 碰撞属性 self.collision_enabled = True self.collision_group = 1 self.collision_mask = 0xFFFFFFFF self.collision_response = True # 是否响应碰撞 # 高级粒子属性 self.particle_trail_enabled = False self.particle_trail_length = 10 self.particle_fade_enabled = True self.particle_size_variation = 0.2 self.particle_velocity_variation = 0.3 self.particle_rotation_enabled = False self.particle_rotation_speed = 0.0 self.particle_rotation_variation = 0.5 # 环境影响 self.wind_enabled = False self.wind_vector = Vec3(0, 0, 0) self.wind_turbulence = 0.0 self.temperature_effect = 0.0 # 温度影响(浮力等) # 特殊效果 self.vortex_enabled = False self.vortex_center = Point3(0, 0, 0) self.vortex_strength = 0.0 self.vortex_axis = Vec3(0, 0, 1) # 粒子生命周期行为 self.size_over_lifetime = None # [(time, scale), ...] self.color_over_lifetime = None # [(time, color), ...] self.force_over_lifetime = None # [(time, force), ...] # 粒子子发射器 self.sub_emitter_enabled = False self.sub_emitter_rate = 0.0 self.sub_emitter_count = 1 self.sub_emitter_lifetime = 1.0 # 性能优化 self.sleeping_threshold = 0.1 # 速度低于此值时休眠 self.culling_distance = 100.0 # 距离摄像机超过此距离时剔除 self.batch_processing = True # 批量处理 # 统计信息 self.emitted_count = 0 self.recycled_count = 0 self.collision_count = 0 self.max_active_particles = 0 # 时间控制 self.time_scale = 1.0 self.paused = False # 粒子材质 self.particle_material = None # 粒子纹理(如果需要渲染) self.particle_texture = None self.particle_blend_mode = 'alpha' # alpha, additive, multiply # 粒子死亡回调 self.death_callbacks = [] # 粒子碰撞回调 self.collision_callbacks = [] print(f"粒子系统初始化完成,最大粒子数: {max_particles},类型: {particle_type}") def set_emitter_properties(self, position=None, velocity=None, spread=None, radius=None, shape=None, dimensions=None): """ 设置发射器属性 Args: position (Point3): 发射器位置 velocity (Vec3): 发射器基础速度 spread (float): 发射扩散角度 radius (float): 发射器半径 shape (str): 发射器形状 dimensions (Vec3): 发射器尺寸 """ if position is not None: self.emitter_position = position if velocity is not None: self.emitter_velocity = velocity if spread is not None: self.emitter_spread = spread if radius is not None: self.emitter_radius = radius if shape is not None: self.emitter_shape = shape if dimensions is not None: self.emitter_dimensions = dimensions def set_particle_properties(self, mass=None, radius=None, size=None, lifetime=None, color=None, shape_type=None): """ 设置粒子属性 Args: mass (float): 粒子质量 radius (float): 粒子半径(球形) size (Vec3): 粒子尺寸(非球形) lifetime (float): 粒子生命周期 color (Vec3): 粒子颜色 shape_type (str): 粒子形状类型 """ if mass is not None: self.particle_mass = mass if radius is not None: self.particle_radius = radius if size is not None: self.particle_size = size if lifetime is not None: self.particle_lifetime = lifetime if color is not None: self.particle_color = color if shape_type is not None: self.particle_shape_type = shape_type def set_physics_properties(self, gravity_scale=None, air_resistance=None, bounce_factor=None, friction=None, restitution=None, linear_damping=None, angular_damping=None): """ 设置物理属性 Args: gravity_scale (float): 重力缩放 air_resistance (float): 空气阻力 bounce_factor (float): 弹跳因子 friction (float): 摩擦系数 restitution (float): 恢复系数 linear_damping (float): 线性阻尼 angular_damping (float): 角阻尼 """ if gravity_scale is not None: self.gravity_scale = gravity_scale if air_resistance is not None: self.air_resistance = air_resistance if bounce_factor is not None: self.bounce_factor = bounce_factor if friction is not None: self.friction = friction if restitution is not None: self.restitution = restitution if linear_damping is not None: self.linear_damping = linear_damping if angular_damping is not None: self.angular_damping = angular_damping def set_collision_properties(self, enabled=None, group=None, mask=None, response=None): """ 设置碰撞属性 Args: enabled (bool): 是否启用碰撞 group (int): 碰撞组 mask (int): 碰撞掩码 response (bool): 是否响应碰撞 """ if enabled is not None: self.collision_enabled = enabled if group is not None: self.collision_group = group if mask is not None: self.collision_mask = mask if response is not None: self.collision_response = response def set_environmental_effects(self, wind_enabled=None, wind_vector=None, wind_turbulence=None, temperature_effect=None): """ 设置环境影响 Args: wind_enabled (bool): 是否启用风 wind_vector (Vec3): 风向量 wind_turbulence (float): 风湍流 temperature_effect (float): 温度影响 """ if wind_enabled is not None: self.wind_enabled = wind_enabled if wind_vector is not None: self.wind_vector = wind_vector if wind_turbulence is not None: self.wind_turbulence = wind_turbulence if temperature_effect is not None: self.temperature_effect = temperature_effect def set_special_effects(self, vortex_enabled=None, vortex_center=None, vortex_strength=None, vortex_axis=None): """ 设置特殊效果 Args: vortex_enabled (bool): 是否启用涡流 vortex_center (Point3): 涡流中心 vortex_strength (float): 涡流强度 vortex_axis (Vec3): 涡流轴向 """ if vortex_enabled is not None: self.vortex_enabled = vortex_enabled if vortex_center is not None: self.vortex_center = vortex_center if vortex_strength is not None: self.vortex_strength = vortex_strength if vortex_axis is not None: self.vortex_axis = vortex_axis def set_lifetime_behaviors(self, size_curve=None, color_curve=None, force_curve=None): """ 设置粒子生命周期行为 Args: size_curve (list): 尺寸随时间变化曲线 [(time, scale), ...] color_curve (list): 颜色随时间变化曲线 [(time, color), ...] force_curve (list): 力随时间变化曲线 [(time, force), ...] """ if size_curve is not None: self.size_over_lifetime = size_curve if color_curve is not None: self.color_over_lifetime = color_curve if force_curve is not None: self.force_over_lifetime = force_curve def set_sub_emitter(self, enabled=None, rate=None, count=None, lifetime=None): """ 设置粒子子发射器 Args: enabled (bool): 是否启用子发射器 rate (float): 子发射器速率 count (int): 每次发射的子粒子数 lifetime (float): 子粒子生命周期 """ if enabled is not None: self.sub_emitter_enabled = enabled if rate is not None: self.sub_emitter_rate = rate if count is not None: self.sub_emitter_count = count if lifetime is not None: self.sub_emitter_lifetime = lifetime def set_performance_options(self, sleeping_threshold=None, culling_distance=None, batch_processing=None): """ 设置性能优化选项 Args: sleeping_threshold (float): 休眠阈值 culling_distance (float): 剔除距离 batch_processing (bool): 批量处理 """ if sleeping_threshold is not None: self.sleeping_threshold = sleeping_threshold if culling_distance is not None: self.culling_distance = culling_distance if batch_processing is not None: self.batch_processing = batch_processing def set_emission_rate(self, rate): """ 设置发射速率 Args: rate (float): 每秒发射粒子数 """ self.emission_rate = max(0, rate) def set_emission_burst(self, count, interval=0.0): """ 设置爆发发射 Args: count (int): 每次爆发的粒子数 interval (float): 爆发间隔(秒) """ self.emission_burst_count = count self.emission_burst_interval = interval def emit_particle(self, position=None, velocity=None, lifetime=None, color=None): """ 发射单个粒子 Args: position (Point3): 粒子位置 velocity (Vec3): 粒子速度 lifetime (float): 粒子生命周期 color (Vec3): 粒子颜色 Returns: dict: 粒子信息字典 """ # 检查是否达到最大粒子数 if len(self.active_particles) >= self.max_particles: return None # 从对象池获取粒子或创建新粒子 if self.inactive_particles: particle_info = self.inactive_particles.popleft() self.recycled_count += 1 else: particle_info = self._create_new_particle() # 设置粒子属性 if position is None: position = self._calculate_emission_position() if velocity is None: velocity = self._calculate_emission_velocity() if lifetime is None: lifetime = self.particle_lifetime * random.uniform( 1.0 - self.particle_lifetime * 0.2, 1.0 + self.particle_lifetime * 0.2 ) if color is None: color = self.particle_color # 初始化粒子状态 particle_info['position'] = position particle_info['velocity'] = velocity particle_info['lifetime'] = lifetime particle_info['age'] = 0.0 particle_info['color'] = color particle_info['initial_size'] = particle_info['size'] particle_info['initial_color'] = color particle_info['initial_velocity'] = velocity particle_info['rotation'] = random.uniform(0, 360) if self.particle_rotation_enabled else 0.0 particle_info['rotation_speed'] = self.particle_rotation_speed * random.uniform( 1.0 - self.particle_rotation_variation, 1.0 + self.particle_rotation_variation ) particle_info['trail'] = [] if self.particle_trail_enabled else None particle_info['collisions'] = 0 particle_info['sleeping'] = False particle_info['culled'] = False # 添加到物理世界 self.physics_world.bullet_world.attachRigidBody(particle_info['body']) # 设置物理属性 particle_info['body'].setLinearVelocity(velocity) particle_info['body'].setFriction(self.friction) particle_info['body'].setRestitution(self.restitution) particle_info['body'].setLinearDamping(self.linear_damping) particle_info['body'].setAngularDamping(self.angular_damping) # 设置碰撞属性 if self.collision_enabled: particle_info['body'].setCollideFilterGroup(self.collision_group) particle_info['body'].setCollideFilterMask(self.collision_mask) else: particle_info['body'].setCollideFilterGroup(0) particle_info['body'].setCollideFilterMask(0) # 设置位置 particle_info['node'].setPos(position) # 添加到活动粒子列表 self.active_particles.append(particle_info) self.emitted_count += 1 # 更新统计 self.max_active_particles = max(self.max_active_particles, len(self.active_particles)) return particle_info def _create_new_particle(self): """ 创建新粒子 Returns: dict: 粒子信息字典 """ # 创建粒子形状 if self.particle_shape_type == 'sphere': size = self.particle_radius * random.uniform( 1.0 - self.particle_size_variation, 1.0 + self.particle_size_variation ) particle_shape = BulletSphereShape(size) elif self.particle_shape_type == 'box': size = self.particle_size * random.uniform( 1.0 - self.particle_size_variation, 1.0 + self.particle_size_variation ) particle_shape = BulletBoxShape(size * 0.5) else: # 默认使用球形 size = self.particle_radius * random.uniform( 1.0 - self.particle_size_variation, 1.0 + self.particle_size_variation ) particle_shape = BulletSphereShape(size) # 创建粒子刚体 particle_body = BulletRigidBodyNode(f'Particle_{len(self.particles)}') particle_body.addShape(particle_shape) particle_body.setMass(self.particle_mass) # 创建粒子节点 from panda3d.core import NodePath dummy_np = NodePath("dummy") particle_node = dummy_np.attachNewNode(particle_body) # 创建粒子信息 particle_info = { 'node': particle_node, 'body': particle_body, 'shape': particle_shape, 'position': Point3(0, 0, 0), 'velocity': Vec3(0, 0, 0), 'lifetime': self.particle_lifetime, 'age': 0.0, 'color': self.particle_color, 'size': size, 'mass': self.particle_mass, 'radius': size if self.particle_shape_type == 'sphere' else None, 'type': self.particle_type, 'material': self.particle_material, 'creation_time': time.time(), } self.particles.append(particle_info) return particle_info def _calculate_emission_position(self): """ 计算发射位置 Returns: Point3: 发射位置 """ if self.emitter_shape == 'point': return self.emitter_position elif self.emitter_shape == 'circle': angle = random.uniform(0, 2 * math.pi) distance = random.uniform(0, self.emitter_radius) offset = Point3( math.cos(angle) * distance, math.sin(angle) * distance, 0 ) return self.emitter_position + offset elif self.emitter_shape == 'box': offset = Point3( random.uniform(-self.emitter_dimensions.getX() * 0.5, self.emitter_dimensions.getX() * 0.5), random.uniform(-self.emitter_dimensions.getY() * 0.5, self.emitter_dimensions.getY() * 0.5), random.uniform(-self.emitter_dimensions.getZ() * 0.5, self.emitter_dimensions.getZ() * 0.5) ) return self.emitter_position + offset elif self.emitter_shape == 'sphere': # 球面均匀分布 theta = random.uniform(0, 2 * math.pi) phi = math.acos(2 * random.uniform(0, 1) - 1) radius = self.emitter_radius offset = Point3( radius * math.sin(phi) * math.cos(theta), radius * math.sin(phi) * math.sin(theta), radius * math.cos(phi) ) return self.emitter_position + offset else: return self.emitter_position def _calculate_emission_velocity(self): """ 计算发射速度 Returns: Vec3: 发射速度 """ # 基础速度 base_velocity = self.emitter_velocity.copy() # 添加随机扩散 if self.emitter_spread > 0: spread_angle = random.uniform(0, self.emitter_spread) spread_direction = random.uniform(0, 2 * math.pi) spread_offset = Vec3( math.cos(spread_direction) * math.sin(spread_angle), math.sin(spread_direction) * math.sin(spread_angle), math.cos(spread_angle) ) base_velocity += spread_offset * base_velocity.length() # 添加速度变化 if self.particle_velocity_variation > 0: variation = random.uniform( 1.0 - self.particle_velocity_variation, 1.0 + self.particle_velocity_variation ) base_velocity *= variation # 添加随机速度分量 random_velocity = Vec3( random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1) ) * base_velocity.length() * 0.1 return base_velocity + random_velocity def update(self, dt): """ 更新粒子系统 Args: dt (float): 时间增量 """ if self.paused: return # 应用时间缩放 dt *= self.time_scale # 更新现有粒子 for particle in self.active_particles[:]: # 使用副本避免迭代时修改 self._update_particle(particle, dt) # 发射新粒子 self._emit_particles(dt) # 处理爆发发射 self._handle_burst_emission(dt) def _update_particle(self, particle, dt): """ 更新单个粒子 Args: particle (dict): 粒子信息 dt (float): 时间增量 """ # 更新粒子年龄 particle['age'] += dt # 检查生命周期 if particle['age'] >= particle['lifetime']: self._destroy_particle(particle) return # 检查是否休眠 velocity = particle['body'].getLinearVelocity() if velocity.length() < self.sleeping_threshold: particle['sleeping'] = True else: particle['sleeping'] = False # 如果休眠,跳过物理更新 if particle['sleeping']: return # 应用重力 if self.gravity_scale > 0: gravity_force = Vec3(0, 0, -9.81) * self.gravity_scale * particle['mass'] particle['body'].applyCentralForce(gravity_force) # 应用空气阻力 if self.air_resistance > 0: drag_force = -velocity * self.air_resistance particle['body'].applyCentralForce(drag_force) # 应用风力 if self.wind_enabled and self.wind_vector.length() > 0: wind_force = self.wind_vector * particle['mass'] if self.wind_turbulence > 0: turbulence = Vec3( random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1) ) * self.wind_turbulence wind_force += turbulence particle['body'].applyCentralForce(wind_force) # 应用涡流力 if self.vortex_enabled and self.vortex_strength > 0: self._apply_vortex_force(particle) # 应用温度影响(浮力等) if self.temperature_effect != 0: # 简化的浮力计算 buoyancy_force = Vec3(0, 0, 1) * self.temperature_effect * particle['mass'] particle['body'].applyCentralForce(buoyancy_force) # 应用生命周期力 if self.force_over_lifetime: self._apply_lifetime_force(particle) # 更新轨迹 if self.particle_trail_enabled and particle['trail'] is not None: position = particle['node'].getPos() particle['trail'].append(position) if len(particle['trail']) > self.particle_trail_length: particle['trail'].pop(0) # 更新旋转 if self.particle_rotation_enabled: particle['rotation'] += particle['rotation_speed'] * dt # 更新尺寸 if self.size_over_lifetime: self._update_particle_size(particle) # 更新颜色 if self.particle_fade_enabled or self.color_over_lifetime: self._update_particle_color(particle) # 处理子发射器 if self.sub_emitter_enabled and self.sub_emitter_rate > 0: self._handle_sub_emitter(particle, dt) def _apply_vortex_force(self, particle): """ 应用涡流力 Args: particle (dict): 粒子信息 """ position = particle['node'].getPos() to_center = self.vortex_center - position distance = to_center.length() if distance > 0: # 径向力(向中心吸引) radial_force = to_center.normalized() * self.vortex_strength / (distance + 1.0) # 切向力(绕轴旋转) tangent_dir = self.vortex_axis.cross(to_center).normalized() tangent_force = tangent_dir * self.vortex_strength total_force = (radial_force + tangent_force) * particle['mass'] particle['body'].applyCentralForce(total_force) def _apply_lifetime_force(self, particle): """ 应用生命周期力 Args: particle (dict): 粒子信息 """ normalized_age = particle['age'] / particle['lifetime'] # 在力曲线上插值 for i in range(len(self.force_over_lifetime) - 1): time1, force1 = self.force_over_lifetime[i] time2, force2 = self.force_over_lifetime[i + 1] if time1 <= normalized_age <= time2: t = (normalized_age - time1) / (time2 - time1) if time2 != time1 else 0 interpolated_force = force1 + (force2 - force1) * t particle['body'].applyCentralForce(interpolated_force * particle['mass']) break def _update_particle_size(self, particle): """ 更新粒子尺寸 Args: particle (dict): 粒子信息 """ normalized_age = particle['age'] / particle['lifetime'] # 在尺寸曲线上插值 for i in range(len(self.size_over_lifetime) - 1): time1, scale1 = self.size_over_lifetime[i] time2, scale2 = self.size_over_lifetime[i + 1] if time1 <= normalized_age <= time2: t = (normalized_age - time1) / (time2 - time1) if time2 != time1 else 0 interpolated_scale = scale1 + (scale2 - scale1) * t # 更新粒子尺寸(这个功能需要与渲染系统集成) # 这里只是更新记录,实际渲染需要在渲染系统中实现 if isinstance(particle['size'], (int, float)): particle['size'] = particle['initial_size'] * interpolated_scale break def _update_particle_color(self, particle): """ 更新粒子颜色 Args: particle (dict): 粒子信息 """ normalized_age = particle['age'] / particle['lifetime'] # 淡出效果 if self.particle_fade_enabled: fade_factor = 1.0 - (normalized_age * normalized_age) # 二次淡出 particle['color'] = particle['initial_color'] * fade_factor # 生命周期颜色变化 if self.color_over_lifetime: for i in range(len(self.color_over_lifetime) - 1): time1, color1 = self.color_over_lifetime[i] time2, color2 = self.color_over_lifetime[i + 1] if time1 <= normalized_age <= time2: t = (normalized_age - time1) / (time2 - time1) if time2 != time1 else 0 interpolated_color = color1 + (color2 - color1) * t if self.particle_fade_enabled: # 结合淡出效果 particle['color'] = interpolated_color * (1.0 - normalized_age) else: particle['color'] = interpolated_color break def _handle_sub_emitter(self, particle, dt): """ 处理子发射器 Args: particle (dict): 粒子信息 dt (float): 时间增量 """ # 简化的子发射器实现 pass # 需要更复杂的实现 def _emit_particles(self, dt): """ 发射粒子 Args: dt (float): 时间增量 """ # 基于发射速率发射粒子 self.emission_accumulator += dt * self.emission_rate while self.emission_accumulator >= 1.0 and len(self.active_particles) < self.max_particles: self.emit_particle() self.emission_accumulator -= 1.0 def _handle_burst_emission(self, dt): """ 处理爆发发射 Args: dt (float): 时间增量 """ if self.emission_burst_count > 0 and self.emission_burst_interval > 0: self.emission_burst_timer += dt if self.emission_burst_timer >= self.emission_burst_interval: for _ in range(min(self.emission_burst_count, self.max_particles - len(self.active_particles))): self.emit_particle() self.emission_burst_timer = 0.0 def _destroy_particle(self, particle): """ 销毁粒子 Args: particle (dict): 粒子信息 """ if particle in self.active_particles: # 从物理世界移除 self.physics_world.bullet_world.removeRigidBody(particle['body']) # 移除节点 if particle['node'] and not particle['node'].isEmpty(): particle['node'].removeNode() # 从活动列表移除 self.active_particles.remove(particle) # 添加到对象池 self.inactive_particles.append(particle) # 调用死亡回调 for callback in self.death_callbacks: try: callback(particle) except Exception as e: print(f"粒子死亡回调错误: {e}") def get_particle_count(self): """ 获取活动粒子数量 Returns: int: 活动粒子数量 """ return len(self.active_particles) def get_total_emitted(self): """ 获取总发射粒子数 Returns: int: 总发射粒子数 """ return self.emitted_count def get_statistics(self): """ 获取粒子系统统计信息 Returns: dict: 统计信息 """ return { 'active_particles': len(self.active_particles), 'total_emitted': self.emitted_count, 'recycled_particles': self.recycled_count, 'collision_count': self.collision_count, 'max_active_particles': self.max_active_particles, 'emission_rate': self.emission_rate, 'particle_type': self.particle_type, 'system_age': time.time() - (self.particles[0]['creation_time'] if self.particles else time.time()), } def clear_particles(self): """ 清除所有粒子 """ # 销毁所有活动粒子 for particle in self.active_particles[:]: self._destroy_particle(particle) # 清空对象池 self.inactive_particles.clear() # 清空粒子列表 self.particles.clear() self.active_particles.clear() # 重置统计信息 self.emitted_count = 0 self.recycled_count = 0 self.collision_count = 0 self.max_active_particles = 0 # 重置发射器 self.emission_accumulator = 0.0 self.emission_burst_timer = 0.0 print("所有粒子已清除") def pause(self, paused=True): """ 暂停或恢复粒子系统 Args: paused (bool): 是否暂停 """ self.paused = paused def set_time_scale(self, scale): """ 设置时间缩放 Args: scale (float): 时间缩放因子 """ self.time_scale = max(0.0, scale) def add_death_callback(self, callback): """ 添加粒子死亡回调 Args: callback (function): 回调函数 """ if callback not in self.death_callbacks: self.death_callbacks.append(callback) def remove_death_callback(self, callback): """ 移除粒子死亡回调 Args: callback (function): 回调函数 """ if callback in self.death_callbacks: self.death_callbacks.remove(callback) def add_collision_callback(self, callback): """ 添加粒子碰撞回调 Args: callback (function): 回调函数 """ if callback not in self.collision_callbacks: self.collision_callbacks.append(callback) def remove_collision_callback(self, callback): """ 移除粒子碰撞回调 Args: callback (function): 回调函数 """ if callback in self.collision_callbacks: self.collision_callbacks.remove(callback) def create_explosion(self, position, particle_count=50, explosion_force=100.0, particle_radius=None, particle_lifetime=None, explosion_radius=5.0): """ 创建爆炸效果 Args: position (Point3): 爆炸位置 particle_count (int): 粒子数量 explosion_force (float): 爆炸力 particle_radius (float): 粒子半径 particle_lifetime (float): 粒子生命周期 explosion_radius (float): 爆炸半径 """ if particle_radius is None: particle_radius = self.particle_radius if particle_lifetime is None: particle_lifetime = self.particle_lifetime for i in range(particle_count): # 随机方向 direction = Vec3( random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(0, 1) ) direction.normalize() # 粒子速度 velocity = direction * random.uniform(explosion_force * 0.5, explosion_force) # 粒子位置(在爆炸半径内) offset = direction * random.uniform(0, explosion_radius) particle_position = position + offset # 创建粒子 particle_shape = BulletSphereShape(particle_radius * random.uniform(0.5, 2.0)) particle_body = BulletRigidBodyNode(f'ExplosionParticle_{self.emitted_count + i}') particle_body.addShape(particle_shape) particle_body.setMass(self.particle_mass * 0.5) # 爆炸粒子质量较小 # 设置碰撞属性 if self.collision_enabled: particle_body.setCollideFilterGroup(self.collision_group) particle_body.setCollideFilterMask(self.collision_mask) # 创建粒子节点 from panda3d.core import NodePath dummy_np = NodePath("dummy") particle_node = dummy_np.attachNewNode(particle_body) particle_node.setPos(particle_position) # 添加到物理世界 self.physics_world.bullet_world.attachRigidBody(particle_body) # 应用初始力 particle_body.setLinearVelocity(velocity) # 创建粒子信息 particle_info = { 'node': particle_node, 'body': particle_body, 'shape': particle_shape, 'position': particle_position, 'velocity': velocity, 'lifetime': particle_lifetime * random.uniform(0.5, 1.5), 'age': 0.0, 'color': Vec3(1, random.uniform(0.5, 1), 0), # 橙色/红色 'size': particle_radius, 'mass': self.particle_mass * 0.5, 'radius': particle_radius, 'type': self.TYPE_EXPLOSION, 'material': self.particle_material, 'creation_time': time.time(), 'rotation': random.uniform(0, 360), 'rotation_speed': random.uniform(-10, 10), 'trail': [] if self.particle_trail_enabled else None, 'collisions': 0, 'sleeping': False, 'culled': False } self.particles.append(particle_info) self.active_particles.append(particle_info) self.emitted_count += 1 print(f"创建爆炸效果,位置: {position}, 粒子数: {particle_count}") def create_fire_effect(self, position, particle_count=30, rise_speed=5.0, particle_radius=None, particle_lifetime=None): """ 创建火焰效果 Args: position (Point3): 火焰位置 particle_count (int): 粒子数量 rise_speed (float): 上升速度 particle_radius (float): 粒子半径 particle_lifetime (float): 粒子生命周期 """ if particle_radius is None: particle_radius = self.particle_radius if particle_lifetime is None: particle_lifetime = self.particle_lifetime * 2.0 # 火焰粒子寿命更长 for i in range(particle_count): # 粒子位置(在火焰底部附近) offset = Vec3( random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), random.uniform(0, 1) ) particle_position = position + offset # 粒子速度(向上) velocity = Vec3( random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), random.uniform(rise_speed * 0.8, rise_speed * 1.2) ) # 创建粒子 particle_shape = BulletSphereShape(particle_radius * random.uniform(0.8, 1.5)) particle_body = BulletRigidBodyNode(f'FireParticle_{self.emitted_count + i}') particle_body.addShape(particle_shape) particle_body.setMass(self.particle_mass * 0.1) # 火焰粒子质量很小 # 设置物理属性(火焰粒子受重力影响较小) particle_body.setFriction(0.1) particle_body.setRestitution(0.1) # 创建粒子节点 from panda3d.core import NodePath dummy_np = NodePath("dummy") particle_node = dummy_np.attachNewNode(particle_body) particle_node.setPos(particle_position) # 添加到物理世界 self.physics_world.bullet_world.attachRigidBody(particle_body) # 应用初始速度 particle_body.setLinearVelocity(velocity) # 创建粒子信息 particle_info = { 'node': particle_node, 'body': particle_body, 'shape': particle_shape, 'position': particle_position, 'velocity': velocity, 'lifetime': particle_lifetime * random.uniform(0.8, 1.2), 'age': 0.0, 'color': Vec3(1, random.uniform(0.3, 0.7), 0), # 黄色/橙色 'size': particle_radius, 'mass': self.particle_mass * 0.1, 'radius': particle_radius, 'type': self.TYPE_FIRE, 'material': self.particle_material, 'creation_time': time.time(), 'rotation': random.uniform(0, 360), 'rotation_speed': random.uniform(-5, 5), 'trail': [] if self.particle_trail_enabled else None, 'collisions': 0, 'sleeping': False, 'culled': False } self.particles.append(particle_info) self.active_particles.append(particle_info) self.emitted_count += 1 print(f"创建火焰效果,位置: {position}, 粒子数: {particle_count}") def destroy(self): """ 销毁粒子系统 """ self.clear_particles() self.death_callbacks.clear() self.collision_callbacks.clear() print("粒子系统已销毁")