The best way to create a lightning effect

I need to create a lightning effect from the top of a tower to a target on the ground I already have a targeting system in place, but im not sure how to best create the effect.
Should I create an animated bold of lightning in maya?
Should I just make the lightning come from the sky?
Im not sure really were to begin implementing this on script
Any help would be appreciated!

1 Like

LineRenderers work great for lightning. Here is a script for a lightning gun I made a while back. This was from when I was first starting Unity, so I can’t say that it is necessarily the best put together. The code that actually draws the lightning is in the function “RedrawLine”. Particularly the While loop at the bottom of that function.

var jumpRadius = 5;
var arcLength = 1.0;
var arcVariation = 0.75;
var inaccuracy = 10.0;
var damage = 50;
var redrawRate = .1;
var selectionRate = .5;
var scifi0408 : AudioClip;
var maxLength = 50.0;
private var targets = Array();
var LR : LineRenderer;
var color1 : Color;
var color2 : Color;
var energyMax : int = 10;
var energyLeft : float;
var activeWeapon : boolean = false;
var regenRate = 1.0;
var GUIstyle : GUIStyle;
private var nextFireTime = 0.0;
private var nextRedrawTime = 0.0;


function Start() {
	energyLeft = energyMax;
}

function Update() {
	if (Input.GetButton ("Fire1")  (energyLeft > 0)  activeWeapon)
			Fire();
	else
	LR.enabled = false;
	if (energyLeft < energyMax  (!Input.GetButton ("Fire1") || !activeWeapon))
	energyLeft += regenRate * Time.deltaTime;
}

function Fire () {
	if (!audio.isPlaying)
		audio.Play();
	
	
	// Keep firing until we used up the fire time
	if ( nextFireTime < Time.time)
	{
		NewTargets();
		nextFireTime = selectionRate + Time.time;
	}
	
	if ( nextRedrawTime < Time.time)
	{	
	RedrawLine();
	LR.enabled = true;
	nextRedrawTime = redrawRate + Time.time;
	}
	energyLeft -= 1 * Time.deltaTime;
	
	var existsList = Array();
	for (var i = 0; i<targets.length; i++) {
		if (targets[i])
		existsList.Add(targets[i]);
		}
	targets = existsList;
	
	for (targs in targets) {
		if (targs.gameObject.layer != 10)
	targs.SendMessageUpwards("ApplyDamage", damage * Time.deltaTime, SendMessageOptions.DontRequireReceiver);
	}
}
// + Vector3(Random.value, Random.value, Random.value) * inaccuracy

function NewTargets () {
	var lengthLeft = maxLength;
	var lastPoint = transform.position + transform.TransformDirection(Randomize(Vector3.forward, inaccuracy)) * Random.Range(jumpRadius * arcVariation, jumpRadius) * 1.5;
	targets.Clear();
	while (lengthLeft > 0) {
		var colliders : Collider[] = Physics.OverlapSphere (lastPoint, jumpRadius);
		if (colliders.length == 0)
			return;
		var possibleTargs = Array();
		for (var hit : Collider in colliders) {
		if ((hit.gameObject.tag == "Enemy")  (NotInList(hit.transform, targets)  (hit.gameObject.layer != 10)))
			possibleTargs.Add(hit.transform);
		}
		
		if (possibleTargs.length > 0) {
		var i = Random.Range(0, possibleTargs.length - 1);
		targets.Add(possibleTargs[i].transform);
		lengthLeft -= Vector3.Distance(possibleTargs[i].transform.position, lastPoint);
		lastPoint = possibleTargs[i].transform.position;
		}
		else
			return;
	}
}

function RedrawLine () {
	var colorStart = color1;
	var colorEnd = color1;
	
	if (Random.value > .5)
	colorStart = color2;
	if (Random.value > .5)
	colorEnd = color2;
	LR.SetColors(colorStart, colorEnd);
	var existsList = Array();
	for (var i = 0; i<targets.length; i++) {
		//var exists = targets[i];
			//if (exists)
			if (targets[i])
			existsList.Add(targets[i]);
		}
		targets = existsList;
		LR.SetPosition(0, transform.position);
		var lastPoint = transform.position;
		var j = 1;
		var fwd : Vector3;
	if (targets.length == 0) {
		LR.SetVertexCount(2);
		var target = transform.position + transform.TransformDirection(Randomize(Vector3.forward, inaccuracy / 4)) * Random.Range(jumpRadius * arcVariation, jumpRadius) * 1.5;
		while (Vector3.Distance(target, lastPoint) >.5) {
				LR.SetVertexCount((j + 1));
				fwd = target - lastPoint;
				fwd.Normalize();
				fwd = Randomize(fwd, inaccuracy);
				fwd *= Random.Range(arcLength * arcVariation, arcLength);
				fwd += lastPoint;
				LR.SetPosition(j, fwd);
				j++;
				lastPoint = fwd;
			}
		return;
	}
	else {
		for (i = 0; i<targets.length; i++) {
			while (Vector3.Distance(targets[i].position, lastPoint) >.5) {
				LR.SetVertexCount((j + 1));
				fwd = targets[i].position - lastPoint;
				fwd.Normalize();
				fwd = Randomize(fwd, inaccuracy);
				fwd *= Random.Range(arcLength * arcVariation, arcLength);
				fwd += lastPoint;
				LR.SetPosition(j, fwd);
				j++;
				lastPoint = fwd;
			}
		}
	}
		
}

function Randomize (v3 : Vector3, inaccuracy2 : float) {
	v3 += Vector3(Random.Range(-1.0, 1.0), Random.Range(-1.0, 1.0), Random.Range(-1.0, 1.0)) * inaccuracy2;
	v3.Normalize();
	return v3;
}

