Crate bevy_hanabi

source ·
Expand description

🎆 Hanabi – a GPU particle system plugin for the Bevy game engine.

The 🎆 Hanabi particle system is a GPU-based particle system integrated with the built-in Bevy renderer. It allows creating complex visual effects with millions of particles simulated in real time, by leveraging the power of compute shaders and offloading most of the work of particle simulating to the GPU.

Note: This library makes heavy use of compute shaders to offload work to the GPU in a performant way. Support for compute shaders on the wasm target (WebAssembly) via WebGPU is only available in Bevy in general since the Bevy v0.11, and is not yet available in this library. See #41 for details on progress.

§2D vs. 3D

🎆 Hanabi integrates both with the 2D and the 3D core pipelines of Bevy. The 2D pipeline integration is controlled by the 2d cargo feature, while the 3D pipeline integration is controlled by the 3d cargo feature. Both features are enabled by default for convenience. As an optimization, users can disable default features and re-enable only one of the two modes.

# Example: enable only 3D integration
bevy_hanabi = { version = "0.12", default-features = false, features = ["3d"] }

§Example

Add the 🎆 Hanabi plugin to your app:

use bevy_hanabi::prelude::*;

App::default()
    .add_plugins(DefaultPlugins)
    .add_plugins(HanabiPlugin)
    .run();

Create an EffectAsset describing a visual effect:

fn setup(mut effects: ResMut<Assets<EffectAsset>>) {
    // Define a color gradient from red to transparent black
    let mut gradient = Gradient::new();
    gradient.add_key(0.0, Vec4::new(1., 0., 0., 1.));
    gradient.add_key(1.0, Vec4::splat(0.));

    // Create a new expression module
    let mut module = Module::default();

    // On spawn, randomly initialize the position of the particle
    // to be over the surface of a sphere of radius 2 units.
    let init_pos = SetPositionSphereModifier {
        center: module.lit(Vec3::ZERO),
        radius: module.lit(0.05),
        dimension: ShapeDimension::Surface,
    };

    // Also initialize a radial initial velocity to 6 units/sec
    // away from the (same) sphere center.
    let init_vel = SetVelocitySphereModifier {
        center: module.lit(Vec3::ZERO),
        speed: module.lit(6.),
    };

    // Initialize the total lifetime of the particle, that is
    // the time for which it's simulated and rendered. This modifier
    // is almost always required, otherwise the particles won't show.
    let lifetime = module.lit(10.); // literal value "10.0"
    let init_lifetime = SetAttributeModifier::new(Attribute::LIFETIME, lifetime);

    // Every frame, add a gravity-like acceleration downward
    let accel = module.lit(Vec3::new(0., -3., 0.));
    let update_accel = AccelModifier::new(accel);

    // Create the effect asset
    let effect = EffectAsset::new(
        // Maximum number of particles alive at a time
        vec![32768],
        // Spawn at a rate of 5 particles per second
        Spawner::rate(5.0.into()),
        // Move the expression module into the asset
        module,
    )
    .with_name("MyEffect")
    .init(init_pos)
    .init(init_vel)
    .init(init_lifetime)
    .update(update_accel)
    // Render the particles with a color gradient over their
    // lifetime. This maps the gradient key 0 to the particle spawn
    // time, and the gradient key 1 to the particle death (10s).
    .render(ColorOverLifetimeModifier { gradient });

    // Insert into the asset system
    let effect_asset = effects.add(effect);
}

Then add an instance of that effect to an entity by spawning a ParticleEffect component referencing the asset. The simplest way is to use the ParticleEffectBundle to ensure all required components are spawned together.

commands.spawn(ParticleEffectBundle {
    effect: ParticleEffect::new(effect_asset),
    transform: Transform::from_translation(Vec3::Y),
    ..Default::default()
});

§Workflow

Authoring and using a particle effect follows this workflow:

  1. Create an EffectAsset representing the definition of the particle effect. This asset is a proper Bevy Asset, expected to be authored in advance, serialized, and shipped with your application. Creating an EffectAsset at runtime while the application is running is also supported, though. In any case however, the asset doesn’t do anything by itself.
  2. At runtime, when the application is running, create an actual particle effect instance by spawning a ParticleEffect component (or use the ParticleEffectBundle for simplicity). The component references the EffectAsset via its handle field. Multiple instances can reference the same asset at the same time, and some changes to the asset are reflected to its instances, although not all changes are supported. In general, avoid changing an EffectAsset while it’s in use by one or more ParticleEffect.
  3. Also spawn an EffectProperties component on the same entity. This is required even if the effect doesn’t use properties. See properties for more details.
  4. If actually using properties, update them through the EffectProperties at any time while the effect is active. This allows some moderate CPU-side control over the simulation and rendering of the effect, without having to destroy the effect and re-create a new one.

Re-exports§

Modules§

  • Effect attributes, like the position or velocity of a particle.
  • Effect graph and language definition.
  • Building blocks to create a visual effect.
  • Effect properties.

Structs§

Enums§

Traits§

Functions§