Cool Down and Targeting Arc

Hey all got a beam working but its got some problems I need to solve… so I added in a yield in the code for the beam to remain active longer on hit of space bar which works fine however if you don’t wait till its done and you spam the space bar it glitches out and plays the sound a ton of times. So how can I fix that? Maybe even add a cool down timer or something and also give it a targeting arc… I am a bit lost so please take a look and see whats wrong and how to improve the script.

[RequireComponent(typeof(LineRenderer))]
 
public class BeamArray : MonoBehaviour 
{
    float range = 100.0f;
    LineRenderer line;
    public Material lineMaterial;
    bool phaser = false;
    public AudioClip sound;
    bool playedsound = false;
 
    void Start()
    {
        line = GetComponent<LineRenderer>();
        line.SetVertexCount(2);
        line.renderer.material = lineMaterial;
        line.SetWidth(0.1f, 0.25f);
    }
 
    void Update()
    {
       if(Input.GetKeyDown("space") && phaser == false)
       {
         phaser = true;
       }
       StartCoroutine (pulsetime());
    }
       IEnumerator pulsetime()
        {
         var script = GameObject.Find("aaMain Camera").GetComponent<SelectTarget>();
            if(phaser == true)
            {
             line.enabled = true;
          if(phaser == true && playedsound == false)
          {
              AudioSource.PlayClipAtPoint(sound, Camera.main.transform.position);
              playedsound = true;
          }
             line.SetPosition(0, transform.position);
             line.SetPosition(1, script.selectedTarget.transform.position);
          yield return new WaitForSeconds(1.680f);
          line.enabled = false;
          playedsound = false;
          phaser = false;
            }
        }
}

There’s an error in Update: you must only start the coroutine when it’s not already running (when phaser == false). Move StartCoroutine inside the if:

...
void Update()
{
	// only start the coroutine if it's not already running
	if(Input.GetKeyDown("space") && phaser == false)
	{ 
		phaser = true;
		StartCoroutine (pulsetime());
	}
}
...

You can use Linecast (or Raycast) in order to check whether the beam is hitting something. Actually, you should do it in order do find the hit point, and set there the end point of the beam. You could also use SendMessage in order to call a function in the target script and apply damage to (or destroy) the target:

IEnumerator pulsetime()
{
	SelectTarget script = GameObject.Find("aaMain Camera").GetComponent<SelectTarget>();
	line.enabled = true;
	AudioSource.PlayClipAtPoint(sound, Camera.main.transform.position);
	RaycastHit hit;
	if (Physics.Linecast(transform.position, script.selectedTarget.transform.position, out hit)
	{
		// call ApplyDamage(10) in the hit object:
		hit.transform.SendMessage("ApplyDamage", 10, SendMessageOptions.DontRequireReceiver);
	}
	// wait the cooldown time in a loop
	float delay = 1.68f;
	while (delay > 0){ // while cooldown time running...
		// update beam endpoints
		line.SetPosition(0, transform.position);
		line.SetPosition(1, script.selectedTarget.transform.position);
		// decrement delay time:
		delay -= Time.deltaTime;
		// let Unity free until next frame:
		yield return null;
	}
	line.enabled = false;
	phaser = false;
}

You must add a function ApplyDamage(int damage) to the target script in order to apply the damage to it (objects that don’t have such function will not be affected). The code in the target script could be something like this:

using UnityEngine;
using System.Collections;

public class TargetHealth : MonoBehaviour {

    int health = 100;

    void ApplyDamage(int damage){
        health -= damage;
        if (health <= 0){
            // target is dead: destroy it
            Destroy(gameObject);
        }
    }
}