function NotInList (item, list : Array) : boolean {
	for (var i = 0; i<list.length; i++) {
		if (item == list[i])
		return false;
	}
	return true;
}

function OnGUI () {
	
	GUI.Box (Rect (10, Screen.height - 60, Mathf.Clamp01(energyLeft / energyMax) * (Screen.width - 20), 20), "Lightning Energy", GUIstyle);
	
}

The basic idea is to draw a line in the rough direction of your target until you hit it. Each individual segment is off a bit, but overall, the bolt heads to the target. Redraw the line a couple times per second for a pretty convincing lightning effect.

Whoa, this will take me awhile to soak in exactly how this works, but it looks very promising! Ive never worked with a line renderer before, what do I assign to the LR variable?
I havent gotten it to work with what i am trying to do yet, but i attribute that to my lack of understanding. What was this used for? A gun in a fps? A sentry gun type thing?
Thanks so much sharing this im sure its exactly what i need.

Here is the same script trimmed down to just the graphics for the lightning. To test it out, make a scene with two objects that are separated by a long bit. Put this on one and assign the target variable to the other. Add a line renderer to the object with the script. make sure Use World Space is turned ON for the line renderer. Assign the LR variable to the line renderer. You might have to fiddle with the other variables to get the right look.

var target : gameObject;
var LR : LineRenderer;
var arcLength = 2.0;
var arcVariation = 2.0;
var inaccuracy = 1.0;

function Update() {
	var lastPoint = transform.position;
	var i = 1;
	LR.SetPosition(0, transform.position);//make the origin of the LR the same as the transform
	while (Vector3.Distance(target.transform.position, lastPoint) >.5) {//was the last arc not touching the target? 
            LR.SetVertexCount(i + 1);//then we need a new vertex in our line renderer
            var fwd = target.transform.position - lastPoint;//gives the direction to our target from the end of the last arc
            fwd.Normalize();//makes the direction to scale
            fwd = Randomize(fwd, inaccuracy);//we don't want a straight line to the target though
            fwd *= Random.Range(arcLength * arcVariation, arcLength);//nature is never too uniform
            fwd += lastPoint;//point + distance * direction = new point. this is where our new arc ends
            LR.SetPosition(i, fwd);//this tells the line renderer where to draw to
            i++;
            lastPoint = fwd;//so we know where we are starting from for the next arc
         }
}

function Randomize (v3 : Vector3, inaccuracy2 : float) { 
   v3 += Vector3(Random.Range(-1.0, 1.0), Random.Range(-1.0, 1.0), Random.Range(-1.0, 1.0)) * inaccuracy2; 
   v3.Normalize(); 
   return v3; 
}

I don’t know if this will compile as is, I am just typing this up while I drink my coffee before work. Also, it will redraw every frame, so you might need to pause it and then go step by step to really get a good look at it in action.

It was used originally for a third-person game, but I think it would work just fine in a FPS, maybe with the variables tweaked a bit.

1 Like

You Rule! this works great and is simple.

+1 8)
THX

JP

Thanks for the script, here’s a quick c# port for those who prefer it.

using UnityEngine;
using System.Collections;

public class lightning_start : MonoBehaviour {
    private GameObject target;
    private LineRenderer lineRend;
    private float arcLength = 1.0f;
    private float arcVariation = 1.0f;
    private float inaccuracy = 0.5f;
    private float timeOfZap = 0.25f;
    private float zapTimer;
    private LightningTrace lightTrace;

    void Start () {
        lineRend = gameObject.GetComponent<LineRenderer> ();
        zapTimer = 0;
        lineRend.SetVertexCount (1);
        lightTrace = gameObject.GetComponent <LightningTrace> ();
    }
  
    void Update() {
        if (zapTimer > 0) {
                        Vector3 lastPoint = transform.position;
                        int i = 1;
                        lineRend.SetPosition (0, transform.position);//make the origin of the LR the same as the transform
                        while (Vector3.Distance(target.transform.position, lastPoint) > 3.0f) {//was the last arc not touching the target?
                                lineRend.SetVertexCount (i + 1);//then we need a new vertex in our line renderer
                                Vector3 fwd = target.transform.position - lastPoint;//gives the direction to our target from the end of the last arc
                                fwd.Normalize ();//makes the direction to scale
                                fwd = Randomize (fwd, inaccuracy);//we don't want a straight line to the target though
                                fwd *= Random.Range (arcLength * arcVariation, arcLength);//nature is never too uniform
                                fwd += lastPoint;//point + distance * direction = new point. this is where our new arc ends
                                lineRend.SetPosition (i, fwd);//this tells the line renderer where to draw to
                                i++;
                                lastPoint = fwd;//so we know where we are starting from for the next arc
                        }
                        lineRend.SetVertexCount (i + 1);
                        lineRend.SetPosition (i, target.transform.position);
                        lightTrace.TraceLight (gameObject.transform.position, target.transform.position);
                        zapTimer = zapTimer - Time.deltaTime;
                } else
                        lineRend.SetVertexCount (1);

    }
  
    private Vector3 Randomize (Vector3 newVector, float devation) {
        newVector += new Vector3(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f)) * devation;
        newVector.Normalize();
        return newVector;
    }

    public void ZapTarget( GameObject newTarget){
        //print ("zap called");
        target = newTarget;
        zapTimer = timeOfZap;


    }
}

*Edit: the LightningTrace component in the code is just a ray tracer in a different scripts, sorry for the confusion.

2 Likes

Great Lightning effect and I am using it!

I would heavily recommend people use something like

lRend.SetWidth(.15f, .15f);

to make the lightning effect smaller (or bigger)