Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game / Unity

Day 88 of 100 Days of VR: Endless Flyer – Adding Magnet Visual Effects with Particle Systems

0.00/5 (No votes)
6 Feb 2019CPOL5 min read 2K  
Adding Magnet Visual Effects with Particle Systems

Introduction

Welcome back to day 88! On the previous day, we created the music loop for our magnet power-up.

Today, we’re going to continue to work on creating visual special effects for our magnet power-up. More specifically, we’re going to use particle effects to create a magnet visual effect.

The visual effects will appear whenever we collect the power-up and disappear when our power-up expires.

Let’s get started!

Step 1: Adding Visual Effects

Now that we have sound, the next thing that I want to experiment with is adding some visual effects. We can do this by adding a particle effect.

This isn’t going to be anything great, but for an amateurish attempt, let’s see what we can try and do!

The first decision is that we are NOT going to try and create our own particle effect, we’re just going to use someone else’s.

Step 1.1: Getting a Particle Effect From the Unity Store

For a magnet effect, I was looking for some sort of electric static effect.

I looked around and found this awesome particle pack in the Unity Asset Store. It’s called: Unity Particle Pack 5.X.

Image 1

Download the assets and import it into Unity, everything will be in a folder called Almgp_particle_vol_1.

Step 1.2: Configuring the Particle Effect in Our Game

I explored around and the specific particle effect that we want is located at Almgp_particle_vol_1/prefabs/simple/lightning/lightning_b.

  1. Drag lightning_b and make it a child of Player > Plane Collider (there’s a reason for this that we’ll talk about later)
  2. Change the position to be (0, 2.5, 0)

Here’s a sneak preview of what it looks like if we play it on the Unity Editor.

Image 2

Next, we just want to change some of the numbers in the Particle System. Many of these are up to you, however, there’s one that’s important:

  • Simulation Space: Local – This is important. What this setting does is that it affects how the particle moves relative to a local position. If we keep it at World, the particle will stay where they are while we’re moving. If we set it to be Local but a child of Player, the particles created will also be moving at the same speed of the game object which looks weird. However, if we were to set it as a child of Player (something that’s position isn’t being transformed) like PlaneContainer, our particles position will stay relative to it, specifically, it won’t be affected by the movement of the plane and will always dance around in your plane.
  • Max Particle: 48 – This creates more particles in one instance, I wanted more particles.
  • Start Speed: 0.7 – 2 – This sets how fast the particles move over time. I decreased the end speed a bit.

Once again, the only important part is Simulation Space, the rest can be whatever you want it to be.

For more details about the different particle effect settings, check out the documentation on Unity.

Now that we have an idea of how we want to create our particle effect, it’s time to make a prefab of it and generate it whenever we collect a power-up!

  1. Rename lightning_b to be Magnet Particle
  2. Drag it to our Prefab folder to make a prefab of it

Step 1.3: Generating the Particle Effect When We Collect the Power-Up

Now that we have a particle effect, it’s time to create it whenever we collect the Magnet power-up. Let’s go back to our PlaneCollider script:

C#
using UnityEngine;

public class PlaneCollider : MonoBehaviour 
{
	public GameObject PlaneObject;
    public GameObject Explosion;
    public AudioClip MagnetSFX;
    public GameObject MagnetParticleEffect;

    private SoundManager _soundManager;
    private GameObject _currentParticleEffect;

    void Start()
    {
        _soundManager = GetComponent<SoundManager>();
    }

    void OnTriggerEnter(Collider other)
    {
		print(other + " name " + other.name);
		switch (other.tag) {
			case "Coin":
				CoinCollision(other);
				break;
            case "Magnet":
                MagnetCollision(other);
                break;
			default:
				CheckUnTaggedCollision(other);
				break;
		}
    }

