Source code for core.components.particle_emitter

from core.ecs import Component


[docs] class ParticleEmitterComponent(Component): LAYER_BEHIND = "behind" LAYER_FRONT = "front" SHAPE_CIRCLE = "circle" SHAPE_SQUARE = "square" SHAPE_PIXEL = "pixel" def __init__( self, emitting: bool = True, one_shot: bool = False, local_space: bool = False, render_layer: str = LAYER_FRONT, blend_additive: bool = False, max_particles: int = 512, emission_rate: float = 0.0, burst_count: int = 0, burst_interval: float = 1.0, lifetime_min: float = 0.25, lifetime_max: float = 0.75, speed_min: float = 30.0, speed_max: float = 90.0, direction_degrees: float = 270.0, spread_degrees: float = 360.0, gravity_x: float = 0.0, gravity_y: float = 0.0, damping: float = 0.0, radial_offset_min: float = 0.0, radial_offset_max: float = 0.0, angular_velocity_min: float = 0.0, angular_velocity_max: float = 0.0, start_size_min: float = 4.0, start_size_max: float = 10.0, end_size_min: float = 0.0, end_size_max: float = 2.0, start_color: tuple[int, int, int, int] = (255, 180, 80, 255), end_color: tuple[int, int, int, int] = (200, 60, 10, 0), emitter_lifetime: float = -1.0, shape: str = SHAPE_CIRCLE ): self.entity = None self.emitting = bool(emitting) self.one_shot = bool(one_shot) self.local_space = bool(local_space) layer = str(render_layer).lower() self.render_layer = layer if layer in (self.LAYER_BEHIND, self.LAYER_FRONT) else self.LAYER_FRONT self.blend_additive = bool(blend_additive) self.max_particles = max(1, int(max_particles)) self.emission_rate = max(0.0, float(emission_rate)) self.burst_count = max(0, int(burst_count)) self.burst_interval = max(0.01, float(burst_interval)) self.lifetime_min = max(0.01, float(lifetime_min)) self.lifetime_max = max(self.lifetime_min, float(lifetime_max)) self.speed_min = float(speed_min) self.speed_max = max(self.speed_min, float(speed_max)) self.direction_degrees = float(direction_degrees) self.spread_degrees = max(0.0, float(spread_degrees)) self.gravity_x = float(gravity_x) self.gravity_y = float(gravity_y) self.damping = max(0.0, float(damping)) self.radial_offset_min = max(0.0, float(radial_offset_min)) self.radial_offset_max = max(self.radial_offset_min, float(radial_offset_max)) self.angular_velocity_min = float(angular_velocity_min) self.angular_velocity_max = max(self.angular_velocity_min, float(angular_velocity_max)) self.start_size_min = max(0.1, float(start_size_min)) self.start_size_max = max(self.start_size_min, float(start_size_max)) self.end_size_min = max(0.0, float(end_size_min)) self.end_size_max = max(self.end_size_min, float(end_size_max)) self.start_color = tuple(int(max(0, min(255, channel))) for channel in start_color[:4]) self.end_color = tuple(int(max(0, min(255, channel))) for channel in end_color[:4]) while len(self.start_color) < 4: self.start_color += (255,) while len(self.end_color) < 4: self.end_color += (0,) self.emitter_lifetime = float(emitter_lifetime) normalized_shape = str(shape).lower() self.shape = normalized_shape if normalized_shape in (self.SHAPE_CIRCLE, self.SHAPE_SQUARE, self.SHAPE_PIXEL) else self.SHAPE_CIRCLE self._particle_state = None self._pending_bursts = 1 if self.one_shot and self.burst_count > 0 and self.emitting else 0
[docs] def start(self, reset: bool = False): self.emitting = True if reset and self._particle_state: self._particle_state["alive"] = 0 self._particle_state["rate_carry"] = 0.0 self._particle_state["burst_timer"] = 0.0 self._particle_state["elapsed"] = 0.0 if self.one_shot and self.burst_count > 0: self._pending_bursts += 1
[docs] def stop(self, clear_particles: bool = False): self.emitting = False if clear_particles and self._particle_state: self._particle_state["alive"] = 0
[docs] def trigger_burst(self, count: int = 1): self._pending_bursts += max(1, int(count))
[docs] @classmethod def explosion(cls): return cls( one_shot=True, blend_additive=True, max_particles=300, emission_rate=0.0, burst_count=80, burst_interval=1.0, lifetime_min=0.2, lifetime_max=0.8, speed_min=120.0, speed_max=420.0, direction_degrees=270.0, spread_degrees=360.0, gravity_y=280.0, damping=2.0, radial_offset_min=0.0, radial_offset_max=12.0, angular_velocity_min=-220.0, angular_velocity_max=220.0, start_size_min=6.0, start_size_max=14.0, end_size_min=0.0, end_size_max=4.0, start_color=(255, 225, 120, 255), end_color=(255, 60, 20, 0), emitter_lifetime=0.15, shape=cls.SHAPE_CIRCLE )
[docs] @classmethod def smoke(cls): return cls( one_shot=False, local_space=False, blend_additive=False, max_particles=400, emission_rate=45.0, burst_count=0, lifetime_min=0.8, lifetime_max=1.9, speed_min=10.0, speed_max=55.0, direction_degrees=270.0, spread_degrees=70.0, gravity_y=-10.0, damping=0.9, radial_offset_min=0.0, radial_offset_max=5.0, angular_velocity_min=-45.0, angular_velocity_max=45.0, start_size_min=6.0, start_size_max=16.0, end_size_min=20.0, end_size_max=36.0, start_color=(120, 120, 120, 160), end_color=(70, 70, 70, 0), emitter_lifetime=-1.0, shape=cls.SHAPE_CIRCLE )
[docs] @classmethod def magic(cls): return cls( one_shot=False, local_space=False, blend_additive=True, max_particles=520, emission_rate=120.0, burst_count=0, lifetime_min=0.3, lifetime_max=1.2, speed_min=35.0, speed_max=140.0, direction_degrees=270.0, spread_degrees=360.0, gravity_y=0.0, damping=0.45, radial_offset_min=0.0, radial_offset_max=18.0, angular_velocity_min=-180.0, angular_velocity_max=180.0, start_size_min=2.0, start_size_max=8.0, end_size_min=0.0, end_size_max=3.0, start_color=(130, 120, 255, 220), end_color=(70, 0, 255, 0), emitter_lifetime=-1.0, shape=cls.SHAPE_CIRCLE )