How do I toggle a particle system on and off using a script?

Hi everybody!

I have FlappyBird clone type of game that is based on this tutorial:

Since completing the tutorial I have been modifying different aspects of the game. It turned into a project where pipes are giant cigarettes that emit smoke, which in turn adds to the challenge.

I would like to implement a powerup that removes the smoke effect for a set time. I have been succesful in creating a script (EmissionToggler) that on Update checks wether a boolean (smokeEnabled) is true or false and either plays or stops the emitting. I am able to access this in the inspector and can confirm that it works.

The problem arises when I try to configure the powerup to change the state of smokeEnabled in the EmissionToggler script. Unity does not give me any error messages but for some reason, the state of the boolean just does not change. As a novice coder I have no idea how to solve this, so I appreciate any help I can get.

This is the code for the powerup (the script name is terrible, I know):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PowerUpDisableSmoke : MonoBehaviour
{

    private EmissionToggler emissionToggler;

    private void Start()
    {
        emissionToggler = FindObjectOfType<EmissionToggler>();
    }


    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            emissionToggler.smokeEnabled = false;

            Pickup ();
        }

        void Pickup ()
        {
           
            emissionToggler.smokeEnabled = true;
        }
    }

}

This is the code for the EmissionToggler script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EmissionToggler : MonoBehaviour
{

    private ParticleSystem smoker;
    //public GameObject smokeSource;
    public bool smokeEnabled = true;



    void Start()
    {
        smoker = GetComponent<ParticleSystem>();
    }

    private void Update()
    {
        if (smokeEnabled == !true)
        {
            smoker.Stop(true, ParticleSystemStopBehavior.StopEmitting);
        }

        else
        {
            smoker.Play();
        }

    }
}

In unity I basically i have a prefab called “Smokes” that has two particle systems that emit smoke (one for both the cigarettes coming from the ground and cigarettes that descend from the ceiling) as child objects. EmissionToggler is attached to both of these. The PowerUpDisableSmoke is attached to the powerup prefab.

Any help is appreciated. I am new at this but boy is it fun to create stuff with Unity!

This is easily the smartest and most effective way to advance your brain and level up your gamedev skills. Bravo!!!

This line will only find ONE EmissionToggler… I think you actually want ALL of them, right?

It might be better to make some kind of central GameManager that has the notion of “SmokeEnabled” and then your emission toggler would always refer to that boolean rather than keeping its own, sort of a central place.

Then when the powerup hits, you tell the gameManager, hey, smoke off for 10 seconds or whatever, and all the other emission togglers shut off their particles.

You can google up the idea of a GameManager, but it’s basically a shared object that hangs around for the duration of the game, keeping track of stuff like this. You can make them out of MonoBehavior, out of ScriptableObjects, or just bare singletons.

In other news, you can actually toggle particles a bunch of ways:

  • the way you are (stop / play)
  • enabling/disabling the entire particle system (the smoke will blink out and back in)
  • turning on and off the emissionModule .enabled (my preferred way, since it maps in my brain to a faucet)

https://docs.unity3d.com/ScriptReference/ParticleSystem.EmissionModule.html

Seriously. It is awesome. Best game engine ever. So fun.

1 Like

Take

void Pickup ()
        {
            emissionToggler.smokeEnabled = true;
        }

out of your OnTriggerEnter2D method. It needs to be at the same level as all the other methods.
Also I’m curious: why disable, then enable immediately? Seems like it wouldn’t do anything, but I may have missed something.

Oh good spot SeeJay, that isn’t gonna do anything useful as far as turning off the particles!

OP: Line 20 turns off smoke, then you call Pickup which turns on smoke, instantly, all in the same frame.

As Seej noted, you probably don’t want that.

Thank you for your advice!

Seejay correctly pointed out that the Pickup method was on wrong level. I actually feel kinda stupid for not noticing this. Seems like beer and debugging don’t really work together :smile: Also the Pickup method was a leftover from a previous attempt where I used a coroutine to make the effect last for a few seconds. As it did not work I decided to make in instant, just to make things simpler to test it out.

I did what Kurt suggested and created a PowerUpManager to execute powerup effects (I already have a Game Manager but as it is based on the tutorial (which was more like intermediate level) I am afraid to change too much in it in case I end up breaking the whole game. Now the emission stops as intended but it does not start playing again. Here’s what happens:

First the powerup detects collision and changes the boolean value in PowerUpManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PowerUpDisableSmoke : MonoBehaviour
{

   
    private PowerUpManager poweUpManager;

    private void Start()
    {
        poweUpManager = FindObjectOfType<PowerUpManager>();
    }


    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            poweUpManager.ToggleSmoke(); ;

            //Pickup (); use this to destroy the powerup after picking it up
        }

    }
}

Next the bool “smokeEnabled” in PowerUpManager changes to false.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PowerUpManager : MonoBehaviour
{

    public bool smokeEnabled = true;
    public float duration = 5f;


    // Start is called before the first frame update
    void Start()
    {
        smokeEnabled = true;
    }

    public void ToggleSmoke()
    {
        StartCoroutine(ToggleEffect());
    }

    IEnumerator ToggleEffect()
    {
        smokeEnabled = false;
        Debug.Log("Smoke is off");

        yield return new WaitForSeconds(duration);

        smokeEnabled = true;
        Debug.Log("Smoke is on");
    }
}

The debug messages show up in the console, so this should be working as intended. Oh, and the reason I access the ToggleEffect Coroutine using another method (ToggleSmoke) is because I couldn’t figure out how to access the coroutine directly from the EmissionToggler script. In that script the Update checks wether smokeEnabled in PowerUpManager is true and plays or stops the emission accordingly:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EmissionToggler : MonoBehaviour
{

    private ParticleSystem smoker;
    private PowerUpManager powerupManager;



    void Start()
    {
        smoker = GetComponent<ParticleSystem>();
        powerupManager = FindObjectOfType<PowerUpManager>();
    }

    private void Update()
    {
        if (powerupManager.smokeEnabled == false)
           
        {
            smoker.Stop(true, ParticleSystemStopBehavior.StopEmitting);
        }

        else
        {
            smoker.Play(true);
        }

    }
}

So, for some reason the smoker.Play -command does not start the emission again. I have tried to removing the (true) from the brackets as well as using this: smoker.Play(true, ParticleSystemStopBehavior.StarEmitting) but apparently StartEmitting is not a valid command.

This is very puzzling but I feel like I’m already a step closer. Additional advice is much appreciated. And thank you for helping me get this far!

I did some additional debugging by adding Debug.log lines to the EmissionToggler script:

private void Update()
    {
        if (powerupManager.smokeEnabled == false)
           
        {
            smoker.Stop(true, ParticleSystemStopBehavior.StopEmitting);
            Debug.Log("Cigarettes stopped Emitting");
        }

        else
        {
            smoker.Play(true);
            Debug.Log("Cigarettes are Emitting");
        }

    }
}

The console prints these as it is supposed to. For some reason the smoke effect does not start playing once it is stopped by the script.

You need to start using source control so you can rip-tear-shred fearlessly and instantly revert if need be. Seriously, it opens up a whole new avenue of exploration when you know your hard-fought work is completely safe and you can experiment fearlessly.

Personally I use git because it is free and there are tons of tutorials out there to help you set it up.

Here’s how I use git in one of my games, Jetpack Kurt:

https://discussions.unity.com/t/807568/3

Using fine-grained source control as you work to refine your engineering:

https://discussions.unity.com/t/826718/2

Share/Sharing source code between projects:

https://discussions.unity.com/t/719810/2

Like I said, I always just enable/disable the emission, rather than start/stopping the ParticleSystem itself. Seems to work fine for me.

When I do quantitative particles like a thruster, I don’t even disable it: I just change the emit rate over time from min to max, a nice tidy little scriptlet like this:

    private ParticleSystem.EmissionModule em;
    // to record whatever you set up in the editor, and use that as its reference amount
    private float BaseEmitOverTimeRate;

    void Start ()
    {
        ParticleSystem ps = GetComponent<ParticleSystem> ();
        em = ps.emission;
        BaseEmitOverTimeRate = em.rateOverTime.constant;

        SetEmission( 0.0f);
    }

    public void    SetEmission( float fraction)
    {
        em.rateOverTime = fraction * BaseEmitOverTimeRate;
    }

Quote of the year.

The PS controls were a little strange to me at first, but they do make sense. I think the confusing part (for me anyway) was saying Stop() only stops the generation of new particles…existing ones continue their lifespan. Which didn’t seem right at first, but is a necessary command. For clearing of all particles, use Clear().

All you should need for basic particle system control are:

Stop() – Stops new particle generation, doesn’t remove existing ones
Play() – Plays particle system from current time
Pause() – Pauses, basically a freeze, particle generation internal time stops
Clear() – Clears all existing particles

None of these require arguments, though as you saw you can add the Boolean to say whether the command should affect any child object particle systems, and you can add an argument for Stop() to have it clear the particles that are already there.

You might also need ps.time = 0, to reset the PS time to 0.

Attach this script to an empty GameObject, add a Particle System to it, uncheck Play on Awake, and enable Color over Lifetime with a gradient, so you can see the change over the default 5 second lifetime. This will help you track when setting time = 0.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class particleSystemTest : MonoBehaviour
{

    ParticleSystem myPS;

    void Start()
    {
        myPS = GetComponent<ParticleSystem>();
        //myPS.GetComponent<Renderer>().enabled = false; // how to access the Renderer PS module, if needed
        Invoke("invokeMethods", 2);
    }

    void invokeMethods()
    {
        print("Invoke Methods started");
        Invoke("startParticles", 2);
        Invoke("stopParticles", 4); // note that existing ones continue their lifespan
        Invoke("startParticles", 6);
        Invoke("pauseParticles", 8); // freeze time
        Invoke("startParticles", 10);
        Invoke("clearParticles", 12); // clear all particles. Play/stop/pause state unaffected.
        Invoke("resetParticleSystemTime", 14);
        Invoke("invokeMethods", 16); // do it forever!
    }

    void startParticles()
    {
        myPS.Play();
        print("Play");
    }
    void stopParticles()
    {
        myPS.Stop();
        print("Stop");
    }
    void pauseParticles()
    {
        myPS.Pause();
        print("Pause");
    }
    void clearParticles()
    {
        myPS.Clear();
        print("Clear");
    }
    void resetParticleSystemTime()
    {
        myPS.time = 0;
        print("Time reset");
    }

}

And it’s only March…:smile:

Thanks to you both for your help! Finally got it working. Here’s the code incase anyone is trying to find a solution to a similiar problem. The way the ParticleSystem works was very tricky to figure out and I don’t think I understand it even now. But it works, so that’s the main thing!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EmissionToggler : MonoBehaviour
{

    public ParticleSystem smoker;
  
    private PowerUpManager powerupManager;




    void Start()
    {
        smoker = GetComponent<ParticleSystem>();
      
        powerupManager = FindObjectOfType<PowerUpManager>();

        smoker.Play();
      
    }

    private void Update()
    {
        if (powerupManager.smokeEnabled == false)

        {

            var emission = smoker.emission;
            emission.enabled = false;

          
          
        }

        else if (powerupManager.smokeEnabled == true)
        {
            var emission = smoker.emission;
            emission.enabled = true;

          
        }

        else
        {
            return;
        }

    }
}[/code
1 Like