//! Example showing the effect of [`SimulationCondition`] to continue simulating //! or not when the entity is invisible. //! //! This example spawns two effects: //! - The top one is only simulated when visible //! ([`SimulationCondition::WhenVisible`]; default behavior). //! - The bottom one is always simulated, even when invisible //! ([`SimulationCondition::Always`]). //! //! A system updates the visibility of the effects, toggling it ON and OFF. We //! can observe that the top effect continue to be simulated while hidden. use std::time::Duration; use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*}; use bevy_hanabi::prelude::*; mod utils; use utils::*; fn main() -> Result<(), Box> { let app_exit = utils::make_test_app("visibility") .add_systems(Startup, setup) .add_systems(Update, update) .run(); app_exit.into_result() } fn setup( mut commands: Commands, mut effects: ResMut>, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(( Transform::from_translation(Vec3::Z * 100.), Camera3d::default(), Tonemapping::None, )); commands.spawn(DirectionalLight { color: Color::WHITE, // Crank the illuminance way (too) high to make the reference cube clearly visible illuminance: 100000., shadows_enabled: false, ..Default::default() }); let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); let mat = materials.add(utils::COLOR_PURPLE); let mut gradient = Gradient::new(); gradient.add_key(0.0, Vec4::new(1.0, 0.0, 0.0, 1.0)); gradient.add_key(0.25, Vec4::new(0.0, 1.0, 0.0, 1.0)); gradient.add_key(0.5, Vec4::new(0.0, 0.0, 1.0, 1.0)); gradient.add_key(0.75, Vec4::new(0.0, 1.0, 1.0, 1.0)); gradient.add_key(1.0, Vec4::new(1.0, 1.0, 1.0, 1.0)); let writer = ExprWriter::new(); // Set the same constant velocity to all particles so they stay grouped // together. This is not really representative of a real world visual effect, // but is useful for the purpose of this example. let velocity = writer.lit(Vec3::X * 3.).expr(); let init_velocity = SetAttributeModifier::new(Attribute::VELOCITY, velocity); let age = writer.lit(0.).expr(); let init_age = SetAttributeModifier::new(Attribute::AGE, age); let lifetime = writer.lit(15.).expr(); let init_lifetime = SetAttributeModifier::new(Attribute::LIFETIME, lifetime); let init_pos = SetPositionSphereModifier { center: writer.lit(Vec3::ZERO).expr(), radius: writer.lit(5.).expr(), dimension: ShapeDimension::Volume, }; let mut asset = EffectAsset::new( 4096, Spawner::burst(50.0.into(), 15.0.into()), writer.finish(), ) .with_simulation_condition(SimulationCondition::WhenVisible) .init(init_pos) .init(init_velocity) .init(init_age) .init(init_lifetime) //.update(AccelModifier::constant(Vec3::new(0., 2., 0.))) .render(ColorOverLifetimeModifier { gradient }); let effect1 = effects.add(asset.clone()); // Reference cube to visualize the emit origin commands .spawn(( Transform::from_translation(Vec3::new(-30., -20., 0.)), Mesh3d(cube.clone()), MeshMaterial3d(mat.clone()), )) .with_children(|p| { p.spawn(( Name::new("WhenVisible"), ParticleEffectBundle { effect: ParticleEffect::new(effect1), ..Default::default() }, )); }); asset.simulation_condition = SimulationCondition::Always; let effect2 = effects.add(asset); // Reference cube to visualize the emit origin commands .spawn(( Transform::from_translation(Vec3::new(-30., 20., 0.)), Mesh3d(cube.clone()), MeshMaterial3d(mat.clone()), )) .with_children(|p| { p.spawn(( Name::new("Always"), ParticleEffectBundle { effect: ParticleEffect::new(effect2), ..Default::default() }, )); }); } fn update( time: Res