Lightning Gun - Targeting

Currently we're building one of the games secondary weapons. The lightning gun, or Tesla cannon as its been nicknamed in the studio. Out of all the weapons this is probably been the trickiest to build.

In order to make the lightning gun vfx, you first need to access the particle system and manually assign a waypoint path for each particle to follow.

This system has been majorly simplified, to make it more personal for what you're looking to achieve.

There are 3 main steps to this process:
    1) Get the particles
    2) Manipulate the particles
    3) Set the particles

Lets setup the global variables that we will need for this to work.
These variables will be available to all parts of the script, this is useful for when we separate the code into specific functions, they will all need access to them.

// Global Variables 
ParticleSystem ps;
ParticleSystem.Particle[] particles;
public int maxParticles = 20; // public so we can change this from within Unity.

float speed = 5f;
Collider[] targets;
Transform enemy;


Get Particles
We first need to get the particles that have been emitted.
To do this we need a reference to the particle system, which in the case we will call ps. And an array of particles to hold them in.
We can do this in our start method for now, as this only gets run once, we will need to add further code to make this a real-time effect.

private void Start()
{
    //this function grabs all the particles and puts them into the particles array we created earlier.
    //thing to remember is this only runs once, so if you need to do this every frame, this will need to go into the Update function instead.
    //we have done this ourselves as well, but I figured this is a good starting step to learn how to get the particles.
    
    ps = GetComponent<ParticleSystem>(); // to grab a reference to the particle system
    particles = new ParticleSystem.Particle[maxParticles]; // to initialise the particles array to the size we define
    ps.GetParticles(particles); // puts the current emitted particles into the particles array.
}

Manipulate Particles
Now, there is no particle system function for manipulating the particles, like there is for getting or setting them; this is where your creativity comes in.

For us, this has a few steps, this is because not only do we want to interpolate (move in a straight line) each particles position to a target position, we also want to do it over an curve instead of a straight line. Although, we will add in the curve code in another post, just to keep this bitesize and easy to digest.

This is where we start doing things in the update method, what you have in start method can remain, as that only runs once.
Later on, like us, you will end up taking this code and seperating it into smaller functions, targeting for example can run every frame, but particle movement only needs to happen on user input.
For now, we will stick to everything in Update for simplicity, even though not that efficient.

private void Update()
{
    // Let's just do a quick and simple automated target selector. This will choose the closest object to the lightning gun for targetting.
    // we will need a couple more global variables, a float for targetRange and a Collider array to hold all targets in range.
    // it is fine for this float to be a local variable, that will get updated each frame.
    float dist = targetRange;
    // now we need to actually get the targets, we will use the physics system and out targetRange to do this.
    targets = Physics.OverlapSphere(transform.position, targetRange);
    if (targets.Length > 0) // test the length of the targets array, if empty, the following code will not run.
    {
        foreach (Collider c in targets)
        {
            // this code will run for every single target in the targets array.
            if (c && c.gameObject.CompareTag("Enemy") // here we are testing if c exists, and if it's tag is Enemy
            {
                float d = Vector3.Distance(transform.position, c.transform.position);
                if (d < dist) 
                {
                    // if the code gets this far, we have found a target that is closet to use than the previous.
                    // so we set dist to be the same a d, as that is the new distance we check all other targets against
                    // we also set enemy to this target 
                    dist = d;
                    enemy = c.transform;
                }
            }
        }
    }
    //now this will run every frame, so we can get updated particle positions each frame, to further manipuate them via linear interpolation.
    ps.GetParticles(particles);
    //Per frame particle manipulation
    //before we can assign a new position, we first need a direction to move them towards
    //and a speed to perform the movement in, which can be assigned in Global Variables or within Unity's Inspector.
    
    //you will need to define your enemy. we have a Transform called enemy, in another post we will make this a waypoints array and cycle through the waypoints that will make the curve
    Vector3 dir = enemy.position - transform.position;
    //We will need to use a For loop to iterate over the particles array and assign their new position for this frame
    // in this example we use the MoveTowards function of Vector3 instead of it's Lerp function, this is because MoveTowards actually provides more accurate results
    // than Lerp when the target is moving as well as the emitter.
    for (int i = 0; i < particles.Length; i++)
    {
        particles[i].position = Vector3.MoveTowards(particles[i].position, dir, Time.deltaTime * speed);
    }
    
    //Set particles
    //you will call this at the end of the manipulation code to apply the changes to the particle system.
    //You will also notice that when setting, we also need to assign how many particles there are.
    ps.SetParticles(particles, particles.Length;
}

At this stage, you will have a working particle system that emits the amount of particles we specified, and then moves them towards the provided target.

You will want to take this further and break it down to smaller functions, and operate them on user input basis, just so they're not running all the time, as the player will want to choose when to use this weapon.

In a future blog post we will add in the waypoints system, this is so we can specifiy a curved path and make the lightning look like it is arcing towards the target.