    // Stops and gets rid of the current particle effect
    public void StopParticleEffect()
    {
        if (_currentParticleEffect != null)
        {
            ParticleSystem particleSystem = _currentParticleEffect.GetComponent<ParticleSystem>();
            particleSystem.Stop();
            Destroy(_currentParticleEffect);
            _currentParticleEffect = null;
        }
    }

	// Collides with the coin, we get the script that controls 
	// the logic for the coin and call collect so it will
	// know what to do after we collide into it.
	private void CoinCollision(Collider other) {
		Coin coin = other.GetComponent<Coin>();
		coin.Collect();
		GameManager.Instance.CollectCoin();
	}

    // Collides with the magnet, we add the power up to our list of power-ups
    // and let the power-up destroy itself from the game.
    private void MagnetCollision(Collider other)
    {
        Debug.Log("magnet collision hit");
        PlayerManager.Instance.AddPowerUp(PlayerManager.PowerUpType.Magnet);
        Magnet magnet = other.GetComponent<Magnet>();
        _soundManager.PlayBackgroundClip(MagnetSFX);
        StopParticleEffect();
        _currentParticleEffect = Instantiate(MagnetParticleEffect, PlaneObject.transform);
        ParticleSystem particleSystem = _currentParticleEffect.GetComponent<ParticleSystem>();
        particleSystem.Play();
        magnet.Collect();
    }

    // Check the collided object if it doesn't have a tag to see if it's
    // something we're also looking for.
    private void CheckUnTaggedCollision(Collider other) {
		if (other.name.Contains("Cube")) {
			EnemyCollision();
		}
	}

    /// Destroy the player and set the current state to the dead state.
    private void EnemyCollision()
    {
        Instantiate(Explosion, PlaneObject.transform.position, Quaternion.identity);
        Destroy(PlaneObject);
        PlayerManager.Instance.GameOver();
        GameUIManager.Instance.GameOver(gameObject);
        CameraManager.Instance.GameOver();
    }
}

Looking at the Fields

  • public GameObject MagnetParticleEffect – This holds our magnet particle generator prefab.
  • private GameObject _currentParticleEffect – This holds our current particle effect that we’re playing. In this game, we could potentially have multiple power-ups active, however, we only care about the most recent one.

Walking Through the Code

To add our particle effects, we need to instantiate it in our plane like we did when we first made it into a prefab.

  1. In MagnetCollision(), we create our particle effect. We stop our current particle effect with StopParticleEffect() if it exists and we’ll replace it with our newest particle effect. To do that, we first instantiate our MagnetParticleEffect and we place it at our PlayerObject (the PlaneContainer). Once we have our game object, we grab the ParticleSystem and then play the particle effect.
  2. We create a public method: StopParticleEffect(), this will either be called by PlayerManager or by the other power-up collision code to get rid of the existing particle effects.

Setting Up the Script

In the PlaneCollider script, drag our Magnet Particles prefab to the Magnet Particle Effect slot.

Step 1.4: Stopping the Particle Effect From the Playermanager Script

At this point, if we were to collide with a magnet power-up, we would have our particle and sound effect playing. After the power-up duration, the sound effect will go away, but not the particle effects. That logic is located at the PlayerManager script where we just added the audio code.

Here’s what it looks like:

C#
using UnityEngine;
using System.Collections.Generic;

public class PlayerManager : MonoBehaviour
{
    public static PlayerManager Instance;
    public enum PlayerState { Alive, Dead }
    public GameObject Player;
    public GameObject MagnetCollider;

    public enum PowerUpType { Magnet }
    private Dictionary<PowerUpType, PowerUp> powerUpDictionary;
    private float powerUpDuration = 30f;
    private List<PowerUpType> itemsToRemove;

    public PlayerState CurrentState
    {
        // I chose to be explicit, but we could also have done:
        // get; private set;
        // read more: https://stackoverflow.com/questions/3847832/understanding-private-setters
        get { return _currentState; }
        private set { _currentState = value; }
    }

    private PlayerState _currentState;

