Skip to content

Conversation

@Soullnik
Copy link

Add start/stop lifecycle and age/lifetime support to SolidParticleSystem

Summary

This PR adds lifecycle management capabilities to SolidParticleSystem to align with the architecture of standard ParticleSystem, while maintaining backward compatibility with the existing SolidParticleSystem behavior-agnostic design.

Changes

New Features

1. Lifecycle Management (start() and stop() methods)

  • start(delay?: number): Starts the particle system and begins automatic updates

    • Calls buildMesh(), initParticles(), and setParticles() in sequence
    • Registers onBeforeRenderObservable for automatic frame-by-frame updates
    • Supports optional delay parameter
    • Prevents multiple starts if already started
  • stop(): Stops the particle system

    • Unregisters the update loop observer
    • Prevents age updates when stopped
    • Prevents multiple stops if already stopped
  • started and stopped getters: Read-only properties to check system state

2. Age and Lifetime Support

  • age: Current age of the particle (starts at 0, increments each frame)
  • lifeTime: Maximum lifetime of the particle (defaults to Infinity for infinite life)
    • Set to Infinity by default to maintain backward compatibility (particles never die by default)
    • Can be set to a finite value for particles with lifecycle management
    • Only checked if finite (isFinite(lifeTime))

3. Update Speed Management

  • updateSpeed: Public property (default: 0.01) matching BaseParticleSystem architecture
  • _scaledUpdateSpeed: Internal scaled value using getAnimationRatio() for FPS independence
  • Age updates are now frame-rate independent, matching standard particle system behavior

Implementation Details

Automatic Update Loop

When start() is called, the system automatically registers to scene.onBeforeRenderObservable:

this._scene.onBeforeRenderObservable.add(() => {
    if (this._started && !this._stopped) {
        this.setParticles();
    }
});

This ensures particles are updated every frame without manual setParticles() calls.

Age and Lifetime Logic

In setParticles(), the system now:

  1. Updates particle.age by _scaledUpdateSpeed each frame (only when started)
  2. Checks if age >= lifeTime only when lifeTime is finite
  3. Sets particle.alive = false when lifetime is reached (no automatic recycling, as SolidParticleSystem uses fixed arrays)

Backward Compatibility

  • Default lifeTime = Infinity: Particles never die by default, maintaining original behavior
  • No automatic particle recycling: Unlike standard particle systems, SolidParticleSystem doesn't recycle particles automatically (architecture uses fixed particle arrays)
  • Existing code continues to work: All existing SolidParticleSystem code remains functional

Usage Examples

Basic Usage (Infinite Lifetime - Default Behavior)

const sps = new SolidParticleSystem("sps", scene);
const box = MeshBuilder.CreateBox("box", { size: 1 }, scene);
sps.addShape(box, 100);
box.dispose();

// Initialize particles
sps.initParticles = function () {
    for (let i = 0; i < this.nbParticles; i++) {
        const p = this.particles[i];
        p.position.y = 0;
        // lifeTime = Infinity by default, particles live forever
    }
};

// Update particles each frame
sps.updateParticle = function (particle) {
    particle.position.y += 0.1;

    // Use age for animations even without lifetime
    const time = particle.age;
    particle.color.r = Math.sin(time) * 0.5 + 0.5;
};

sps.buildMesh();
sps.start(); // Automatically updates every frame

Usage with Lifetime Management

const sps = new SolidParticleSystem("sps", scene);
const box = MeshBuilder.CreateBox("box", { size: 1 }, scene);
sps.addShape(box, 100);
box.dispose();

// Initialize particles with finite lifetime
sps.initParticles = function () {
    for (let i = 0; i < this.nbParticles; i++) {
        const p = this.particles[i];
        p.lifeTime = Math.random() * 3 + 1; // 1-4 seconds
        p.position.y = 0;
        p.age = 0;
        p.alive = true;
    }
};

// Update particles
sps.updateParticle = function (particle) {
    if (!particle.alive) {
        return; // Skip dead particles
    }

    particle.position.y += 0.1;

    // Fade out based on age
    const ratio = particle.age / particle.lifeTime;
    if (particle.color) {
        particle.color.a = 1.0 - ratio;
    }

    // Manually recycle when dead (optional)
    if (particle.age >= particle.lifeTime) {
        particle.age = 0;
        particle.alive = true;
        particle.lifeTime = Math.random() * 3 + 1;
        particle.position.y = 0;
        if (particle.color) {
            particle.color.a = 1.0;
        }
    }
};

sps.buildMesh();
sps.start();

Breaking Changes

None - All changes are backward compatible:

Mikalai Lazitski added 3 commits November 17, 2025 10:26
…p methods in SolidParticleSystem

- Introduced `lifeTime` and `age` properties in `SolidParticle` to manage particle lifespan.
- Updated particle age during the rendering loop in `SolidParticleSystem` and checked against `lifeTime` for particle death.
- Added `start` and `stop` methods to control the particle system's emission and update loop, with an observer for rendering updates.
- Included `updateSpeed` property for customizable animation speed.
@Soullnik Soullnik changed the title Feat/solid particles start and lifetime Solid particles start and lifetime Nov 22, 2025
@bjsplat
Copy link
Collaborator

bjsplat commented Nov 22, 2025

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator

bjsplat commented Nov 22, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Nov 22, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Nov 22, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Nov 22, 2025

@bjsplat
Copy link
Collaborator

bjsplat commented Nov 22, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants