reckfellENGINE

Linked particle stages: sub-emitters

Emitter links, event modes, capacity and chain lifecycle.

On this page

A sub-emitter link sends particle births from one GPU system into another existing GPU system. It does not create a child Entity or a new particle-system Entity for each parent particle. The two emitter Entities can be siblings in the scene tree.

  1. Create both GpuParticles3D Entities with their own process material and draw mesh in the intended World3D.
  2. Assign the primary emitter's Sub Emitter path to the target emitter. Resolve the path from the primary, not from the scene root.
  3. On the primary ParticleProcessMaterial, choose a Sub Emitter Mode other than Disabled and configure the corresponding count/frequency.
  4. Leave the target's autonomous Emitting disabled; its incoming births are driven by the primary. Do not repeatedly restart the target as if it were an ordinary looping emitter.
  5. Trigger the primary and inspect both systems' bounds/materials/capacity. A valid path alone does not enable a disabled material mode.

Create/attach both Entities before assigning a runtime path. If you reparent or replace the target, resolve the link again; a cached EntityPath is not an automatically repaired reference to every future hierarchy.

This workflow requires the supported GPU rendering path. The current material implementation warns that non-disabled sub-emitter modes are unsupported in Compatibility. The executed example uses Windows Forward+/Vulkan; it is not proof of every target device.

Run an end-triggered pair in C#

C#
using System;
using Reckfell;

public static class ParticleChain
{
    private static GpuParticles3D Emitter(string name, Color color)
    {
        var surface = new StandardMaterial3D
        {
            ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded,
            AlbedoColor = color,
        };
        return new GpuParticles3D
        {
            Name = name, Emitting = false, Amount = 32, Lifetime = 1.2,
            LocalCoords = false, UseFixedSeed = true, Seed = 42,
            DrawPass1 = new QuadMesh { Size = new Vector2(0.12f, 0.12f), Material = surface },
            VisibilityAabb = new Aabb(new Vector3(-3, -3, -3), new Vector3(6, 6, 6)),
        };
    }

    public static (GpuParticles3D primary, GpuParticles3D secondary) Create(Entity root)
    {
        if (!root.IsInsideTree()) throw new ArgumentException("Add the root to the tree first.");
        var primary = Emitter("Primary", Colors.Red);
        primary.Amount = 4; primary.OneShot = true; primary.Lifetime = 0.3;
        primary.Explosiveness = 1;
        primary.ProcessMaterial = new ParticleProcessMaterial
        {
            Direction = Vector3.Right, Spread = 0, Gravity = Vector3.Zero,
            InitialVelocityMin = 0.6f, InitialVelocityMax = 0.6f,
            SubEmitterMode = ParticleProcessMaterial.SubEmitterModeEnum.AtEnd,
            SubEmitterAmountAtEnd = 2,
        };
        var secondary = Emitter("Secondary", Colors.Blue);
        secondary.ProcessMaterial = new ParticleProcessMaterial
        {
            Direction = Vector3.Up, Spread = 0, Gravity = Vector3.Zero,
            InitialVelocityMin = 0.4f, InitialVelocityMax = 0.4f,
        };
        root.AddChild(primary);
        root.AddChild(secondary);
        // Both Entities exist before resolving the relative target path.
        primary.SubEmitter = primary.GetPathTo(secondary);
        return (primary, secondary);
    }
}

From an Entity already in the tree, call var (primary, secondary) = ParticleChain.Create(this); and primary.Restart();. Keep an active camera covering the effect. Red primary particles trigger births into the blue target at the end of their life. The helper is functional test geometry; replace its draw assets/materials for a finished effect.

The native test confirms that the target does not appear immediately in AtEnd, then renders after the primary stage. AtStart renders it near the initial stage instead. The Entity count remains two. Disabling the material mode or clearing the link prevents subsequent target births.

Choose a mode deliberately

ModeTrigger / setting
DisabledNo generated sub-emission from this material.
AtStartBirth of a primary particle; SubEmitterAmountAtStart.
AtEndEnd of a primary particle's life; SubEmitterAmountAtEnd.
AtCollisionA qualifying particle collision; SubEmitterAmountAtCollision. Requires working GPU collision setup.
ConstantRepeated emission while the primary particle is active; SubEmitterFrequency in Hz.

Counts are per triggering primary particle, not a single global burst count. Constant frequency must be finite and positive; the setter converts Hz to an interval internally. Simulation-step limits mean it is not a promise to produce an exact arbitrary high-frequency stream regardless of step size.

SubEmitterKeepVelocity controls forwarding the primary velocity. In the generated path, a forwarded velocity can replace the target's initial-velocity initialization; do not assume it simply adds to every target velocity/force setting. This comparison is source-reviewed, not part of the current pixel tests.

Budget capacity and lifecycle

The target's Amount is still its capacity. More requested births do not guarantee all of them fit or become separately visible. Estimate primary count × events × requested children and account for target lifetime, overlap and any additional stages. Chaining is not free or safely unbounded.

Avoid direct self-links and indirect cycles. These are not supported recipes for recursive effects. Use an explicit finite stage design instead of relying on recursive births to terminate themselves.

A primary Finished signal does not prove the target has expired. The target has its own particles and lifetime, and a target driven by sub-emission does not provide one complete-chain event automatically. Do not immediately recycle/free the whole pair from the primary signal alone. Also account for the documented SpeedScale/Finished limitation.

If you work with instantiated prefabs, editor Editable Children affects what can be edited in that instance. It is not a general runtime permission required by path resolution. Inspect the actual runtime hierarchy and link after the relevant Entities exist.

Diagnose missing secondary particles

SymptomCheck
Primary visible, target absentRelative path, target existence, primary material mode and target draw resources.
Only AtCollision failsValidate collision mode, GPU colliders and masks before debugging sub-emission.
Too few target particlesTarget Amount, lifetime, event counts and overlapping geometry.
Target disappears when primary finishesLifetime/pooling code may free the pair too soon.
Retargeting a prefab failsResolve from the primary after runtime instantiation/reparenting.
A material change seems delayedAllow its update/shader preparation and restart a controlled comparison; do not judge from one partially updated frame.

Evidence covers relative linking, AtEnd/AtStart/Disabled, removal of a link and the unchanged Entity hierarchy. Constant, AtCollision, velocity inheritance comparisons, cycles, complex pooling, other renderers/exports and performance are not claimed tested by this example.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.