    void Start ()
	{
	    if (Instance != null)
	    {
	        // If Instance already exists, we should get rid of this game object
	        // and use the original game object that set Instance   
	        Destroy(gameObject);
	        return;
	    }

	    // If Instance doesn't exist, we initialize the Player Manager
	    Init();
	}

    void Update()
    {
        foreach (KeyValuePair<PowerUpType, PowerUp> entry in powerUpDictionary)
        {
            entry.Value.Duration -= Time.deltaTime;

            // We can't remove an item from a dictionary if we're iterating through it.
            // Instead, we have to keep track of it in a list and then remove the items from the
            // dictionary later.
            if (entry.Value.Duration <= 0)
            {
                itemsToRemove.Add(entry.Key);
            }
        }

        // Go through all of the power-ups that need to be removed and remove it from the 
        // dictionary.
        foreach (PowerUpType powerUpType in itemsToRemove)
        {
            switch (powerUpType)
            {
                case PowerUpType.Magnet:
                    Transform magnetCollider = Player.transform.Find("Magnet Collider(Clone)");
                    print(magnetCollider);
                    Destroy(magnetCollider.gameObject);
                    magnetCollider = null;
                    break;
            }
            powerUpDictionary.Remove(powerUpType);
        }

        // We've removed everything, let's clear our list.
        itemsToRemove.Clear();

        // If we no longer have any power-ups active, let's stop playing our background music
        if (powerUpDictionary.Count == 0)
        {
            Player.GetComponent<SoundManager>().StopBackgroundClip();
            Player.GetComponent<PlaneCollider>().StopParticleEffect();
        }
    }

    private void Init()
    {
        Instance = this;
        CurrentState = PlayerState.Alive;

        // Create an empty dictionary and list, otherwise they'll be null later when we
        // try to access them and crash.
        powerUpDictionary = new Dictionary<PowerUpType, PowerUp>();
        itemsToRemove = new List<PowerUpType>();
    }

    /// <summary>
    /// Sets the PlayerManager to be in the game over state.
    /// </summary>
    public void GameOver()
    {
        CurrentState = PlayerState.Dead;
    }

    public void AddPowerUp(PowerUpType powerUpType)
    {
        switch (powerUpType)
        {
            case PowerUpType.Magnet:
                // if we already have the MagnetCollider, don't add it again.
                if (powerUpDictionary.ContainsKey(powerUpType))
                {
                    break;
                }
                // We add the Magnet Collider to our player.
                Instantiate(MagnetCollider, Player.transform.position, 
                            Quaternion.identity, Player.transform);
                break;
        }
        // An interesting part of this is that if we get another power up, that if we
        // get a duplicate power up, we will replace it with the new one.
        powerUpDictionary[powerUpType] = new PowerUp(powerUpDuration);
    }

    /// <summary>
    /// See if the player currently has the power up that we pass in.
    /// </summary>
    public bool ContainsPowerUp(PowerUpType powerUpType)
    {
        return powerUpDictionary.ContainsKey(powerUpType);
    }
}

Walking Through the Code

This is a very simple one-line change.

  1. In Update(), we get the PlaneCollider script from our Player game object and then we tell it to stop playing the particle effect. Just like with the sound, this could be bad, because we’re calling it every frame, but we’re making sure that we’re only doing something if we have a non-null particle generator.

End of Day 88

And that’s it! By the end of today, we should now have a complete power-up that looks something like this.

Pretty neat! Now imagine if we had actual assets that weren’t randomly assembled together!

To recap, today, we:

  • grabbed a lightning particle effect from the Unity asset store.
  • edited our existing script so that we can show our lightning effect.

With this, we’re bringing an end to the magnet power-up series. Next up, we’re going to look at implementing another power-up. Stay tuned!

Day 87 | 100 Days of VR | Day 89

Home

The post Day 88 of 100 Days of VR: Endless Flyer – Adding Magnet Visual Effects with Particle Systems appeared first on Coding Chronicles.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)