Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add texture atlas sprites #22

Merged
merged 14 commits into from
Dec 31, 2022
5 changes: 3 additions & 2 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use bevy::{
};
use bevy_app::PluginGroup;
use bevy_asset::AssetServer;

use bevy_particle_systems::{
ColorOverTime, ColorPoint, Gradient, JitteredValue, ParticleBurst, ParticleSystem,
ParticleSystemBundle, ParticleSystemPlugin, Playing, SinWave, ValueOverTime,
ParticleSystemBundle, ParticleSystemPlugin, ParticleTexture, Playing, SinWave, ValueOverTime,
};

fn main() {
Expand Down Expand Up @@ -36,7 +37,7 @@ fn startup_system(mut commands: Commands, asset_server: Res<AssetServer>) {
.spawn(ParticleSystemBundle {
particle_system: ParticleSystem {
max_particles: 50_000,
default_sprite: asset_server.load("px.png"),
texture: ParticleTexture::Sprite(asset_server.load("px.png")),
spawn_rate_per_second: 1000.0.into(),
initial_speed: JitteredValue::jittered(3.0, -1.0..1.0),
acceleration: ValueOverTime::Sin(SinWave {
Expand Down
9 changes: 5 additions & 4 deletions examples/local_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ use bevy::{
};
use bevy_asset::AssetServer;
use bevy_math::Quat;
use bevy_time::Time;

use bevy_particle_systems::{
ColorOverTime, ColorPoint, Gradient, JitteredValue, ParticleSpace, ParticleSystem,
ParticleSystemBundle, ParticleSystemPlugin, Playing,
ParticleSystemBundle, ParticleSystemPlugin, ParticleTexture, Playing,
};
use bevy_time::Time;

#[derive(Debug, Component)]
pub struct Targets {
Expand All @@ -40,7 +41,7 @@ fn startup_system(mut commands: Commands, asset_server: Res<AssetServer>) {
max_particles: 500,
emitter_shape: std::f32::consts::PI * 0.25,
emitter_angle: 0.0,
default_sprite: asset_server.load("px.png"),
texture: ParticleTexture::Sprite(asset_server.load("px.png")),
spawn_rate_per_second: 35.0.into(),
initial_speed: JitteredValue::jittered(25.0, 0.0..5.0),
acceleration: 0.0.into(),
Expand All @@ -67,7 +68,7 @@ fn startup_system(mut commands: Commands, asset_server: Res<AssetServer>) {
max_particles: 500,
emitter_shape: std::f32::consts::PI * 0.25,
emitter_angle: std::f32::consts::PI,
default_sprite: asset_server.load("px.png"),
texture: ParticleTexture::Sprite(asset_server.load("px.png")),
spawn_rate_per_second: 35.0.into(),
initial_speed: JitteredValue::jittered(25.0, 0.0..5.0),
acceleration: 0.0.into(),
Expand Down
6 changes: 3 additions & 3 deletions examples/time_scaling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use bevy::{
use bevy_asset::AssetServer;
use bevy_particle_systems::{
ColorOverTime, ColorPoint, Gradient, JitteredValue, ParticleSpace, ParticleSystem,
ParticleSystemBundle, ParticleSystemPlugin, Playing,
ParticleSystemBundle, ParticleSystemPlugin, ParticleTexture, Playing,
};
use bevy_time::Time;
fn main() {
Expand All @@ -32,7 +32,7 @@ fn startup_system(mut commands: Commands, asset_server: Res<AssetServer>) {
max_particles: 500,
emitter_shape: std::f32::consts::PI * 0.25,
emitter_angle: 0.0,
default_sprite: asset_server.load("px.png"),
texture: ParticleTexture::Sprite(asset_server.load("px.png")),
spawn_rate_per_second: 35.0.into(),
initial_speed: JitteredValue::jittered(25.0, 0.0..5.0),
acceleration: 0.0.into(),
Expand All @@ -59,7 +59,7 @@ fn startup_system(mut commands: Commands, asset_server: Res<AssetServer>) {
max_particles: 500,
emitter_shape: std::f32::consts::PI * 0.25,
emitter_angle: std::f32::consts::PI,
default_sprite: asset_server.load("px.png"),
texture: ParticleTexture::Sprite(asset_server.load("px.png")),
spawn_rate_per_second: 35.0.into(),
initial_speed: JitteredValue::jittered(25.0, 0.0..5.0),
acceleration: 0.0.into(),
Expand Down
23 changes: 19 additions & 4 deletions src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use bevy_ecs::prelude::{Bundle, Component, Entity, ReflectComponent};
use bevy_math::Vec3;
use bevy_reflect::prelude::*;
use bevy_render::prelude::{Image, VisibilityBundle};
use bevy_sprite::TextureAtlas;
use bevy_transform::prelude::{GlobalTransform, Transform};

use crate::values::{ColorOverTime, JitteredValue, ValueOverTime};
use crate::values::{ColorOverTime, JitteredValue, RandomValue, ValueOverTime};

/// Defines a burst of a specified number of particles at the given time in a running particle system.
///
Expand Down Expand Up @@ -43,6 +44,20 @@ pub enum ParticleSpace {
World,
}

/// Defines what texture to use for a particle
#[derive(Debug, Clone, Reflect, FromReflect)]
pub enum ParticleTexture {
/// Indicates particles should use a given image texture
Sprite(Handle<Image>),
/// Indicates particles should use a given texture atlas
TextureAtlas {
/// The handle to the texture atlas
atlas: Handle<TextureAtlas>,
/// The index in the atlas can constant, or be chosen randomly
index: RandomValue<usize>,
},
}

/// Defines the parameters of how a system and its particles behave.
///
/// A [`ParticleSystem`] will emit particles until it reaches the ``system_duration_seconds`` or forever if ``looping`` is true, so long as the
Expand All @@ -57,8 +72,8 @@ pub struct ParticleSystem {
/// The maximum number of particles the system can have alive at any given time.
pub max_particles: usize,

/// The sprite used for each particle.
pub default_sprite: Handle<Image>,
/// The texture used for each particle.
pub texture: ParticleTexture,

/// The number of particles to spawn per second.
///
Expand Down Expand Up @@ -149,7 +164,7 @@ impl Default for ParticleSystem {
fn default() -> Self {
Self {
max_particles: 100,
default_sprite: Handle::default(),
texture: ParticleTexture::Sprite(Handle::default()),
spawn_rate_per_second: 5.0.into(),
spawn_radius: 0.0.into(),
emitter_shape: std::f32::consts::TAU,
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
//! .spawn_bundle(ParticleSystemBundle {
//! particle_system: ParticleSystem {
//! max_particles: 10_000,
//! default_sprite: asset_server.load("my_particle.png"),
//! texture: ParticleTexture::Sprite(asset_server.load("px.png")),
//! spawn_rate_per_second: 25.0.into(),
//! initial_speed: JitteredValue::jittered(3.0, -1.0..1.0),
//! lifetime: JitteredValue::jittered(8.0, -2.0..2.0),
Expand All @@ -65,7 +65,7 @@ pub mod values;
use bevy_app::prelude::{App, Plugin};
pub use components::*;
use systems::{
partcle_spawner, particle_cleanup, particle_color, particle_lifetime, particle_transform,
particle_cleanup, particle_color, particle_lifetime, particle_spawner, particle_transform,
};
pub use values::*;

Expand All @@ -91,7 +91,7 @@ pub struct ParticleSystemPlugin;

impl Plugin for ParticleSystemPlugin {
fn build(&self, app: &mut App) {
app.add_system(partcle_spawner)
app.add_system(particle_spawner)
.add_system(particle_lifetime)
.add_system(particle_color)
.add_system(particle_transform)
Expand Down
128 changes: 83 additions & 45 deletions src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bevy_ecs::prelude::{Commands, Entity, Query, Res, With};
use bevy_hierarchy::BuildChildren;
use bevy_math::Vec3;
use bevy_sprite::prelude::{Sprite, SpriteBundle};
use bevy_sprite::{SpriteSheetBundle, TextureAtlasSprite};
use bevy_time::Time;
use bevy_transform::prelude::{GlobalTransform, Transform};
use rand::prelude::*;
Expand All @@ -12,7 +13,7 @@ use crate::{
ParticleSystem, Playing, RunningState, Speed,
},
values::ColorOverTime,
DistanceTraveled,
DistanceTraveled, ParticleTexture,
};

#[allow(
Expand All @@ -22,7 +23,7 @@ use crate::{
clippy::type_complexity,
clippy::too_many_lines
)]
pub fn partcle_spawner(
pub fn particle_spawner(
mut particle_systems: Query<
(
Entity,
Expand Down Expand Up @@ -130,8 +131,57 @@ pub fn partcle_spawner(

match particle_system.space {
ParticleSpace::World => {
commands
.spawn(ParticleBundle {
let mut entity_commands = commands.spawn(ParticleBundle {
particle: Particle {
parent_system: entity,
max_lifetime: particle_system.lifetime.get_value(&mut rng),
max_distance: particle_system.max_distance,
use_scaled_time: particle_system.use_scaled_time,
color: particle_system.color.clone(),
scale: particle_system.scale.clone(),
acceleration: particle_system.acceleration.clone(),
despawn_with_parent: particle_system.despawn_particles_with_system,
},
speed: Speed(particle_system.initial_speed.get_value(&mut rng)),
direction: Direction::new(
direction,
particle_system.z_value_override.is_some(),
),
..ParticleBundle::default()
});

match &particle_system.texture {
ParticleTexture::Sprite(image_handle) => {
entity_commands.insert(SpriteBundle {
sprite: Sprite {
color: particle_system.color.at_lifetime_pct(0.0),
..Sprite::default()
},
transform: spawn_point,
texture: image_handle.clone(),
..SpriteBundle::default()
});
}
ParticleTexture::TextureAtlas {
atlas: atlas_handle,
index,
} => {
entity_commands.insert(SpriteSheetBundle {
sprite: TextureAtlasSprite {
color: particle_system.color.at_lifetime_pct(0.0),
index: index.get_value(&mut rng),
..TextureAtlasSprite::default()
},
transform: spawn_point,
texture_atlas: atlas_handle.clone(),
..SpriteSheetBundle::default()
});
}
}
}
ParticleSpace::Local => {
commands.entity(entity).with_children(|parent| {
let mut entity_commands = parent.spawn(ParticleBundle {
particle: Particle {
parent_system: entity,
max_lifetime: particle_system.lifetime.get_value(&mut rng),
Expand All @@ -148,48 +198,36 @@ pub fn partcle_spawner(
particle_system.z_value_override.is_some(),
),
..ParticleBundle::default()
})
.insert(SpriteBundle {
sprite: Sprite {
color: particle_system.color.at_lifetime_pct(0.0),
..Sprite::default()
},
transform: spawn_point,
texture: particle_system.default_sprite.clone(),
..SpriteBundle::default()
});
}
ParticleSpace::Local => {
commands.entity(entity).with_children(|parent| {
parent
.spawn(ParticleBundle {
particle: Particle {
parent_system: entity,
max_lifetime: particle_system.lifetime.get_value(&mut rng),
max_distance: particle_system.max_distance,
use_scaled_time: particle_system.use_scaled_time,
color: particle_system.color.clone(),
scale: particle_system.scale.clone(),
acceleration: particle_system.acceleration.clone(),
despawn_with_parent: particle_system
.despawn_particles_with_system,
},
speed: Speed(particle_system.initial_speed.get_value(&mut rng)),
direction: Direction::new(
direction,
particle_system.z_value_override.is_some(),
),
..ParticleBundle::default()
})
.insert(SpriteBundle {
sprite: Sprite {
color: particle_system.color.at_lifetime_pct(0.0),
..Sprite::default()
},
transform: spawn_point,
texture: particle_system.default_sprite.clone(),
..SpriteBundle::default()
});

match &particle_system.texture {
ParticleTexture::Sprite(image_handle) => {
entity_commands.insert(SpriteBundle {
sprite: Sprite {
color: particle_system.color.at_lifetime_pct(0.0),
..Sprite::default()
},
transform: spawn_point,
texture: image_handle.clone(),
..SpriteBundle::default()
});
}
ParticleTexture::TextureAtlas {
atlas: atlas_handle,
index,
} => {
entity_commands.insert(SpriteSheetBundle {
sprite: TextureAtlasSprite {
color: particle_system.color.at_lifetime_pct(0.0),
index: index.get_value(&mut rng),
..TextureAtlasSprite::default()
},
transform: spawn_point,
texture_atlas: atlas_handle.clone(),
..SpriteSheetBundle::default()
});
}
}
});
}
}
Expand Down
Loading