About 'Iterating With foreach'!

In this video (objectives)…

  1. Setup an input Button for "Fire"
  2. Expose an array of game objects to the Inspector.
  3. Automatically iterate over the array using foreach
  4. Give player ability to start and stop guns.

After watching (learning outcomes)…

Use a foreach loop to iterate over an entire collection automatically.

(Unique Video Reference: 33_AA_CU2)

We would love to know…

  • What you found good about this lecture?
  • What we could do better?

Remember that you can reply to this topic, or create a new topic. The easiest way to create a new topic is to follow the link in Resources. That way the topic will…

  • Be in the correct forum (for the course).
  • Be in the right sub-forum (for the section)
  • Have the correct lecture tag.

Enjoy your stay in our thriving community!

I noticed by enabling/disabling the particle system it also gets rid of any lasers bouncing around in the scene which is undesirable. It might be better to target the particle system’s emission rate and flick it between 0 and a member variable emissionRate (for example).

I noticed the same. I made a workaround but I’m not sure if it was the right way of doing things.

First, I created a second array. This one was a ParticleSystem array.

Then when I load in the GameObject array I set the ParticleSystem Array to be the same length.

I then set the gameobject per Array

My Start looks like this

//Set Array to same length
gunParticle = new ParticleSystem[gunObject.Length];
//Set each value to have the ParticleSystem
for (int i = 0; i < gunObject.Length; i++)
{
    gunParticle[i] = gunObject[i].GetComponent<ParticleSystem>();
}

(I could just get the component per GameObject easy enough, but I wanted to do it this way)

I then use ParticleSystem.Play and ParticleSystem.Stop instead of setting the GameObject to active or inactive. There’s two problems with this though.

  1. It didn’t seem to play unless I was checking to make sure it wasn’t already playing. No worries, a bool that is flagged when starting/stopping and checking before Activate was easy enough

  2. The Particles wouldn’t react to clicks right away. There was a delay. Go to Emmision on the Particle and add a Burst with 0 time. That fixes that.

I had already coded my firing system before this lecture came-up, and after much trouble-shooting, I wound-up using the .Emit(int) method directly on a ParticleSystem reference:

private void TryDischargeWeapon()
{
    if (!(CrossPlatformInputManager.GetButton("Fire1"))) return;

    if (Time.time > coolTime)
    {
        // maxEnergy = trainer mode: no weapon battery drain.
        if (!maxEnergy) weaponBattery -= basePlayerWeaponUseRate;
        else Debug.Log("M A X  E N E R G Y");
        if (weaponBattery < 0) return;

        coolTime = Time.time + (basePlayerWeaponCoolTime / 1000f);
        TryPewPew(); // audio control
        switch (lastWeaponFired)
        {
            case 0:
                dischargeLight_1.SetActive(true);
                weapon_1.Emit(basePlayerVolley); // "Activate" particle system
                lastWeaponFired = 1;
                break;

(The objects called dischargeLight_# all get turned-off before each frame-update, for simplicity. The objects called weapon_# are the particle systems, both are setup in the inspector as seen here:

[SerializeField] GameObject dischargeLight_1;   
[SerializeField] ParticleSystem weapon_1;

The benefit to using ParticleSystem.Emit(int) is that the particle system can remain active at all times, allowing particles to die naturally rather than prematurely.

I went super-basic and a bit brute force with my fix for the same “problem”.

  • Use play()/Stop() to fire the particles. The stop API has a version that allows existing particles to stay alive after the emitter is stopped.
  • A var on the script that owns firing state (so one state persists through the iterator). This doesn’t smell great and is due for refactoring.

private void ProcessFiring()
{
bool firePressed = CrossPlatformInputManager.GetButton(“Fire”);

    foreach(ParticleSystem spell in magic)
    {
        if (firePressed)
        {
            if(!isFiring)
            {
                print("spell.Play();");
                spell.Play(true);
            }
        }
        else
        {
            if (isFiring)
            {
                print("spell.Stop();");
                spell.Stop(true, ParticleSystemStopBehavior.StopEmitting);
            }
        }
    }
    isFiring = firePressed;
}

After some fiddling, I noticed another good option for controlling the particle system is to turn Looping on/off:

bool fireGuns = CrossPlatformInputManager.GetButton("Fire1");

foreach (ParticleSystem gun in guns)
{
    var main = gun.main;
    main.loop = fireGuns;

    if (fireGuns && !gun.isEmitting)
    {
        gun.Play();
    }
}

Heres my take on what you could do, effectively turn the particle emitter on/off.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;

public class PlayerController : MonoBehaviour
{
    [Header("General")]
    [Tooltip("In m")] [SerializeField] float _xRange = 6f;
    [Tooltip("In m")] [SerializeField] float _yRange = 5f;
    [Tooltip("In ms^-1")] [SerializeField] float _xControlSpeed = 20f;
    [Tooltip("In ms^-1")] [SerializeField] float _yControlSpeed = 20f;

    [Header("Screen-position parameters")]
    [SerializeField] float _positionPitchFactor = -3f;
    [SerializeField] float _positionYawFactor = 3f;
    [Header("Control-throw parameters")]
    [SerializeField] float _controlPitchFactor = -17.5f;
    [SerializeField] float _controlRollFactor = -20f;

    [Header("Weapons")]
    [SerializeField] ParticleSystem[] _lasers = null;
    // TODO - implement laser sound

    float xThrow, yThrow;
    bool _controlsEnabled = true;

    void Update()
    {
        if (_controlsEnabled)
        {
            ProcessTranslation();
            ProcessRotation();
            ProcessFiring();
        }
    }

    void OnPlayerDeath()
    {
        _controlsEnabled = false;
        DeactivateGuns();
    }

    void ProcessTranslation()
    {
        xThrow = CrossPlatformInputManager.GetAxis("Horizontal");
        yThrow = CrossPlatformInputManager.GetAxis("Vertical");

        float xOffset = xThrow * _xControlSpeed * Time.deltaTime;
        float rawXPos = transform.localPosition.x + xOffset;
        float clampedXPos = Mathf.Clamp(rawXPos, -_xRange, _xRange);

        float yOffset = yThrow * _yControlSpeed * Time.deltaTime;
        float rawYPos = transform.localPosition.y + yOffset;
        float clampedYPos = Mathf.Clamp(rawYPos, -_yRange, _yRange);

        transform.localPosition = new Vector3(clampedXPos, clampedYPos, transform.localPosition.z);
    }

    void ProcessRotation()
    {
        float pitchDueToPosition = transform.localPosition.y * _positionPitchFactor;
        float pitchDueToControlThrow = yThrow * _controlPitchFactor;
        float pitch = pitchDueToPosition + pitchDueToControlThrow;

        float yawDueToPosition = transform.localPosition.x * _positionYawFactor;
        float yaw = yawDueToPosition;

        float rollDueToControlThrow = xThrow * _controlRollFactor;
        float roll = rollDueToControlThrow;

        transform.localRotation = Quaternion.Euler(pitch, yaw, roll);
    }

    void ProcessFiring()
    {
        if (CrossPlatformInputManager.GetButton("Fire1"))
        {
            ActivateGuns();
        }
        else
        {
            DeactivateGuns();
        }
    }

    void ActivateGuns()
    {
        foreach (ParticleSystem laser in _lasers)
        {
            var emission = laser.emission;
            emission.enabled = true;
        }
    }

    void DeactivateGuns()
    {
        foreach (ParticleSystem laser in _lasers)
        {
            var emission = laser.emission;
            emission.enabled = false;
        }
    }

}

[UPDATE, just watched Lecture 112 which covers this :slight_smile: ]

The one that worked for me is to just turn Emission on or off.

[Will remove the duplication in my code, but this way it’s easy to see what’s happening]

> private ParticleSystem ps;

> private void ActivateGuns()
> 	{
> 		foreach (GameObject gun in guns) 
> 		{
> 			//gun.SetActive (true);
> 			ps = gun.GetComponent<ParticleSystem>();
> 			var theEmission = ps.emission;
> 			theEmission.enabled = true;
> 		}
> 	}
> 
> 	private void DeactivateGuns()
> 	{
> 		foreach (GameObject gun in guns) 
> 		{
> 			//gun.SetActive (false);
> 			ps = gun.GetComponent<ParticleSystem>();
> 			var theEmission = ps.emission;
> 			theEmission.enabled = false;
> 		}
> 	}
3 Likes

Big thanks to @ben to mention how to lock the inspector, it’s these little things that make dealing with Unity a lot easier (and of course way more fun!)

1 Like

Privacy & Terms