Examples
Examples

ParticleFX emitter properties


Setup

Since Defold 1.12.2 you can use go.get() and go.set() on individual ParticleFX emitters by passing keys = { "..." }.

This example focuses on this feature. It toggles a ParticleFX between two setups and shows the properties of the active emitters in the labels.

Setup

  1. Example consists of one game object in the collection having:

    • script particlefx_set_get.script
    • ParticleFX component named #particles
    • 2 label components: #label_core and #label_spark Outline
  2. The ParticleFX:

    • has two emitters: emitter_top and emitter_bottom ParticleFX
  3. The script has the resources exposed as properties:

    • particles_atlas
    • sprites_atlas
    • default_material
    • glow_material

    The current script switches the emitter image directly from code and keeps default_material active for the shown setup.

  4. The example uses 2 atlases with given animations:

    • the particles.atlas with coin and smoke animations
    • the sprites.atlas with ship_red and ship_dark animations

The atlases are set up to contain the animation ids used by the script, so the example can switch between the two setups without extra transition logic. It would also be possible to read the animations from resource.get_atlas(), but this example keeps a small hardcoded ANIMATIONS table to stay as simple as possible.

How it works

The script keeps two hardcoded setups and toggles between them whenever you click or tap:

  1. particles.atlas + default particle material emitter_top uses coin emitter_bottom uses smoke
  2. sprites.atlas + default particle material emitter_top uses ship_red emitter_bottom uses ship_dark

On startup the script stores the current atlas name, reads back the authored emitter properties, writes them into the labels, and starts the ParticleFX. When the setup changes after that, the script:

  1. stops the ParticleFX with { clear = true }
  2. flips self.atlas_name between sprites_atlas and particles_atlas
  3. looks up the atlas resource and the correct animation pair from the ANIMATIONS table
  4. calls set_emitter_properties() for each emitter to set image, animation, and material
  5. calls get_and_print_emitter_properties() to read the current values back with go.get()
  6. writes them into the two labels
  7. plays the ParticleFX again

The helper function set_emitter_properties() applies properties per emitter by passing the emitter id in keys:

go.set("#particles", "image", image, { keys = { "emitter_top" } })
go.set("#particles", "animation", animation, { keys = { "emitter_top" } })
go.set("#particles", "material", material, { keys = { "emitter_top" } })

The helper function get_and_print_emitter_properties() uses the same keys pattern with go.get() and writes the result into the labels, so the example shows which values are currently active for each emitter.

One important limitation: emitter property changes only affect the next play. The script therefore stops the ParticleFX, clears any already spawned particles, applies the new emitter overrides, and then plays it again.

Scripts

particlefx_set_get.script

-- Properties - 2 atlases:
go.property("particles_atlas", resource.atlas("/assets/particles.atlas"))
go.property("sprites_atlas", resource.atlas("/assets/sprites.atlas"))

-- Properties - 2 materials:
go.property("default_material", resource.material("/builtins/materials/particlefx.material"))
go.property("glow_material", resource.material("/example/particlefx_glow.material"))

-- Predefined table with animations for given atlases:
-- (One can get the animations from resource.get_atlas() too)
local ANIMATIONS = {
    ["particles_atlas"] = { hash("coin"), hash("smoke") },
    ["sprites_atlas"] = { hash("ship_red"), hash("ship_dark") }
}

-- Relative address of the particlefx component:
local PARTICLEFX = "#particles"

-- Read the current emitter properties back from the particlefx component
-- and show them in the on-screen labels.
local function get_and_print_emitter_properties(emitter, url)
    -- Get the properties of the emitter:
    local image = go.get(PARTICLEFX, "image", { keys = { emitter } })
    local animation = go.get(PARTICLEFX, "animation", { keys = { emitter } })
    local material = go.get(PARTICLEFX, "material", { keys = { emitter } })

    -- Show the properties in the label:
    label.set_text(url, emitter .. ":\nimage: " .. image
        .. "\nanimation: " .. animation .. "\nmaterial: " .. material )
end

-- Apply one full property set to a single emitter.
local function set_emitter_properties(emitter, image, animation, material)
    go.set(PARTICLEFX, "image", image, { keys = { emitter } })
    go.set(PARTICLEFX, "animation", animation, { keys = { emitter } })
    go.set(PARTICLEFX, "material", material, { keys = { emitter } })
end

-- Toggle between the two hardcoded setups:
-- particles atlas + glow material, or sprites atlas + default material.
local function change_particlefx_properties(self)
    -- Stop the particlefx before swapping atlas/material overrides.
    particlefx.stop(PARTICLEFX, { clear = true })

    -- Swap the atlas name and material:
    if self.atlas_name == "sprites_atlas" then
        self.atlas_name = "particles_atlas"
        self.material = self.glow_material
    else
        self.atlas_name = "sprites_atlas"
        self.material = self.default_material
    end

    -- Get current atlas resource and animations set for it:
    local atlas = self[self.atlas_name]
    local animations = ANIMATIONS[self.atlas_name]

    -- Set the atlas, animation and material for each emitter:
    set_emitter_properties("emitter_top", atlas, animations[1], self.material)
    set_emitter_properties("emitter_bottom", atlas, animations[2], self.material)

    -- Get the current properties and print them in the label for each emitter:
    get_and_print_emitter_properties("emitter_top", "#label_core")
    get_and_print_emitter_properties("emitter_bottom", "#label_spark")

    -- Play the particlefx again:
    particlefx.play(PARTICLEFX)
end

function init(self)
    -- Acquire input focus to react to the mouse click/touch:
    msg.post(".", "acquire_input_focus")

    -- Initialize the atlas name:
    self.atlas_name = "sprites_atlas"
    self.material = self.default_material

    -- Get the current properties and print them in the label for each emitter:
    get_and_print_emitter_properties("emitter_top", "#label_core")
    get_and_print_emitter_properties("emitter_bottom", "#label_spark")

    -- Start the ParticleFX:
    particlefx.play(PARTICLEFX)
end

function on_input(self, action_id, action)
    -- Toggle the setup on touch release:
    if action_id == hash("touch") and action.released then
        change_particlefx_properties(self)
    end
end

particlefx_glow.fp

#version 140

in mediump vec2 var_texcoord0;
in mediump vec4 var_color;

out vec4 out_fragColor;

uniform mediump sampler2D texture_sampler;

uniform fs_uniforms
{
    mediump vec4 tint;
};

void main()
{
    mediump vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
    mediump vec4 base = texture(texture_sampler, var_texcoord0.xy) * var_color * tint_pm;
    mediump float energy = max(max(base.r, base.g), base.b);
    mediump vec3 glow = vec3(0.9, 1.4, 1.8) * energy;
    out_fragColor = vec4(base.rgb * 0.2 + glow, base.a);
}

This example was created by Defold Foundation.

DOWNLOAD

 

Do you want to see more examples? Why not write a few yourself and submit a pull request? We love contributions.

GITHUB